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.