twinx and plot_date #162

Closed
ddale opened this Issue Jun 20, 2011 · 3 comments

Comments

Projects
None yet
4 participants
Contributor

ddale commented Jun 20, 2011

Original report at SourceForge, opened Tue Aug 17 02:13:52 2010

This is my first bug submission; Please ask if more information is needed.

I'm attempting to plot two data sets on a single figure with a common x axis but separate y axes. I want the first set to correspond to the right y-axis. My strategy is to add a set of axes to the figure, then create a second set of axes using twinx(), then use plot_date() to add data to the second set of axes. It appears that there is some problem in the order in which things are done that causes this to fail when the figure is drawn.

I think the problem may have something to do with the default axis limits being (0.0, 1.0). These limits cannot be converted to dates since the range for dates is >= 1.0.

Here is a simplified example of code that will fail:

import matplotlib.pyplot as plt
import matplotlib.dates
import datetime

t = datetime.datetime.now().date()
left_varr = range(10)
right_varr = range(10)
right_varr.reverse()

tarr = matplotlib.dates.date2num([t + datetime.timedelta(days=i) for i in varr])

This will fail

fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
ax2 = ax1.twinx()

ax2.plot_date(tarr, varr)

fig.canvas.draw()

SourceForge Comments

On Fri Oct 22 10:55:06 2010, leejjoon wrote:

This has been a known problem but not fixed yet. There could be a few other workarounds. One is not to use autoscale_view.

ax1 = fig.add_subplot(1,1,1)
ax2 = ax1.twinx()
ax1.set_autoscalex_on(False)
ax2.set_autoscalex_on(False)

ax2.plot_date(tarr, left_varr)

ax2.set_xlim(min(tarr), max(tarr))

or add same data to the other axes but makes it invisible.

ax2.plot_date(tarr, left_varr)
l1, = ax1.plot_date(tarr, left_varr)
l1.set_visible(False)

I think one way to solve this issue is to check if axes have any artist (has_data method), and include only those axes with child artists during the autoscale_view. However, I'll let others review it.

On Tue Aug 17 02:18:10 2010, pzakielarz wrote:

Oops, I hit the add button too soon.

Continuing on:

The resulting traceback is:

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
draw(artist, renderer, _args, *_kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/figure.py", line 798, in draw
func(_args)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
draw(artist, renderer, *args, *_kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/axes.py", line 1942, in draw
a.draw(renderer)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
draw(artist, renderer, _args, *_kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/axis.py", line 971, in draw
tick_tups = [ t for t in self.iter_ticks()]
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/axis.py", line 904, in iter_ticks
majorLocs = self.major.locator()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/dates.py", line 743, in call
self.refresh()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/dates.py", line 752, in refresh
dmin, dmax = self.viewlim_to_dt()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/dates.py", line 524, in viewlim_to_dt
return num2date(vmin, self.tz), num2date(vmax, self.tz)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/dates.py", line 289, in num2date
if not cbook.iterable(x): return _from_ordinalf(x, tz)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/dates.py", line 203, in _from_ordinalf
dt = datetime.datetime.fromordinal(ix)
ValueError: ordinal must be >= 1

I've come up with a work around that somewhat mimics twinx() and allows the rest of my code to work properly.

ax1 = fig.add_subplot(1,1,1)

ax1.plot_date(tarr, right_varr)

ax1.yaxis.tick_right()
ax1.yaxis.set_label_position('right')

fig.canvas.draw()

ax2 = ax1.figure.add_axes(ax1.get_position(True), sharex=ax1, frameon=False)
ax2.yaxis.tick_left()
ax2.xaxis.set_visible(False)

ax2.plot_date(tarr, left_varr)

fig.canvas.draw()

This workaround sets up my axes as desired and allows the rest of my code to function as intended.

Member

pelson commented Aug 19, 2012

Confirming with:


>>> import matplotlib.pyplot as plt
>>> import matplotlib.dates
>>> import datetime
>>> 
>>> t = datetime.datetime.now().date()
>>> left_varr = range(10)
>>> right_varr = range(10)
>>> right_varr.reverse()
>>> tarr = matplotlib.dates.date2num([t + datetime.timedelta(days=i) for i in right_varr])
>>> fig = plt.figure()
>>> ax1 = fig.add_subplot(1,1,1)
>>> 
>>> ax2 = ax1.twinx()
>>> ax2.plot_date(tarr, right_varr)
[<matplotlib.lines.Line2D object at 0x1025374d0>]
>>> fig.canvas.draw()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/backends/backend_agg.py", line 433, in draw
    self.figure.draw(self.renderer)
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/artist.py", line 55, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/figure.py", line 927, in draw
    func(*args)
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/artist.py", line 55, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/axes.py", line 1998, in draw
    a.draw(renderer)
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/artist.py", line 55, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/axis.py", line 1047, in draw
    ticks_to_draw = self._update_ticks(renderer)
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/axis.py", line 937, in _update_ticks
    tick_tups = [ t for t in self.iter_ticks()]
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/axis.py", line 884, in iter_ticks
    majorLocs = self.major.locator()
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/dates.py", line 752, in __call__
    self.refresh()
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/dates.py", line 761, in refresh
    dmin, dmax = self.viewlim_to_dt()
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/dates.py", line 533, in viewlim_to_dt
    return num2date(vmin, self.tz), num2date(vmax, self.tz)
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/dates.py", line 292, in num2date
    if not cbook.iterable(x): return _from_ordinalf(x, tz)
  File "/Users/pelson/dev/matplotlib/lib/matplotlib/dates.py", line 206, in _from_ordinalf
    dt = datetime.datetime.fromordinal(ix)
ValueError: ordinal must be >= 1

On master just before the 1.2.x freeze.

Member

dmcdougall commented Nov 24, 2012

This might be useful.

Owner

efiring commented May 25, 2013

It looks like this has been fixed. Using the example above from @pelson, I can't reproduce it with 1.2.x or master. Closing.

@efiring efiring closed this May 25, 2013

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