Please put in exactly the code snippets that yield the two example images, and elaborate on what it is that looks wrong. I can't tell from what you have here so far.
The issue is that the xticks and xlims aren't mapping to the datavalues, which are between 1990 and 2020. It should look something like this (which I got by manually setting the labels):
I don't see anything wrong, except in your manually-adjusted example. The Formatter is using an offset of 2000, so the tick labels are from 2000 - 10 to 2000 + 20. In many cases like this one doesn't actually want an offset. It can be turned off with rcParams['axes.formatter.useoffset'] = False, or ax.ticklabel_format(useOffset) = False.
As efiring stated, this is relatively standard behavior, albeit rather counterintuitive. I don't know the source code well enough without looking; could there be room for improvement in the default behavior of deciding when to turn on offsetting?
I fully fully support changing the default 'cause that offset seems so incredibly arbitrary and like you said, way counterintuitive. I think it looks at base 10 for defaults somehow, since something like ax.plot(range(200,400)) yields ticks between 200 and 400.
Also, @efiring how come calling ax.set_xlim((1990,2020)) doesn't overwrite the offset?
@efiring I just realized that half my issue is that I didn't read the notation correctly. Why is the default to pitch it to scientific notation for 4 numbers? (What is the cut off?)
Why is the default to pitch it to scientific notation for 4 numbers?
(What is the cut off?)
A non-zero offset is used if it saves at least 2 significant digits in
the tick labels, according to the comment in
ScalarFormatter._compute_offset(). There is no API for changing that
threshold when offsets are used.
It looks like the offset is always being forced into scientific
notation. This does seem like a misfeature. Maybe the offset should be
formatted using the same powerlimits criterion as is used for the tick
labels.
@efiring I'm for whatever solution is easy and more user friendly. I support @tacaswell's suggestion of making a hard minimum of 4 digits since years are very common use case, so much so that it's the usecase that triggered my opening this issue.
Activity
efiring commented on Sep 13, 2016
Please put in exactly the code snippets that yield the two example images, and elaborate on what it is that looks wrong. I can't tell from what you have here so far.
story645 commented on Sep 13, 2016
Sorry about that:
and it's not a range bug, 'cause the following yields the same figure:
This is version 2.0.0b3.post2245+g4f559a9
The issue is that the xticks and xlims aren't mapping to the datavalues, which are between 1990 and 2020. It should look something like this (which I got by manually setting the labels):

efiring commented on Sep 13, 2016
I don't see anything wrong, except in your manually-adjusted example. The Formatter is using an offset of 2000, so the tick labels are from 2000 - 10 to 2000 + 20. In many cases like this one doesn't actually want an offset. It can be turned off with rcParams['axes.formatter.useoffset'] = False, or ax.ticklabel_format(useOffset) = False.
LindyBalboa commented on Sep 13, 2016
As efiring stated, this is relatively standard behavior, albeit rather counterintuitive. I don't know the source code well enough without looking; could there be room for improvement in the default behavior of deciding when to turn on offsetting?
story645 commented on Sep 13, 2016
I fully fully support changing the default 'cause that offset seems so incredibly arbitrary and like you said, way counterintuitive. I think it looks at base 10 for defaults somehow, since something like ax.plot(range(200,400)) yields ticks between 200 and 400.
Also, @efiring how come calling ax.set_xlim((1990,2020)) doesn't overwrite the offset?
efiring commented on Sep 13, 2016
On 2016/09/13 9:15 AM, hannah wrote:
Setting the view limits is independent of the Formatter; the offset
comes from the Formatter.
story645 commented on Sep 13, 2016
@efiring I just realized that half my issue is that I didn't read the notation correctly. Why is the default to pitch it to scientific notation for 4 numbers? (What is the cut off?)
efiring commented on Sep 13, 2016
On 2016/09/13 9:27 AM, hannah wrote:
A non-zero offset is used if it saves at least 2 significant digits in
the tick labels, according to the comment in
ScalarFormatter._compute_offset(). There is no API for changing that
threshold when offsets are used.
It looks like the offset is always being forced into scientific
notation. This does seem like a misfeature. Maybe the offset should be
formatted using the same powerlimits criterion as is used for the tick
labels.
story645 commented on Sep 13, 2016
I'm 👍 on that.
4 remaining items
efiring commented on Oct 10, 2016
@story645 do you see a way out of the impasse here? If not, I think this needs to be booted down the road to v2.1.
anntzer commented on Oct 10, 2016
There's a relevant discussion of the formatting of the offset value in the first three messages of #5804, FWIW.
tacaswell commented on Oct 10, 2016
Lets bump the threshold up to 3 or 4 digits to make years behave better.
There are enough questions on SO specifically related to this issue with years (and I have seen it go wrong for my co workers) is worth fixing.
anntzer commented on Oct 10, 2016
Ah, I didn't think about that use case. Seems reasonable to me.
story645 commented on Oct 13, 2016
@efiring I'm for whatever solution is easy and more user friendly. I support @tacaswell's suggestion of making a hard minimum of 4 digits since years are very common use case, so much so that it's the usecase that triggered my opening this issue.
tacaswell commented on Oct 19, 2016
https://github.com/matplotlib/matplotlib/blob/v2.x/lib/matplotlib/ticker.py#L693 is the expression that needs to change, someone just needs to understand it enough to make the threshold 4 instead of 2....
efiring commented on Oct 23, 2016
In the line below that, I would try changing to this:
STY: in ScalarFormatter, use offset to save at least 4 digits, not 2
dopplershift commented on Nov 8, 2016
Fixed by #7365.