Skip to content

Commit 31c2b80

Browse files
committed
BUG: DataFrame.plot may not handle shared ax properly
1 parent b4ce396 commit 31c2b80

File tree

3 files changed

+269
-71
lines changed

3 files changed

+269
-71
lines changed

doc/source/whatsnew/v0.17.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -824,3 +824,5 @@ Bug Fixes
824824
- Bug in ``DataFrame.where`` when handling Series slicing (:issue:`10218`, :issue:`9558`)
825825
- Bug where ``pd.read_gbq`` throws ``ValueError`` when Bigquery returns zero rows (:issue:`10273`)
826826
- Bug in ``to_json`` which was causing segmentation fault when serializing 0-rank ndarray (:issue:`9576`)
827+
- Bug in plotting functions may raise ``IndexError`` when plotted on ``GridSpec`` (:issue:`10819`)
828+
- Bug in plot result may show unnecessary minor ticklabels (:issue:`10657`)

pandas/tests/test_graphics.py

+225-30
Original file line numberDiff line numberDiff line change
@@ -3210,6 +3210,7 @@ def _check_errorbar_color(containers, expected, has_err='has_xerr'):
32103210
self._check_has_errorbars(ax, xerr=0, yerr=1)
32113211
_check_errorbar_color(ax.containers, 'green', has_err='has_yerr')
32123212

3213+
@slow
32133214
def test_sharex_and_ax(self):
32143215
# https://github.com/pydata/pandas/issues/9737
32153216
# using gridspec, the axis in fig.get_axis() are sorted differently than pandas expected
@@ -3218,68 +3219,96 @@ def test_sharex_and_ax(self):
32183219
plt.close('all')
32193220
gs, axes = _generate_4_axes_via_gridspec()
32203221

3221-
df = DataFrame({"a":[1,2,3,4,5,6], "b":[1,2,3,4,5,6]})
3222+
df = DataFrame({"a": [1, 2, 3, 4, 5, 6],
3223+
"b": [1, 2, 3, 4, 5, 6],
3224+
"c": [1, 2, 3, 4, 5, 6],
3225+
"d": [1, 2, 3, 4, 5, 6]})
3226+
3227+
def _check(axes):
3228+
for ax in axes:
3229+
self.assertEqual(len(ax.lines), 1)
3230+
self._check_visible(ax.get_yticklabels(), visible=True)
3231+
for ax in [axes[0], axes[2]]:
3232+
self._check_visible(ax.get_xticklabels(), visible=False)
3233+
self._check_visible(ax.get_xticklabels(minor=True), visible=False)
3234+
for ax in [axes[1], axes[3]]:
3235+
self._check_visible(ax.get_xticklabels(), visible=True)
3236+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
32223237

32233238
for ax in axes:
32243239
df.plot(x="a", y="b", title="title", ax=ax, sharex=True)
3225-
32263240
gs.tight_layout(plt.gcf())
3227-
for ax in plt.gcf().get_axes():
3228-
for label in ax.get_xticklabels():
3229-
self.assertEqual(label.get_visible(), ax.is_last_row(),
3230-
"x ticklabel has wrong visiblity")
3231-
self.assertEqual(ax.xaxis.get_label().get_visible(), ax.is_last_row(),
3232-
"x label has wrong visiblity")
3241+
_check(axes)
3242+
tm.close()
3243+
3244+
gs, axes = _generate_4_axes_via_gridspec()
3245+
with tm.assert_produces_warning(UserWarning):
3246+
axes = df.plot(subplots=True, ax=axes, sharex=True)
3247+
_check(axes)
3248+
tm.close()
32333249

3234-
plt.close('all')
32353250
gs, axes = _generate_4_axes_via_gridspec()
32363251
# without sharex, no labels should be touched!
32373252
for ax in axes:
32383253
df.plot(x="a", y="b", title="title", ax=ax)
32393254

32403255
gs.tight_layout(plt.gcf())
3241-
for ax in plt.gcf().get_axes():
3242-
for label in ax.get_xticklabels():
3243-
self.assertTrue(label.get_visible(), "x ticklabel is invisible but shouldn't")
3244-
self.assertTrue(ax.xaxis.get_label().get_visible(),
3245-
"x label is invisible but shouldn't")
3246-
3256+
for ax in axes:
3257+
self.assertEqual(len(ax.lines), 1)
3258+
self._check_visible(ax.get_yticklabels(), visible=True)
3259+
self._check_visible(ax.get_xticklabels(), visible=True)
3260+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
3261+
tm.close()
32473262

3263+
@slow
32483264
def test_sharey_and_ax(self):
32493265
# https://github.com/pydata/pandas/issues/9737
32503266
# using gridspec, the axis in fig.get_axis() are sorted differently than pandas expected
32513267
# them, so make sure that only the right ones are removed
32523268
import matplotlib.pyplot as plt
32533269

3254-
plt.close('all')
32553270
gs, axes = _generate_4_axes_via_gridspec()
32563271

3257-
df = DataFrame({"a":[1,2,3,4,5,6], "b":[1,2,3,4,5,6]})
3272+
df = DataFrame({"a": [1, 2, 3, 4, 5, 6],
3273+
"b": [1, 2, 3, 4, 5, 6],
3274+
"c": [1, 2, 3, 4, 5, 6],
3275+
"d": [1, 2, 3, 4, 5, 6]})
3276+
3277+
def _check(axes):
3278+
for ax in axes:
3279+
self.assertEqual(len(ax.lines), 1)
3280+
self._check_visible(ax.get_xticklabels(), visible=True)
3281+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
3282+
for ax in [axes[0], axes[1]]:
3283+
self._check_visible(ax.get_yticklabels(), visible=True)
3284+
for ax in [axes[2], axes[3]]:
3285+
self._check_visible(ax.get_yticklabels(), visible=False)
32583286

32593287
for ax in axes:
32603288
df.plot(x="a", y="b", title="title", ax=ax, sharey=True)
3261-
32623289
gs.tight_layout(plt.gcf())
3263-
for ax in plt.gcf().get_axes():
3264-
for label in ax.get_yticklabels():
3265-
self.assertEqual(label.get_visible(), ax.is_first_col(),
3266-
"y ticklabel has wrong visiblity")
3267-
self.assertEqual(ax.yaxis.get_label().get_visible(), ax.is_first_col(),
3268-
"y label has wrong visiblity")
3290+
_check(axes)
3291+
tm.close()
32693292

3270-
plt.close('all')
32713293
gs, axes = _generate_4_axes_via_gridspec()
3294+
with tm.assert_produces_warning(UserWarning):
3295+
axes = df.plot(subplots=True, ax=axes, sharey=True)
3296+
3297+
gs.tight_layout(plt.gcf())
3298+
_check(axes)
3299+
tm.close()
32723300

3301+
gs, axes = _generate_4_axes_via_gridspec()
32733302
# without sharex, no labels should be touched!
32743303
for ax in axes:
32753304
df.plot(x="a", y="b", title="title", ax=ax)
32763305

32773306
gs.tight_layout(plt.gcf())
3278-
for ax in plt.gcf().get_axes():
3279-
for label in ax.get_yticklabels():
3280-
self.assertTrue(label.get_visible(), "y ticklabel is invisible but shouldn't")
3281-
self.assertTrue(ax.yaxis.get_label().get_visible(),
3282-
"y label is invisible but shouldn't")
3307+
for ax in axes:
3308+
self.assertEqual(len(ax.lines), 1)
3309+
self._check_visible(ax.get_yticklabels(), visible=True)
3310+
self._check_visible(ax.get_xticklabels(), visible=True)
3311+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
32833312

32843313
def test_memory_leak(self):
32853314
""" Check that every plot type gets properly collected. """
@@ -3311,6 +3340,172 @@ def test_memory_leak(self):
33113340
# need to actually access something to get an error
33123341
results[key].lines
33133342

3343+
@slow
3344+
def test_df_subplots_patterns_minorticks(self):
3345+
# GH 10657
3346+
import matplotlib.pyplot as plt
3347+
3348+
df = DataFrame(np.random.randn(10, 2),
3349+
index=date_range('1/1/2000', periods=10),
3350+
columns=list('AB'))
3351+
3352+
# shared subplots
3353+
fig, axes = plt.subplots(2, 1, sharex=True)
3354+
axes = df.plot(subplots=True, ax=axes)
3355+
for ax in axes:
3356+
self.assertEqual(len(ax.lines), 1)
3357+
self._check_visible(ax.get_yticklabels(), visible=True)
3358+
# xaxis of 1st ax must be hidden
3359+
self._check_visible(axes[0].get_xticklabels(), visible=False)
3360+
self._check_visible(axes[0].get_xticklabels(minor=True), visible=False)
3361+
self._check_visible(axes[1].get_xticklabels(), visible=True)
3362+
self._check_visible(axes[1].get_xticklabels(minor=True), visible=True)
3363+
tm.close()
3364+
3365+
fig, axes = plt.subplots(2, 1)
3366+
with tm.assert_produces_warning(UserWarning):
3367+
axes = df.plot(subplots=True, ax=axes, sharex=True)
3368+
for ax in axes:
3369+
self.assertEqual(len(ax.lines), 1)
3370+
self._check_visible(ax.get_yticklabels(), visible=True)
3371+
# xaxis of 1st ax must be hidden
3372+
self._check_visible(axes[0].get_xticklabels(), visible=False)
3373+
self._check_visible(axes[0].get_xticklabels(minor=True), visible=False)
3374+
self._check_visible(axes[1].get_xticklabels(), visible=True)
3375+
self._check_visible(axes[1].get_xticklabels(minor=True), visible=True)
3376+
tm.close()
3377+
3378+
# not shared
3379+
fig, axes = plt.subplots(2, 1)
3380+
axes = df.plot(subplots=True, ax=axes)
3381+
for ax in axes:
3382+
self.assertEqual(len(ax.lines), 1)
3383+
self._check_visible(ax.get_yticklabels(), visible=True)
3384+
self._check_visible(ax.get_xticklabels(), visible=True)
3385+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
3386+
tm.close()
3387+
3388+
@slow
3389+
def test_df_gridspec_patterns(self):
3390+
# GH 10819
3391+
import matplotlib.pyplot as plt
3392+
import matplotlib.gridspec as gridspec
3393+
3394+
ts = Series(np.random.randn(10),
3395+
index=date_range('1/1/2000', periods=10))
3396+
3397+
df = DataFrame(np.random.randn(10, 2), index=ts.index,
3398+
columns=list('AB'))
3399+
3400+
def _get_vertical_grid():
3401+
gs = gridspec.GridSpec(3, 1)
3402+
fig = plt.figure()
3403+
ax1 = fig.add_subplot(gs[:2, :])
3404+
ax2 = fig.add_subplot(gs[2, :])
3405+
return ax1, ax2
3406+
3407+
def _get_horizontal_grid():
3408+
gs = gridspec.GridSpec(1, 3)
3409+
fig = plt.figure()
3410+
ax1 = fig.add_subplot(gs[:, :2])
3411+
ax2 = fig.add_subplot(gs[:, 2])
3412+
return ax1, ax2
3413+
3414+
for ax1, ax2 in [_get_vertical_grid(), _get_horizontal_grid()]:
3415+
ax1 = ts.plot(ax=ax1)
3416+
self.assertEqual(len(ax1.lines), 1)
3417+
ax2 = df.plot(ax=ax2)
3418+
self.assertEqual(len(ax2.lines), 2)
3419+
for ax in [ax1, ax2]:
3420+
self._check_visible(ax.get_yticklabels(), visible=True)
3421+
self._check_visible(ax.get_xticklabels(), visible=True)
3422+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
3423+
tm.close()
3424+
3425+
# subplots=True
3426+
for ax1, ax2 in [_get_vertical_grid(), _get_horizontal_grid()]:
3427+
axes = df.plot(subplots=True, ax=[ax1, ax2])
3428+
self.assertEqual(len(ax1.lines), 1)
3429+
self.assertEqual(len(ax2.lines), 1)
3430+
for ax in axes:
3431+
self._check_visible(ax.get_yticklabels(), visible=True)
3432+
self._check_visible(ax.get_xticklabels(), visible=True)
3433+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
3434+
tm.close()
3435+
3436+
# vertical / subplots / sharex=True / sharey=True
3437+
ax1, ax2 = _get_vertical_grid()
3438+
with tm.assert_produces_warning(UserWarning):
3439+
axes = df.plot(subplots=True, ax=[ax1, ax2],
3440+
sharex=True, sharey=True)
3441+
self.assertEqual(len(axes[0].lines), 1)
3442+
self.assertEqual(len(axes[1].lines), 1)
3443+
for ax in [ax1, ax2]:
3444+
# yaxis are visible because there is only one column
3445+
self._check_visible(ax.get_yticklabels(), visible=True)
3446+
# xaxis of axes0 (top) are hidden
3447+
self._check_visible(axes[0].get_xticklabels(), visible=False)
3448+
self._check_visible(axes[0].get_xticklabels(minor=True), visible=False)
3449+
self._check_visible(axes[1].get_xticklabels(), visible=True)
3450+
self._check_visible(axes[1].get_xticklabels(minor=True), visible=True)
3451+
tm.close()
3452+
3453+
# horizontal / subplots / sharex=True / sharey=True
3454+
ax1, ax2 = _get_horizontal_grid()
3455+
with tm.assert_produces_warning(UserWarning):
3456+
axes = df.plot(subplots=True, ax=[ax1, ax2],
3457+
sharex=True, sharey=True)
3458+
self.assertEqual(len(axes[0].lines), 1)
3459+
self.assertEqual(len(axes[1].lines), 1)
3460+
self._check_visible(axes[0].get_yticklabels(), visible=True)
3461+
# yaxis of axes1 (right) are hidden
3462+
self._check_visible(axes[1].get_yticklabels(), visible=False)
3463+
for ax in [ax1, ax2]:
3464+
# xaxis are visible because there is only one column
3465+
self._check_visible(ax.get_xticklabels(), visible=True)
3466+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
3467+
tm.close()
3468+
3469+
# boxed
3470+
def _get_boxed_grid():
3471+
gs = gridspec.GridSpec(3,3)
3472+
fig = plt.figure()
3473+
ax1 = fig.add_subplot(gs[:2, :2])
3474+
ax2 = fig.add_subplot(gs[:2, 2])
3475+
ax3 = fig.add_subplot(gs[2, :2])
3476+
ax4 = fig.add_subplot(gs[2, 2])
3477+
return ax1, ax2, ax3, ax4
3478+
3479+
axes = _get_boxed_grid()
3480+
df = DataFrame(np.random.randn(10, 4),
3481+
index=ts.index, columns=list('ABCD'))
3482+
axes = df.plot(subplots=True, ax=axes)
3483+
for ax in axes:
3484+
self.assertEqual(len(ax.lines), 1)
3485+
# axis are visible because these are not shared
3486+
self._check_visible(ax.get_yticklabels(), visible=True)
3487+
self._check_visible(ax.get_xticklabels(), visible=True)
3488+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
3489+
tm.close()
3490+
3491+
# subplots / sharex=True / sharey=True
3492+
axes = _get_boxed_grid()
3493+
with tm.assert_produces_warning(UserWarning):
3494+
axes = df.plot(subplots=True, ax=axes, sharex=True, sharey=True)
3495+
for ax in axes:
3496+
self.assertEqual(len(ax.lines), 1)
3497+
for ax in [axes[0], axes[2]]: # left column
3498+
self._check_visible(ax.get_yticklabels(), visible=True)
3499+
for ax in [axes[1], axes[3]]: # right column
3500+
self._check_visible(ax.get_yticklabels(), visible=False)
3501+
for ax in [axes[0], axes[1]]: # top row
3502+
self._check_visible(ax.get_xticklabels(), visible=False)
3503+
self._check_visible(ax.get_xticklabels(minor=True), visible=False)
3504+
for ax in [axes[2], axes[3]]: # bottom row
3505+
self._check_visible(ax.get_xticklabels(), visible=True)
3506+
self._check_visible(ax.get_xticklabels(minor=True), visible=True)
3507+
tm.close()
3508+
33143509
@slow
33153510
def test_df_grid_settings(self):
33163511
# Make sure plot defaults to rcParams['axes.grid'] setting, GH 9792

0 commit comments

Comments
 (0)