diff --git a/doc/source/whatsnew/v0.19.2.txt b/doc/source/whatsnew/v0.19.2.txt index 49c8330490ed1..52bcc7d054629 100644 --- a/doc/source/whatsnew/v0.19.2.txt +++ b/doc/source/whatsnew/v0.19.2.txt @@ -38,7 +38,8 @@ Bug Fixes - Bug in ``pd.cut`` with negative values and a single bin (:issue:`14652`) - Bug in ``pd.to_numeric`` where a 0 was not unsigned on a ``downcast='unsigned'`` argument (:issue:`14401`) - +- Bug in plotting regular and irregular timeseries using shared axes + (``sharex=True`` or ``ax.twinx()``) (:issue:`13341`, :issue:`14322`). diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index 0f7bc02e24915..f07aadba175f2 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -778,6 +778,41 @@ def test_mixed_freq_irreg_period(self): irreg.plot() ps.plot() + def test_mixed_freq_shared_ax(self): + + # GH13341, using sharex=True + idx1 = date_range('2015-01-01', periods=3, freq='M') + idx2 = idx1[:1].union(idx1[2:]) + s1 = Series(range(len(idx1)), idx1) + s2 = Series(range(len(idx2)), idx2) + + fig, (ax1, ax2) = self.plt.subplots(nrows=2, sharex=True) + s1.plot(ax=ax1) + s2.plot(ax=ax2) + + self.assertEqual(ax1.freq, 'M') + self.assertEqual(ax2.freq, 'M') + self.assertEqual(ax1.lines[0].get_xydata()[0, 0], + ax2.lines[0].get_xydata()[0, 0]) + + # using twinx + fig, ax1 = self.plt.subplots() + ax2 = ax1.twinx() + s1.plot(ax=ax1) + s2.plot(ax=ax2) + + self.assertEqual(ax1.lines[0].get_xydata()[0, 0], + ax2.lines[0].get_xydata()[0, 0]) + + # TODO (GH14330, GH14322) + # plotting the irregular first does not yet work + # fig, ax1 = plt.subplots() + # ax2 = ax1.twinx() + # s2.plot(ax=ax1) + # s1.plot(ax=ax2) + # self.assertEqual(ax1.lines[0].get_xydata()[0, 0], + # ax2.lines[0].get_xydata()[0, 0]) + @slow def test_to_weekly_resampling(self): idxh = date_range('1/1/1999', periods=52, freq='W') diff --git a/pandas/tseries/plotting.py b/pandas/tseries/plotting.py index fe64af67af0ed..89aecf2acc07e 100644 --- a/pandas/tseries/plotting.py +++ b/pandas/tseries/plotting.py @@ -162,18 +162,37 @@ def _decorate_axes(ax, freq, kwargs): ax.date_axis_info = None -def _get_freq(ax, series): - # get frequency from data - freq = getattr(series.index, 'freq', None) - if freq is None: - freq = getattr(series.index, 'inferred_freq', None) - +def _get_ax_freq(ax): + """ + Get the freq attribute of the ax object if set. + Also checks shared axes (eg when using secondary yaxis, sharex=True + or twinx) + """ ax_freq = getattr(ax, 'freq', None) if ax_freq is None: + # check for left/right ax in case of secondary yaxis if hasattr(ax, 'left_ax'): ax_freq = getattr(ax.left_ax, 'freq', None) elif hasattr(ax, 'right_ax'): ax_freq = getattr(ax.right_ax, 'freq', None) + if ax_freq is None: + # check if a shared ax (sharex/twinx) has already freq set + shared_axes = ax.get_shared_x_axes().get_siblings(ax) + if len(shared_axes) > 1: + for shared_ax in shared_axes: + ax_freq = getattr(shared_ax, 'freq', None) + if ax_freq is not None: + break + return ax_freq + + +def _get_freq(ax, series): + # get frequency from data + freq = getattr(series.index, 'freq', None) + if freq is None: + freq = getattr(series.index, 'inferred_freq', None) + + ax_freq = _get_ax_freq(ax) # use axes freq if no data freq if freq is None: @@ -191,7 +210,7 @@ def _get_freq(ax, series): def _use_dynamic_x(ax, data): freq = _get_index_freq(data) - ax_freq = getattr(ax, 'freq', None) + ax_freq = _get_ax_freq(ax) if freq is None: # convert irregular if axes has freq info freq = ax_freq @@ -244,7 +263,7 @@ def _maybe_convert_index(ax, data): freq = freq.rule_code if freq is None: - freq = getattr(ax, 'freq', None) + freq = _get_ax_freq(ax) if freq is None: raise ValueError('Could not get frequency alias for plotting')