As explained in my original StackOverflow question, I've found some unexpected behavior when plotting single points in matplotlib.pyplot with Datetime objects. Plotting multiple points (>1) gives the range of dates with some buffer on either side. Plotting a single point causes the range to span multiple years (2014 - 2018 using Feb 03, 2016).
When using the AutoDateLocator(), the (assumed) issue isn't as noticeable since the generated ticks are fairly conservative, but using a WeekdayLocator() instead results in a dense mess of ticks. The application I'm using this for deals with time spans of at most a few months, so suddenly getting a range of multiple years is quite jarring.
Below is some code that generates 6 matplotlib.pyplot plot images to demonstrate a few example cases and the output from running the script with the --verbose-debug flag
import matplotlib.pyplot as plt
import matplotlib.dates as md
from datetime import datetime
# Create figures
fig0 = plt.figure(0)
fig1 = plt.figure(1)
fig2 = plt.figure(2)
fig3 = plt.figure(3)
fig4 = plt.figure(4)
fig5 = plt.figure(5)
fig6 = plt.figure(6)
# Add some subplots
sub3 = fig3.add_subplot(111)
sub4 = fig4.add_subplot(111)
sub5 = fig5.add_subplot(111)
sub6 = fig6.add_subplot(111)
# Creating a regular plot directly with a single point
plt.figure(0)
plt.plot((2,), (3,), marker='o')
plt.title('Regular plot, single point')
# Doing the same, but with a subplot
sub3.plot((2,), (3,), marker='o')
sub3.set_title('Subplot, single point')
# Creating a regular plot using a Datetime object with multiple points
# Using the AutoDateLocator()
test_date_one = datetime.strptime('2016-02-03', '%Y-%m-%d')
test_date_two = datetime.strptime('2016-03-03', '%Y-%m-%d')
plt.figure(1)
plt.plot((test_date_one, test_date_two), (2, 3), marker='o')
fig1.autofmt_xdate()
plt.title('Regular plot, multiple Datetime points')
# Now with a single point
plt.figure(2)
plt.plot(test_date_one, 2, marker='o')
fig2.autofmt_xdate()
plt.title('Regular plot, single Datetime point')
# Doing the same, but with a subplot
# Multiple points
sub4.plot((test_date_one, test_date_two), (2, 3), marker='o')
fig4.autofmt_xdate()
sub4.set_title('Subplot, multiple Datetime points')
# Single point
sub5.plot(test_date_one, 2, marker='o')
fig5.autofmt_xdate()
sub5.set_title('Subplot, single Datetime point')
# Finally, single Datetime point with WeekdayLocator()
sub6.plot(test_date_two, 3, marker='o')
sub6.xaxis.set_major_locator(md.WeekdayLocator())
fig6.autofmt_xdate()
sub6.set_title('Subplot, single Datetime point, WeekdayLocator()')
plt.show()
Debug Output
$HOME=C:\Users******
matplotlib data path C:\Python27\lib\site-packages\matplotlib\mpl-data
You have the following UNSUPPORTED LaTeX preamble customizations:
Please do not ask for support with these customizations active.
loaded rc file C:\Python27\lib\site-packages\matplotlib\mpl-data\matplotlibrc
matplotlib version 1.5.1
verbose.level debug
interactive is False
platform is win32
loaded modules: <dictionary-keyiterator object at 0x04E08810>
CACHEDIR=C:\Users*.matplotlib
Using fontManager instance from C:\Users*.matplotlib\fontList.cache
backend TkAgg version 8.5
Could not load matplotlib icon: can't use "pyimage10" as iconphoto: not a photo image
Could not load matplotlib icon: can't use "pyimage19" as iconphoto: not a photo image
Could not load matplotlib icon: can't use "pyimage28" as iconphoto: not a photo image
Could not load matplotlib icon: can't use "pyimage37" as iconphoto: not a photo image
Could not load matplotlib icon: can't use "pyimage46" as iconphoto: not a photo image
Could not load matplotlib icon: can't use "pyimage55" as iconphoto: not a photo image
findfont: Matching :family=sans-serif:style=normal:variant=normal:weight=400:stretch=normal:size=medium to Bitstream Vera Sans (u'c:\python27\lib\site-packages\matplotlib\mpl-data\fonts\ttf\Vera.ttf') with score of 0.000000
findfont: Matching :family=sans-serif:style=normal:variant=normal:weight=400:stretch=normal:size=large to Bitstream Vera Sans (u'c:\python27\lib\site-packages\matplotlib\mpl-data\fonts\ttf\Vera.ttf') with score of 0.000000
I've never submitted a bug report for something like this before, so I wanted to err on the side of too much info vs. too little. My apologies for the lengthy example though.
No worries! Thank you for the report. In the future please err on the side of brief but clear, the easier you make it for the reader the understand your problem the more likely it is they will look into fixing it. For example, we do not need to see the cases where it works (we assume the library works 😉 ).
Do you have any suggestions on how, from a single time stamp, to pick a good scale? I think what is happening here is that for non-date data we go a few percent on either side. The way we plot dates is that internally they are converted to float seconds from year 0, so a date around now gives a value
In [164]: ax.convert_xunits(test_date_two)
Out[164]: 736026.0
which gives a range of (690000.0, 780000.0) from the auto-expanding, which isn't so bad if you were plotting against scalars, but gives you this rather silly result if you put ticks ever 8.6k or so.
The best solution here might be for you to write a helper function, something like
defplot_my_date_data(ax, x, y, **kwargs):
ret=ax.plot(x, y, **kwargs)
iflen(x) ==1:
ax.set_xlim(x-week, x+week)
ax.xaxis.set_major_locator(md.WeekdayLocator())
returnret
@tacaswell: As far as I understand it, the change in defaults is to make the display range = range of the data +/- 4%. For a single point, this gives a zero-width display range of [data value, data value] :-).
Obviously this is not ideal, you need to special-case the singular case somehow and invent some sort of non-zero width. But I think the way you do that is orthogonal to the actual change in axis limit setting rules? Even with the old axis limit setting, if all you have is a single value 123.4, then the axis limits could be anyhing -- [100, 200], [120, 125], [123.0, 124.0], ...
What if we went with an order of magnitude rule? So, if seconds are
non-zero, then 4% around seconds. If minutes are non-zero, 4% around the
minute. If hour is non-zero, then 4% around that (and so on and so forth)?
I think it might be more natural for each specific date locator type to expand the singular range to a few of its units. Perhaps the AutoDateLocator could use something like your order of magnitude rule.
I think it might be more natural for each specific date locator type to
expand the singular range to a few of its units. Perhaps the
AutoDateLocator could use something like your order of magnitude rule.
—
Reply to this email directly or view it on GitHub #5963 (comment)
.
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!
Activity
tacaswell commented on Feb 4, 2016
Minimal example:
Thank you for the extensive report, but there is something to be send for brevity.
bcoov commented on Feb 4, 2016
I've never submitted a bug report for something like this before, so I wanted to err on the side of too much info vs. too little. My apologies for the lengthy example though.
tacaswell commented on Feb 4, 2016
No worries! Thank you for the report. In the future please err on the side of brief but clear, the easier you make it for the reader the understand your problem the more likely it is they will look into fixing it. For example, we do not need to see the cases where it works (we assume the library works 😉 ).
Do you have any suggestions on how, from a single time stamp, to pick a good scale? I think what is happening here is that for non-date data we go a few percent on either side. The way we plot dates is that internally they are converted to float seconds from year 0, so a date around now gives a value
which gives a range of (690000.0, 780000.0) from the auto-expanding, which isn't so bad if you were plotting against scalars, but gives you this rather silly result if you put ticks ever 8.6k or so.
The best solution here might be for you to write a helper function, something like
@njsmith Any thoughts on this sort of thing?
tacaswell commented on Feb 4, 2016
labeled this as 2.0 as it relates to changing the behavior of the auto-limits.
tacaswell commented on Feb 4, 2016
Actually, digging into this a bit more, the nonsingular should be handled by the locator which is special cased for dates already....
njsmith commented on Feb 4, 2016
@tacaswell: As far as I understand it, the change in defaults is to make the display range = range of the data +/- 4%. For a single point, this gives a zero-width display range of [data value, data value] :-).
Obviously this is not ideal, you need to special-case the singular case somehow and invent some sort of non-zero width. But I think the way you do that is orthogonal to the actual change in axis limit setting rules? Even with the old axis limit setting, if all you have is a single value 123.4, then the axis limits could be anyhing -- [100, 200], [120, 125], [123.0, 124.0], ...
bcoov commented on Feb 4, 2016
I was going to suggest basing the range off of the Locator being used, but considering:
I assume this is already implemented, or at least intended to be?
WeatherGod commented on Feb 4, 2016
What if we went with an order of magnitude rule? So, if seconds are
non-zero, then 4% around seconds. If minutes are non-zero, 4% around the
minute. If hour is non-zero, then 4% around that (and so on and so forth)?
On Thu, Feb 4, 2016 at 12:45 PM, bcoov notifications@github.com wrote:
efiring commented on Feb 5, 2016
I think it might be more natural for each specific date locator type to expand the singular range to a few of its units. Perhaps the AutoDateLocator could use something like your order of magnitude rule.
WeatherGod commented on Feb 5, 2016
Yeah, I think that makes sense.
On Thu, Feb 4, 2016 at 10:21 PM, Eric Firing notifications@github.com
wrote:
8 remaining items
github-actions commented on Mar 21, 2023
This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!
jklymak commented on Mar 21, 2023
I'm pretty sure the default date span is one day now. I'll close but feel free to reopen if I'm mistaken
rcomer commented on Mar 21, 2023
The code from #5963 (comment) now produces