Skip to content

Commit 4cc4364

Browse files
committed
BUG: secondary ax has incorrect right_ax property
1 parent f0ac930 commit 4cc4364

File tree

4 files changed

+41
-19
lines changed

4 files changed

+41
-19
lines changed

doc/source/whatsnew/v0.16.1.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ Bug Fixes
108108
- Bug in plotting continuously using ``secondary_y`` may not show legend properly. (:issue:`9610`, :issue:`9779`)
109109

110110
- Bug in ``DataFrame.plot(kind="hist")`` results in ``TypeError`` when ``DataFrame`` contains non-numeric columns (:issue:`9853`)
111+
- Bug in plotting ``secondary_y`` incorrectly attaches ``right_ax`` property to secondary axes specifying itself recursively. (:issue:`9861`)
111112

112113
- Bug in ``Series.quantile`` on empty Series of type ``Datetime`` or ``Timedelta`` (:issue:`9675`)
113114
- Bug in ``where`` causing incorrect results when upcasting was required (:issue:`9731`)

pandas/tests/test_graphics.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,7 @@ def test_table(self):
10671067

10681068

10691069
@tm.mplskip
1070-
class TestDataFramePlots(TestPlotBase):
1070+
class TestAADataFramePlots(TestPlotBase):
10711071
def setUp(self):
10721072
TestPlotBase.setUp(self)
10731073
import matplotlib as mpl
@@ -1609,7 +1609,10 @@ def test_line_lim(self):
16091609
self.assertEqual(xmax, lines[0].get_data()[0][-1])
16101610

16111611
axes = df.plot(secondary_y=True, subplots=True)
1612+
self._check_axes_shape(axes, axes_num=3, layout=(3, 1))
16121613
for ax in axes:
1614+
self.assertTrue(hasattr(ax, 'left_ax'))
1615+
self.assertFalse(hasattr(ax, 'right_ax'))
16131616
xmin, xmax = ax.get_xlim()
16141617
lines = ax.get_lines()
16151618
self.assertEqual(xmin, lines[0].get_data()[0][0])

pandas/tools/plotting.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -939,19 +939,21 @@ def _has_plotted_object(self, ax):
939939

940940
def _maybe_right_yaxis(self, ax, axes_num):
941941
if not self.on_right(axes_num):
942-
if hasattr(ax, 'left_ax'):
943-
# secondary axes may be passed as axes
944-
return ax.left_ax
945-
return ax
942+
# secondary axes may be passed via ax kw
943+
return self._get_ax_layer(ax)
946944

947945
if hasattr(ax, 'right_ax'):
946+
# if it has right_ax proparty, ``ax`` must be left axes
948947
return ax.right_ax
948+
elif hasattr(ax, 'left_ax'):
949+
# if it has left_ax proparty, ``ax`` must be right axes
950+
return ax
949951
else:
952+
# otherwise, create twin axes
950953
orig_ax, new_ax = ax, ax.twinx()
951954
new_ax._get_lines.color_cycle = orig_ax._get_lines.color_cycle
952955

953956
orig_ax.right_ax, new_ax.left_ax = new_ax, orig_ax
954-
new_ax.right_ax = new_ax
955957

956958
if not self._has_plotted_object(orig_ax): # no data on left y
957959
orig_ax.get_yaxis().set_visible(False)
@@ -999,9 +1001,8 @@ def result(self):
9991001
all_sec = (com.is_list_like(self.secondary_y) and
10001002
len(self.secondary_y) == self.nseries)
10011003
if (sec_true or all_sec):
1002-
# if all data is plotted on secondary,
1003-
# return secondary axes
1004-
return self.axes[0].right_ax
1004+
# if all data is plotted on secondary, return right axes
1005+
return self._get_ax_layer(self.axes[0], primary=False)
10051006
else:
10061007
return self.axes[0]
10071008

@@ -1241,11 +1242,18 @@ def _get_index_name(self):
12411242

12421243
return name
12431244

1245+
@classmethod
1246+
def _get_ax_layer(cls, ax, primary=True):
1247+
"""get left (primary) or right (secondary) axes"""
1248+
if primary:
1249+
return getattr(ax, 'left_ax', ax)
1250+
else:
1251+
return getattr(ax, 'right_ax', ax)
1252+
12441253
def _get_ax(self, i):
12451254
# get the twinx ax if appropriate
12461255
if self.subplots:
12471256
ax = self.axes[i]
1248-
12491257
ax = self._maybe_right_yaxis(ax, i)
12501258
self.axes[i] = ax
12511259
else:
@@ -2521,7 +2529,7 @@ def plot_series(data, kind='line', ax=None, # Series unique
25212529
"""
25222530
if ax is None and len(plt.get_fignums()) > 0:
25232531
ax = _gca()
2524-
ax = getattr(ax, 'left_ax', ax)
2532+
ax = MPLPlot._get_ax_layer(ax)
25252533
# is there harm in this?
25262534
if label is None:
25272535
label = data.name
@@ -3371,11 +3379,9 @@ def _flatten(axes):
33713379
def _get_all_lines(ax):
33723380
lines = ax.get_lines()
33733381

3374-
# check for right_ax, which can oddly sometimes point back to ax
3375-
if hasattr(ax, 'right_ax') and ax.right_ax != ax:
3382+
if hasattr(ax, 'right_ax'):
33763383
lines += ax.right_ax.get_lines()
33773384

3378-
# no such risk with left_ax
33793385
if hasattr(ax, 'left_ax'):
33803386
lines += ax.left_ax.get_lines()
33813387

pandas/tseries/tests/test_plotting.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,9 @@ def test_secondary_y(self):
528528

529529
ser = Series(np.random.randn(10))
530530
ser2 = Series(np.random.randn(10))
531-
ax = ser.plot(secondary_y=True).right_ax
531+
ax = ser.plot(secondary_y=True)
532+
self.assertTrue(hasattr(ax, 'left_ax'))
533+
self.assertFalse(hasattr(ax, 'right_ax'))
532534
fig = ax.get_figure()
533535
axes = fig.get_axes()
534536
l = ax.get_lines()[0]
@@ -543,16 +545,22 @@ def test_secondary_y(self):
543545
plt.close(ax2.get_figure())
544546

545547
ax = ser2.plot()
546-
ax2 = ser.plot(secondary_y=True).right_ax
548+
ax2 = ser.plot(secondary_y=True)
547549
self.assertTrue(ax.get_yaxis().get_visible())
550+
self.assertFalse(hasattr(ax, 'left_ax'))
551+
self.assertTrue(hasattr(ax, 'right_ax'))
552+
self.assertTrue(hasattr(ax2, 'left_ax'))
553+
self.assertFalse(hasattr(ax2, 'right_ax'))
548554

549555
@slow
550556
def test_secondary_y_ts(self):
551557
import matplotlib.pyplot as plt
552558
idx = date_range('1/1/2000', periods=10)
553559
ser = Series(np.random.randn(10), idx)
554560
ser2 = Series(np.random.randn(10), idx)
555-
ax = ser.plot(secondary_y=True).right_ax
561+
ax = ser.plot(secondary_y=True)
562+
self.assertTrue(hasattr(ax, 'left_ax'))
563+
self.assertFalse(hasattr(ax, 'right_ax'))
556564
fig = ax.get_figure()
557565
axes = fig.get_axes()
558566
l = ax.get_lines()[0]
@@ -577,7 +585,9 @@ def test_secondary_kde(self):
577585

578586
import matplotlib.pyplot as plt
579587
ser = Series(np.random.randn(10))
580-
ax = ser.plot(secondary_y=True, kind='density').right_ax
588+
ax = ser.plot(secondary_y=True, kind='density')
589+
self.assertTrue(hasattr(ax, 'left_ax'))
590+
self.assertFalse(hasattr(ax, 'right_ax'))
581591
fig = ax.get_figure()
582592
axes = fig.get_axes()
583593
self.assertEqual(axes[1].get_yaxis().get_ticks_position(), 'right')
@@ -890,7 +900,9 @@ def test_secondary_upsample(self):
890900
ax = high.plot(secondary_y=True)
891901
for l in ax.get_lines():
892902
self.assertEqual(PeriodIndex(l.get_xdata()).freq, 'D')
893-
for l in ax.right_ax.get_lines():
903+
self.assertTrue(hasattr(ax, 'left_ax'))
904+
self.assertFalse(hasattr(ax, 'right_ax'))
905+
for l in ax.left_ax.get_lines():
894906
self.assertEqual(PeriodIndex(l.get_xdata()).freq, 'D')
895907

896908
@slow

0 commit comments

Comments
 (0)