Skip to content

plot_date() after axhline() doesn't rescale axes #7742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dstansby opened this issue Jan 4, 2017 · 12 comments · Fixed by #20168
Closed

plot_date() after axhline() doesn't rescale axes #7742

dstansby opened this issue Jan 4, 2017 · 12 comments · Fixed by #20168

Comments

@dstansby
Copy link
Member

dstansby commented Jan 4, 2017

If I plot an axhline first, and then two points with plot_date, the x-axis is not rescaled to the two new data points:

import matplotlib.pyplot as plt
from datetime import datetime

fig, axs = plt.subplots(2, 1)

axs[0].axhline(1.5)
axs[0].plot([datetime(2016, 1, 1, 0, 0, 0), datetime(2016, 1, 2, 0, 0, 0)], [1, 2])

axs[1].plot([datetime(2016, 1, 1, 0, 0, 0), datetime(2016, 1, 2, 0, 0, 0)], [1, 2])
axs[1].axhline(1.5)

plt.show()

figure_1-1

I would expect the two plots to be the same, and the plotting order not to matter.

Present on current master (2.0.0rc2.post2912+g015d8d5) and on 1.5

@Yasaman-Mah
Copy link

I think I have a fix for this, after making the fix, the script above results in:
screenshot from 2017-02-28 17 12 32
But I'm not sure how to write tests for it. Are they needed?

@phobson
Copy link
Member

phobson commented Feb 28, 2017

@Yasaman-Mah
In this is case, I would write a test confirming that both axes have the expected limits:

import matplotlib.pyplot as plt
from datetime import datetime

dates = [datetime(2016, 1, 1, 0, 0, 0), datetime(2016, 1, 2, 0, 0, 0)]
values = [1, 2]
expected_limits = (xmin, xmax) # fill this in

fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.axhline(1.5)
ax1.plot(dates, values)

ax2.plot(dates, values)
ax2.axhline(1.5)

assert ax1.get_xlim() == expected_limits
assert ax1.get_xlim() == ax2.get_xlim()

I think it'd be fine to place this in matplotlib/lib/tests/test_axes.py

@tacaswell tacaswell added this to the 2.0.1 (next bug fix release) milestone Mar 1, 2017
@tacaswell
Copy link
Member

@Yasaman-Mah Looking forward to seeing your PR!

Yasaman-Mah added a commit to ibnIrshad/matplotlib that referenced this issue Mar 4, 2017
@Shang-Jia
Copy link

Shang-Jia commented Mar 6, 2017

@Yasaman-Mah
Plotting again after axhline causes the x-axis to go only up to 2016-01-10 instead of expected 2016-01-30

axs[1].plot([datetime(2016, 1, 1, 0, 0, 0), datetime(2016, 1, 30, 0, 0, 0)], [1, 2])
axs[1].axhline(1.5)
axs[1].plot([datetime(2016, 1, 1, 0, 0, 0), datetime(2016, 1, 10, 0, 0, 0)], [0, 5])

sughandj added a commit to sughandj/matplotlib that referenced this issue Mar 7, 2017
Modified axhline() function to save state of ignoring limits and restore after plotting the line
@sughandj
Copy link

sughandj commented Mar 7, 2017

I also encountered what @Shang-Jia said and I have submitted a PR #8210 that fixes all issues discussed.

@Yasaman-Mah
Copy link

@Shang-Jia
I understand that you are in UTSC, I just wanted to remind you that we are all doing this as a part of course work, and it would be against course (and possibly university's) policies to look at another student's code before submission deadline. Your comment above indicates that this is what you did.

To matplotlib developers: Thank you for all the feedback. Unfortunately, to me it seems that some of my classmates are trying to take credit for most of my fix by making minor improvements to it. In fact, I do have a better solution in mind, which I will start working on once the situation above is resolved.

kenmaca added a commit to kenmaca/matplotlib that referenced this issue Mar 14, 2017
ibnIrshad pushed a commit to ibnIrshad/matplotlib that referenced this issue Mar 29, 2017
@QuLogic QuLogic modified the milestones: 2.0.1 (next bug fix release), 2.0.2 (next bug fix release) May 3, 2017
@tacaswell tacaswell modified the milestones: 2.1.1 (next bug fix release), 2.2 (next feature release) Oct 9, 2017
@jklymak jklymak modified the milestones: needs sorting, v3.0 Mar 24, 2018
@tacaswell tacaswell modified the milestones: v3.0, v3.1 Aug 11, 2018
@tacaswell tacaswell modified the milestones: v3.1.0, v3.2.0 Mar 18, 2019
@tacaswell tacaswell removed this from the v3.2.0 milestone Sep 10, 2019
@tacaswell tacaswell added this to the needs sorting milestone Sep 10, 2019
@efiring
Copy link
Member

efiring commented Jul 14, 2020

In [3]: matplotlib.__version__
Out[3]: '3.3.0rc1.post273+gb803891a4b'

It looks like we have code in master that is designed to handle this (autoscaling with the mixed transform should be leaving x limits scaling alone), but the original example now yields a traceback:

TypeError                                 Traceback (most recent call last)
<ipython-input-1-01cdab9ed12b> in <module>
      5
      6 axs[0].axhline(1.5)
----> 7 axs[0].plot([datetime(2016, 1, 1, 0, 0, 0), datetime(2016, 1, 2, 0, 0, 0)], [1, 2])
      8
      9 axs[1].plot([datetime(2016, 1, 1, 0, 0, 0), datetime(2016, 1, 2, 0, 0, 0)], [1, 2])

~/work/programs/py/mpl/matplotlib/lib/matplotlib/axes/_axes.py in plot(self, scalex, scaley, data, *args, **kwargs)
   1743         lines = [*self._get_lines(*args, data=data, **kwargs)]
   1744         for line in lines:
-> 1745             self.add_line(line)
   1746         self._request_autoscale_view(scalex=scalex, scaley=scaley)
   1747         return lines

~/work/programs/py/mpl/matplotlib/lib/matplotlib/axes/_base.py in add_line(self, line)
   1975             line.set_clip_path(self.patch)
   1976
-> 1977         self._update_line_limits(line)
   1978         if not line.get_label():
   1979             line.set_label('_line%d' % len(self.lines))

~/work/programs/py/mpl/matplotlib/lib/matplotlib/axes/_base.py in _update_line_limits(self, line)
   1997         Figures out the data limit of the given line, updating self.dataLim.
   1998         """
-> 1999         path = line.get_path()
   2000         if path.vertices.size == 0:
   2001             return

~/work/programs/py/mpl/matplotlib/lib/matplotlib/lines.py in get_path(self)
   1009         """
   1010         if self._invalidy or self._invalidx:
-> 1011             self.recache()
   1012         return self._path
   1013

~/work/programs/py/mpl/matplotlib/lib/matplotlib/lines.py in recache(self, always)
    651         if always or self._invalidx:
    652             xconv = self.convert_xunits(self._xorig)
--> 653             x = _to_unmasked_float_array(xconv).ravel()
    654         else:
    655             x = self._x

~/work/programs/py/mpl/matplotlib/lib/matplotlib/cbook/__init__.py in _to_unmasked_float_array(x)
   1296         return np.ma.asarray(x, float).filled(np.nan)
   1297     else:
-> 1298         return np.asarray(x, float)
   1299
   1300
~/miniconda3/envs/mpl1/lib/python3.7/site-packages/numpy/core/_asarray.py in asarray(a, dtype, order)
     83
     84     """
---> 85     return array(a, dtype, copy=False, order=order)
     86
     87

TypeError: float() argument must be a string or a number, not 'datetime.datetime'

@jklymak
Copy link
Member

jklymak commented Jul 14, 2020

@efiring, that's because on master the date unit converter is not being registered. If you merge #17869 that will be fixed 😉

@jklymak
Copy link
Member

jklymak commented Jul 14, 2020

(ahem, or if you register the date converter manually)

@efiring
Copy link
Member

efiring commented Jul 15, 2020

Merged and verified: the bug reported here still exists.

@jklymak
Copy link
Member

jklymak commented May 6, 2021

This isn't just dates, this is all units. Basically if the units change on an axes (as they do here from no units to date units) axis.update_info is called, before the new plot artists are added. This calls axis.set_default_interval, and the dataLim is set to the default for the converter, which for dates is 2000-2010.

This doesn't happen if the new plot has no units. the xlims correctly remain -np.Inf, +np.Inf and the relimit works as expected.

I don't know why axis.set_default_intervals is called. And this is the only place in the code where it is called. So I think we should just not call it here either.

@dstansby
Copy link
Member Author

dstansby commented Jun 1, 2021

Thanks to everyone who worked on fixing this! I remember opening this in the early days of my contributions to Matplotlib, so great to finally see it fixed!

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