Skip to content

Plotting: don't change visibility of xaxis labels and ticklabels if passing in a axis #9740

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 31, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions doc/source/whatsnew/v0.16.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ API changes



- When passing in an ax to ``df.plot( ..., ax=ax)``, the `sharex` kwarg will now default to `False`.
The result is that the visibility of xlabels and xticklabels will not anymore be changed. You
have to do that by yourself for the right axes in your figure or set ``sharex=True`` explicitly
(but this changes the visible for all axes in the figure, not only the one which is passed in!).
If pandas creates the subplots itself (e.g. no passed in `ax` kwarg), then the
default is still ``sharex=True`` and the visibility changes are applied.



- Add support for separating years and quarters using dashes, for
example 2014-Q1. (:issue:`9688`)
Expand Down
93 changes: 92 additions & 1 deletion pandas/tests/test_graphics.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,8 +1000,14 @@ def test_plot(self):
_check_plot_works(df.plot, xticks=[1, 5, 10])
_check_plot_works(df.plot, ylim=(-100, 100), xlim=(-100, 100))

axes = _check_plot_works(df.plot, subplots=True, title='blah')
_check_plot_works(df.plot, subplots=True, title='blah')
# We have to redo it here because _check_plot_works does two plots, once without an ax
# kwarg and once with an ax kwarg and the new sharex behaviour does not remove the
# visibility of the latter axis (as ax is present).
# see: https://github.com/pydata/pandas/issues/9737
axes = df.plot(subplots=True, title='blah')
self._check_axes_shape(axes, axes_num=3, layout=(3, 1))
#axes[0].figure.savefig("test.png")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this line. And another down on line 3587 and 3602

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

for ax in axes[:2]:
self._check_visible(ax.xaxis) # xaxis must be visible for grid
self._check_visible(ax.get_xticklabels(), visible=False)
Expand Down Expand Up @@ -3138,6 +3144,78 @@ def _check_errorbar_color(containers, expected, has_err='has_xerr'):
self._check_has_errorbars(ax, xerr=0, yerr=1)
_check_errorbar_color(ax.containers, 'green', has_err='has_yerr')

def test_sharex_and_ax(self):
# https://github.com/pydata/pandas/issues/9737
# using gridspec, the axis in fig.get_axis() are sorted differently than pandas expected
# them, so make sure that only the right ones are removed
import matplotlib.pyplot as plt
plt.close('all')
gs, axes = _generate_4_axes_via_gridspec()

df = DataFrame({"a":[1,2,3,4,5,6], "b":[1,2,3,4,5,6]})

for ax in axes:
df.plot(x="a", y="b", title="title", ax=ax, sharex=True)

gs.tight_layout(plt.gcf())
for ax in plt.gcf().get_axes():
for label in ax.get_xticklabels():
self.assertEqual(label.get_visible(), ax.is_last_row(),
"x ticklabel has wrong visiblity")
self.assertEqual(ax.xaxis.get_label().get_visible(), ax.is_last_row(),
"x label has wrong visiblity")

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

gs.tight_layout(plt.gcf())
for ax in plt.gcf().get_axes():
for label in ax.get_xticklabels():
self.assertTrue(label.get_visible(), "x ticklabel is invisible but shouldn't")
self.assertTrue(ax.xaxis.get_label().get_visible(),
"x label is invisible but shouldn't")


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

plt.close('all')
gs, axes = _generate_4_axes_via_gridspec()

df = DataFrame({"a":[1,2,3,4,5,6], "b":[1,2,3,4,5,6]})

for ax in axes:
df.plot(x="a", y="b", title="title", ax=ax, sharey=True)

gs.tight_layout(plt.gcf())
for ax in plt.gcf().get_axes():
for label in ax.get_yticklabels():
self.assertEqual(label.get_visible(), ax.is_first_col(),
"y ticklabel has wrong visiblity")
self.assertEqual(ax.yaxis.get_label().get_visible(), ax.is_first_col(),
"y label has wrong visiblity")

plt.close('all')
gs, axes = _generate_4_axes_via_gridspec()

# without sharex, no labels should be touched!
for ax in axes:
df.plot(x="a", y="b", title="title", ax=ax)

gs.tight_layout(plt.gcf())
for ax in plt.gcf().get_axes():
for label in ax.get_yticklabels():
self.assertTrue(label.get_visible(), "y ticklabel is invisible but shouldn't")
self.assertTrue(ax.yaxis.get_label().get_visible(),
"y label is invisible but shouldn't")



@tm.mplskip
class TestDataFrameGroupByPlots(TestPlotBase):
Expand Down Expand Up @@ -3612,6 +3690,19 @@ def _check_plot_works(f, *args, **kwargs):

return ret

def _generate_4_axes_via_gridspec():
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.gridspec

gs = mpl.gridspec.GridSpec(2, 2)
ax_tl = plt.subplot(gs[0,0])
ax_ll = plt.subplot(gs[1,0])
ax_tr = plt.subplot(gs[0,1])
ax_lr = plt.subplot(gs[1,1])

return gs, [ax_tl, ax_ll, ax_tr, ax_lr]


def curpath():
pth, _ = os.path.split(os.path.abspath(__file__))
Expand Down
126 changes: 83 additions & 43 deletions pandas/tools/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ class MPLPlot(object):
_attr_defaults = {'logy': False, 'logx': False, 'loglog': False,
'mark_right': True, 'stacked': False}

def __init__(self, data, kind=None, by=None, subplots=False, sharex=True,
def __init__(self, data, kind=None, by=None, subplots=False, sharex=None,
sharey=False, use_index=True,
figsize=None, grid=None, legend=True, rot=None,
ax=None, fig=None, title=None, xlim=None, ylim=None,
Expand All @@ -786,7 +786,16 @@ def __init__(self, data, kind=None, by=None, subplots=False, sharex=True,
self.sort_columns = sort_columns

self.subplots = subplots
self.sharex = sharex

if sharex is None:
if ax is None:
self.sharex = True
else:
# if we get an axis, the users should do the visibility setting...
self.sharex = False
else:
self.sharex = sharex

self.sharey = sharey
self.figsize = figsize
self.layout = layout
Expand Down Expand Up @@ -2350,10 +2359,14 @@ def _plot(data, x=None, y=None, subplots=False,
df_ax = """ax : matplotlib axes object, default None
subplots : boolean, default False
Make separate subplots for each column
sharex : boolean, default True
In case subplots=True, share x axis
sharex : boolean, default True if ax is None else False
In case subplots=True, share x axis and set some x axis labels to
invisible; defaults to True if ax is None otherwise False if an ax
is passed in; Be aware, that passing in both an ax and sharex=True
will alter all x axis labels for all axis in a figure!
sharey : boolean, default False
In case subplots=True, share y axis
In case subplots=True, share y axis and set some y axis labels to
invisible
layout : tuple (optional)
(rows, columns) for the layout of subplots"""
series_ax = """ax : matplotlib axes object
Expand Down Expand Up @@ -2465,7 +2478,7 @@ def _plot(data, x=None, y=None, subplots=False,

@Appender(_shared_docs['plot'] % _shared_doc_df_kwargs)
def plot_frame(data, x=None, y=None, kind='line', ax=None, # Dataframe unique
subplots=False, sharex=True, sharey=False, layout=None, # Dataframe unique
subplots=False, sharex=None, sharey=False, layout=None, # Dataframe unique
figsize=None, use_index=True, title=None, grid=None,
legend=True, style=None, logx=False, logy=False, loglog=False,
xticks=None, yticks=None, xlim=None, ylim=None,
Expand Down Expand Up @@ -2730,8 +2743,14 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None,
yrot : float, default None
rotation of y axis labels
ax : matplotlib axes object, default None
sharex : bool, if True, the X axis will be shared amongst all subplots.
sharey : bool, if True, the Y axis will be shared amongst all subplots.
sharex : boolean, default True if ax is None else False
In case subplots=True, share x axis and set some x axis labels to
invisible; defaults to True if ax is None otherwise False if an ax
is passed in; Be aware, that passing in both an ax and sharex=True
will alter all x axis labels for all subplots in a figure!
sharey : boolean, default False
In case subplots=True, share y axis and set some y axis labels to
invisible
figsize : tuple
The size of the figure to create in inches by default
layout: (optional) a tuple (rows, columns) for the layout of the histograms
Expand Down Expand Up @@ -3129,7 +3148,8 @@ def _subplots(naxes=None, sharex=False, sharey=False, squeeze=True,
Keyword arguments:

naxes : int
Number of required axes. Exceeded axes are set invisible. Default is nrows * ncols.
Number of required axes. Exceeded axes are set invisible. Default is
nrows * ncols.

sharex : bool
If True, the X axis will be shared amongst all subplots.
Expand Down Expand Up @@ -3256,12 +3276,12 @@ def _subplots(naxes=None, sharex=False, sharey=False, squeeze=True,
ax = fig.add_subplot(nrows, ncols, i + 1, **kwds)
axarr[i] = ax

_handle_shared_axes(axarr, nplots, naxes, nrows, ncols, sharex, sharey)

if naxes != nplots:
for ax in axarr[naxes:]:
ax.set_visible(False)

_handle_shared_axes(axarr, nplots, naxes, nrows, ncols, sharex, sharey)

if squeeze:
# Reshape the array to have the final desired dimension (nrow,ncol),
# though discarding unneeded dimensions that equal 1. If we only have
Expand All @@ -3276,44 +3296,64 @@ def _subplots(naxes=None, sharex=False, sharey=False, squeeze=True,

return fig, axes

def _remove_xlabels_from_axis(ax):
for label in ax.get_xticklabels():
label.set_visible(False)
try:
# set_visible will not be effective if
# minor axis has NullLocator and NullFormattor (default)
import matplotlib.ticker as ticker

if isinstance(ax.xaxis.get_minor_locator(), ticker.NullLocator):
ax.xaxis.set_minor_locator(ticker.AutoLocator())
if isinstance(ax.xaxis.get_minor_formatter(), ticker.NullFormatter):
ax.xaxis.set_minor_formatter(ticker.FormatStrFormatter(''))
for label in ax.get_xticklabels(minor=True):
label.set_visible(False)
except Exception: # pragma no cover
pass
ax.xaxis.get_label().set_visible(False)

def _remove_ylables_from_axis(ax):
for label in ax.get_yticklabels():
label.set_visible(False)
try:
import matplotlib.ticker as ticker
if isinstance(ax.yaxis.get_minor_locator(), ticker.NullLocator):
ax.yaxis.set_minor_locator(ticker.AutoLocator())
if isinstance(ax.yaxis.get_minor_formatter(), ticker.NullFormatter):
ax.yaxis.set_minor_formatter(ticker.FormatStrFormatter(''))
for label in ax.get_yticklabels(minor=True):
label.set_visible(False)
except Exception: # pragma no cover
pass
ax.yaxis.get_label().set_visible(False)

def _handle_shared_axes(axarr, nplots, naxes, nrows, ncols, sharex, sharey):
if nplots > 1:

# first find out the ax layout, so that we can correctly handle 'gaps"
layout = np.zeros((nrows+1,ncols+1), dtype=np.bool)
for ax in axarr:
layout[ax.rowNum, ax.colNum] = ax.get_visible()

if sharex and nrows > 1:
for ax in axarr[:naxes][:-ncols]: # only bottom row
for label in ax.get_xticklabels():
label.set_visible(False)
try:
# set_visible will not be effective if
# minor axis has NullLocator and NullFormattor (default)
import matplotlib.ticker as ticker

if isinstance(ax.xaxis.get_minor_locator(), ticker.NullLocator):
ax.xaxis.set_minor_locator(ticker.AutoLocator())
if isinstance(ax.xaxis.get_minor_formatter(), ticker.NullFormatter):
ax.xaxis.set_minor_formatter(ticker.FormatStrFormatter(''))
for label in ax.get_xticklabels(minor=True):
label.set_visible(False)
except Exception: # pragma no cover
pass
ax.xaxis.get_label().set_visible(False)
for ax in axarr:
# only the last row of subplots should get x labels -> all other off
# layout handles the case that the subplot is the last in the column,
# because below is no subplot/gap.
if not layout[ax.rowNum+1, ax.colNum]:
continue
_remove_xlabels_from_axis(ax)
if sharey and ncols > 1:
for i, ax in enumerate(axarr):
if (i % ncols) != 0: # only first column
for label in ax.get_yticklabels():
label.set_visible(False)
try:
import matplotlib.ticker as ticker
if isinstance(ax.yaxis.get_minor_locator(), ticker.NullLocator):
ax.yaxis.set_minor_locator(ticker.AutoLocator())
if isinstance(ax.yaxis.get_minor_formatter(), ticker.NullFormatter):
ax.yaxis.set_minor_formatter(ticker.FormatStrFormatter(''))
for label in ax.get_yticklabels(minor=True):
label.set_visible(False)
except Exception: # pragma no cover
pass
ax.yaxis.get_label().set_visible(False)
for ax in axarr:
# only the first column should get y labels -> set all other to off
# as we only have labels in teh first column and we always have a subplot there,
# we can skip the layout test
if ax.is_first_col():
continue
_remove_ylables_from_axis(ax)



def _flatten(axes):
Expand Down