Skip to content

year format heuristic#2395

Open
Fil wants to merge 7 commits intomainfrom
fil/year-format
Open

year format heuristic#2395
Fil wants to merge 7 commits intomainfrom
fil/year-format

Conversation

@Fil
Copy link
Copy Markdown
Contributor

@Fil Fil commented Mar 24, 2026

Format year-like numbers (integers in [1000, 3000]) without thousands separators on axes and tips.

Closes #768

This introduces two new helpers:

  • isYearDomain(domain) — returns true if all finite numeric values are integers in [1000, 3000]; false for non-number types, empty domains; ignores NaN and Infinity
  • formatYear(value) — formats as plain integer YYYY if the value is a number in [0, 9999] (or possibly yyyy.xyz if applied to a non-integer), returns "NaN" for NaN, else falls back to formatNumber(value) (once we decide to format without a comma, we do it over [0…9999] so that if 7000 unexpectedly appears, it is formatted consistently just like 2000).

When deriving an ordinal or linear scale, the data values are checked with isYearDomain, and a hint is added to the scale; in axes, the tickFormat then defaults to formatYear. For tips, when the channel is associated to the scale, we also check the hint; if there is no scale, we call isYearDomain too.

I chose to test [1000, 3000] for the heuristic (instead of, say, [0, 9999]), because the latter was generating many changes in snapshots. (The point is to have a heuristic that catches the most common cases while still being easy to remember; in that regard [1000, 3000] is slightly more complicated than[0, 9999], but it seems more useful that way — or maybe it is that I'm too accustomed to it?)

I wonder if we should:

  • accept tickFormat: "year" as an option (DONE)
  • export formatYear?

@Fil Fil requested review from allisonhorst and mbostock March 24, 2026 13:03
@Fil Fil requested a review from csdiehl16 March 24, 2026 16:06
Copy link
Copy Markdown

@csdiehl16 csdiehl16 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me! I run into this all the time so this is a very useful feature. I think we just want to make sure it's clear to users how to override the default if needed.

Whatever LLM is writing all our code in the year 3001 will need to remember to update it!

@allisonhorst
Copy link
Copy Markdown

Looks good to me, too!

@allisonhorst
Copy link
Copy Markdown

I think this would be nice:

accept format: "year" as an option

@Fil
Copy link
Copy Markdown
Contributor Author

Fil commented Mar 27, 2026

I've implemented the (opt-in) "year" format. I don't think we need another opt-out than tickFormat: "," or some such.

Copy link
Copy Markdown
Member

@mbostock mbostock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t see the need for tickFormat: "year"; wouldn’t it be more appropriate to use the existing tickFormat: "%Y" (for temporal data) or tickFormat: "%d" (for numeric data), rather than introducing another way? If you add tickFormat: "year" you might want other periods (day, month) and you might also want tickFormat: "integer" or some such. There’s extra plumbing here for scale.year and handling temporal data, when the real motivation for this change is numeric data.

I think we should reframe this not as really about years but about four digit (or fewer) integers. If you know that the domain is exclusively four-digit integers, it’s reasonable to say you don’t need a grouping symbol (,) because the number of digits is small and doesn’t vary across values. All we need to do is change the inferTickFormat case to check for four-digit integers and use %d as the default tick format.

Let me take a crack at it…

export * from "./yearly-requests-dot.js";
export * from "./yearly-requests-line.js";
export * from "./yearly-requests.js";
export * from "./year-format.js";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort alphabetically! 😅

@mbostock mbostock mentioned this pull request Apr 1, 2026

If you prefer conventional notation, you can specify the **tickFormat** option to change the behavior of the axis. The **tickFormat** option can either be a [d3.format](https://d3js.org/d3-format) string or a function that takes a tick value and returns the corresponding string. Note, however, that this may result in overlapping text.

For linear and ordinal scales, if the data values are all integers between 1,000 and 3,000, Plot assumes they represent years and defaults to a **tickFormat** without thousands separators — for example, `2026` instead of `2,026`. You can opt into this behavior explicitly with `tickFormat: "year"`. <VersionBadge pr="2395" />
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This documentation is separating the text describing the example from the example… I’ll find a better place for this in #2403.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Smarter formatting for year number channels?

4 participants