Skip to content

Commit 17132ac

Browse files
sinhrksjreback
authored andcommitted
BUG: secondary ax has incorrect right_ax property
1 parent 8fe1cf6 commit 17132ac

File tree

4 files changed

+42
-19
lines changed

4 files changed

+42
-19
lines changed

doc/source/whatsnew/v0.16.1.txt

+3
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ Bug Fixes
106106
- Bug in plotting continuously using ``secondary_y`` may not show legend properly. (:issue:`9610`, :issue:`9779`)
107107
- Bug in ``DataFrame.plot(kind="hist")`` results in ``TypeError`` when ``DataFrame`` contains non-numeric columns (:issue:`9853`)
108108
- Bug where repeated plotting of ``DataFrame`` with a ``DatetimeIndex`` may raise ``TypeError`` (:issue:`9852`)
109+
110+
- Bug in plotting ``secondary_y`` incorrectly attaches ``right_ax`` property to secondary axes specifying itself recursively. (:issue:`9861`)
111+
109112
- Bug in ``Series.quantile`` on empty Series of type ``Datetime`` or ``Timedelta`` (:issue:`9675`)
110113
- Bug in ``where`` causing incorrect results when upcasting was required (:issue:`9731`)
111114
- Bug in ``FloatArrayFormatter`` where decision boundary for displaying "small" floats in decimal format is off by one order of magnitude for a given display.precision (:issue:`9764`)

pandas/tests/test_graphics.py

+3
Original file line numberDiff line numberDiff line change
@@ -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

+19-14
Original file line numberDiff line numberDiff line change
@@ -928,19 +928,21 @@ def _has_plotted_object(self, ax):
928928

929929
def _maybe_right_yaxis(self, ax, axes_num):
930930
if not self.on_right(axes_num):
931-
if hasattr(ax, 'left_ax'):
932-
# secondary axes may be passed as axes
933-
return ax.left_ax
934-
return ax
931+
# secondary axes may be passed via ax kw
932+
return self._get_ax_layer(ax)
935933

936934
if hasattr(ax, 'right_ax'):
935+
# if it has right_ax proparty, ``ax`` must be left axes
937936
return ax.right_ax
937+
elif hasattr(ax, 'left_ax'):
938+
# if it has left_ax proparty, ``ax`` must be right axes
939+
return ax
938940
else:
941+
# otherwise, create twin axes
939942
orig_ax, new_ax = ax, ax.twinx()
940943
new_ax._get_lines.color_cycle = orig_ax._get_lines.color_cycle
941944

942945
orig_ax.right_ax, new_ax.left_ax = new_ax, orig_ax
943-
new_ax.right_ax = new_ax
944946

945947
if not self._has_plotted_object(orig_ax): # no data on left y
946948
orig_ax.get_yaxis().set_visible(False)
@@ -988,9 +990,8 @@ def result(self):
988990
all_sec = (com.is_list_like(self.secondary_y) and
989991
len(self.secondary_y) == self.nseries)
990992
if (sec_true or all_sec):
991-
# if all data is plotted on secondary,
992-
# return secondary axes
993-
return self.axes[0].right_ax
993+
# if all data is plotted on secondary, return right axes
994+
return self._get_ax_layer(self.axes[0], primary=False)
994995
else:
995996
return self.axes[0]
996997

@@ -1230,11 +1231,18 @@ def _get_index_name(self):
12301231

12311232
return name
12321233

1234+
@classmethod
1235+
def _get_ax_layer(cls, ax, primary=True):
1236+
"""get left (primary) or right (secondary) axes"""
1237+
if primary:
1238+
return getattr(ax, 'left_ax', ax)
1239+
else:
1240+
return getattr(ax, 'right_ax', ax)
1241+
12331242
def _get_ax(self, i):
12341243
# get the twinx ax if appropriate
12351244
if self.subplots:
12361245
ax = self.axes[i]
1237-
12381246
ax = self._maybe_right_yaxis(ax, i)
12391247
self.axes[i] = ax
12401248
else:
@@ -2501,8 +2509,7 @@ def plot_series(data, kind='line', ax=None, # Series unique
25012509
"""
25022510
if ax is None and len(plt.get_fignums()) > 0:
25032511
ax = _gca()
2504-
ax = getattr(ax, 'left_ax', ax)
2505-
2512+
ax = MPLPlot._get_ax_layer(ax)
25062513
return _plot(data, kind=kind, ax=ax,
25072514
figsize=figsize, use_index=use_index, title=title,
25082515
grid=grid, legend=legend,
@@ -3349,11 +3356,9 @@ def _flatten(axes):
33493356
def _get_all_lines(ax):
33503357
lines = ax.get_lines()
33513358

3352-
# check for right_ax, which can oddly sometimes point back to ax
3353-
if hasattr(ax, 'right_ax') and ax.right_ax != ax:
3359+
if hasattr(ax, 'right_ax'):
33543360
lines += ax.right_ax.get_lines()
33553361

3356-
# no such risk with left_ax
33573362
if hasattr(ax, 'left_ax'):
33583363
lines += ax.left_ax.get_lines()
33593364

pandas/tseries/tests/test_plotting.py

+17-5
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')
@@ -922,7 +932,9 @@ def test_secondary_upsample(self):
922932
ax = high.plot(secondary_y=True)
923933
for l in ax.get_lines():
924934
self.assertEqual(PeriodIndex(l.get_xdata()).freq, 'D')
925-
for l in ax.right_ax.get_lines():
935+
self.assertTrue(hasattr(ax, 'left_ax'))
936+
self.assertFalse(hasattr(ax, 'right_ax'))
937+
for l in ax.left_ax.get_lines():
926938
self.assertEqual(PeriodIndex(l.get_xdata()).freq, 'D')
927939

928940
@slow

0 commit comments

Comments
 (0)