diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 1fff30525853d..1de02e24910ec 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -458,6 +458,8 @@ Plotting - Fixed bug where :class:`api.extensions.ExtensionArray` could not be used in matplotlib plotting (:issue:`25587`) - Bug in an error message in :meth:`DataFrame.plot`. Improved the error message if non-numerics are passed to :meth:`DataFrame.plot` (:issue:`25481`) - Bug in incorrect ticklabel positions when plotting an index that are non-numeric / non-datetime (:issue:`7612` :issue:`15912` :issue:`22334`) +- Fixed bug causing plots of :class:`PeriodIndex` timeseries to fail if the frequency is a multiple of the frequency rule code (:issue:`14763`) +- - - diff --git a/pandas/plotting/_timeseries.py b/pandas/plotting/_timeseries.py index f836bd0cd52d7..4460537f79b80 100644 --- a/pandas/plotting/_timeseries.py +++ b/pandas/plotting/_timeseries.py @@ -262,7 +262,7 @@ def _get_index_freq(data): def _maybe_convert_index(ax, data): # tsplot converts automatically, but don't want to convert index # over and over for DataFrames - if isinstance(data.index, ABCDatetimeIndex): + if isinstance(data.index, (ABCDatetimeIndex, ABCPeriodIndex)): freq = getattr(data.index, 'freq', None) if freq is None: @@ -279,7 +279,10 @@ def _maybe_convert_index(ax, data): freq = get_base_alias(freq) freq = frequencies.get_period_alias(freq) - data = data.to_period(freq=freq) + if isinstance(data.index, ABCDatetimeIndex): + data = data.to_period(freq=freq) + elif isinstance(data.index, ABCPeriodIndex): + data.index = data.index.asfreq(freq=freq) return data diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index c1987a179cf51..db5316eb3c15d 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -26,8 +26,9 @@ class TestTSPlot(TestPlotBase): def setup_method(self, method): TestPlotBase.setup_method(self, method) - freq = ['S', 'T', 'H', 'D', 'W', 'M', 'Q', 'A'] - idx = [period_range('12/31/1999', freq=x, periods=100) for x in freq] + self.freq = ['S', 'T', 'H', 'D', 'W', 'M', 'Q', 'A'] + idx = [ + period_range('12/31/1999', freq=x, periods=100) for x in self.freq] self.period_ser = [Series(np.random.randn(len(x)), x) for x in idx] self.period_df = [DataFrame(np.random.randn(len(x), 3), index=x, columns=['A', 'B', 'C']) @@ -209,6 +210,16 @@ def test_line_plot_period_series(self): for s in self.period_ser: _check_plot_works(s.plot, s.index.freq) + @pytest.mark.slow + @pytest.mark.parametrize( + 'frqncy', ['1S', '3S', '5T', '7H', '4D', '8W', '11M', '3A']) + def test_line_plot_period_mlt_series(self, frqncy): + # test period index line plot for series with multiples (`mlt`) of the + # frequency (`frqncy`) rule code. tests resolution of issue #14763 + idx = period_range('12/31/1999', freq=frqncy, periods=100) + s = Series(np.random.randn(len(idx)), idx) + _check_plot_works(s.plot, s.index.freq.rule_code) + @pytest.mark.slow def test_line_plot_datetime_series(self): for s in self.datetime_ser: @@ -219,6 +230,19 @@ def test_line_plot_period_frame(self): for df in self.period_df: _check_plot_works(df.plot, df.index.freq) + @pytest.mark.slow + @pytest.mark.parametrize( + 'frqncy', ['1S', '3S', '5T', '7H', '4D', '8W', '11M', '3A']) + def test_line_plot_period_mlt_frame(self, frqncy): + # test period index line plot for DataFrames with multiples (`mlt`) + # of the frequency (`frqncy`) rule code. tests resolution of issue + # #14763 + idx = period_range('12/31/1999', freq=frqncy, periods=100) + df = DataFrame(np.random.randn(len(idx), 3), index=idx, + columns=['A', 'B', 'C']) + freq = df.index.asfreq(df.index.freq.rule_code).freq + _check_plot_works(df.plot, freq) + @pytest.mark.slow def test_line_plot_datetime_frame(self): for df in self.datetime_df: