From a14edfe2c5ae8ab0bfe89adbf51a2e4e3a893c65 Mon Sep 17 00:00:00 2001 From: JoElfner Date: Mon, 29 Apr 2019 21:24:27 +0200 Subject: [PATCH 01/13] BUG: Enable plotting with PeriodIndex with arbitrary frequencies, resolves #14763 --- pandas/plotting/_timeseries.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pandas/plotting/_timeseries.py b/pandas/plotting/_timeseries.py index f836bd0cd52d7..b063a3899bfb8 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) return data From 53441fe881e294e5fd493feca61110184f098f13 Mon Sep 17 00:00:00 2001 From: JoElfner Date: Mon, 29 Apr 2019 21:44:47 +0200 Subject: [PATCH 02/13] Added tests for arbitrary freqs --- pandas/tests/plotting/test_datetimelike.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index c451228b5b319..b3525677d742c 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -4,6 +4,7 @@ import sys import numpy as np +import itertools import pytest from pandas.compat import lrange @@ -28,6 +29,8 @@ def setup_method(self, method): TestPlotBase.setup_method(self, method) freq = ['S', 'T', 'H', 'D', 'W', 'M', 'Q', 'A'] + mults = [1, 5, 23] + freq = [str(mlt) + frq for frq, mlt in itertools.product(freq, mults)] idx = [period_range('12/31/1999', freq=x, periods=100) for x in 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, From 21b5e1f10298212fd496721bae2890fe168e88da Mon Sep 17 00:00:00 2001 From: JoElfner Date: Tue, 30 Apr 2019 12:49:41 +0200 Subject: [PATCH 03/13] Adapt existing tests for arbitrary freq PeriodIndex, args formatting updated --- pandas/plotting/_timeseries.py | 2 +- pandas/tests/plotting/test_datetimelike.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pandas/plotting/_timeseries.py b/pandas/plotting/_timeseries.py index b063a3899bfb8..4460537f79b80 100644 --- a/pandas/plotting/_timeseries.py +++ b/pandas/plotting/_timeseries.py @@ -282,7 +282,7 @@ def _maybe_convert_index(ax, data): if isinstance(data.index, ABCDatetimeIndex): data = data.to_period(freq=freq) elif isinstance(data.index, ABCPeriodIndex): - data.index = data.index.asfreq(freq) + 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 b3525677d742c..30256a79fe9be 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -211,7 +211,7 @@ def check_format_of_first_point(ax, expected_string): @pytest.mark.slow def test_line_plot_period_series(self): for s in self.period_ser: - _check_plot_works(s.plot, s.index.freq) + _check_plot_works(s.plot, s.index.freq.rule_code) @pytest.mark.slow def test_line_plot_datetime_series(self): @@ -221,7 +221,8 @@ def test_line_plot_datetime_series(self): @pytest.mark.slow def test_line_plot_period_frame(self): for df in self.period_df: - _check_plot_works(df.plot, df.index.freq) + 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): From be49874b42ba51eeabfa03d96b4f3ca2497e49a1 Mon Sep 17 00:00:00 2001 From: JoElfner Date: Tue, 30 Apr 2019 13:37:33 +0200 Subject: [PATCH 04/13] test_tsplot updated --- pandas/tests/plotting/test_datetimelike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index 30256a79fe9be..c61f605301ba3 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -125,7 +125,7 @@ def f(*args, **kwds): return tsplot(s, self.plt.Axes.plot, *args, **kwds) for s in self.period_ser: - _check_plot_works(f, s.index.freq, ax=ax, series=s) + _check_plot_works(f, s.index.freq.rule_code, ax=ax, series=s) for s in self.datetime_ser: _check_plot_works(f, s.index.freq.rule_code, ax=ax, series=s) From dbd95fc5477770bfd96b31fa9a205ef86bff5830 Mon Sep 17 00:00:00 2001 From: JoElfner Date: Tue, 30 Apr 2019 13:56:36 +0200 Subject: [PATCH 05/13] test_tsplot updated --- pandas/tests/plotting/test_datetimelike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index c61f605301ba3..493059ff9f2b0 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -1,10 +1,10 @@ """ Test cases for time series specific (freq conversion, etc) """ from datetime import date, datetime, time, timedelta +import itertools import pickle import sys import numpy as np -import itertools import pytest from pandas.compat import lrange From 375a1371aaca14db930dec29c0ea839aa2667852 Mon Sep 17 00:00:00 2001 From: JoElfner Date: Tue, 30 Apr 2019 17:59:06 +0200 Subject: [PATCH 06/13] reverted existing tests, created new --- pandas/tests/plotting/test_datetimelike.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index 493059ff9f2b0..204e20f4d4aaa 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -29,13 +29,18 @@ def setup_method(self, method): TestPlotBase.setup_method(self, method) freq = ['S', 'T', 'H', 'D', 'W', 'M', 'Q', 'A'] - mults = [1, 5, 23] - freq = [str(mlt) + frq for frq, mlt in itertools.product(freq, mults)] idx = [period_range('12/31/1999', freq=x, periods=100) for x in 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']) for x in idx] + mults = [1, 23] # setup periods with multiples of freq.rule_code + freq = [str(mlt) + frq for frq, mlt in itertools.product(freq, mults)] + idx = [period_range('12/31/1999', freq=x, periods=100) for x in freq] + self.period_mlt_ser = [Series(np.random.randn(len(x)), x) for x in idx] + self.period_mlt_df = [DataFrame(np.random.randn(len(x), 3), index=x, + columns=['A', 'B', 'C']) + for x in idx] freq = ['S', 'T', 'H', 'D', 'W', 'M', 'Q-DEC', 'A', '1B30Min'] idx = [date_range('12/31/1999', freq=x, periods=100) for x in freq] @@ -125,7 +130,7 @@ def f(*args, **kwds): return tsplot(s, self.plt.Axes.plot, *args, **kwds) for s in self.period_ser: - _check_plot_works(f, s.index.freq.rule_code, ax=ax, series=s) + _check_plot_works(f, s.index.freq, ax=ax, series=s) for s in self.datetime_ser: _check_plot_works(f, s.index.freq.rule_code, ax=ax, series=s) @@ -211,6 +216,11 @@ def check_format_of_first_point(ax, expected_string): @pytest.mark.slow def test_line_plot_period_series(self): for s in self.period_ser: + _check_plot_works(s.plot, s.index.freq) + + @pytest.mark.slow + def test_line_plot_period_mlt_series(self): + for s in self.period_mlt_ser: _check_plot_works(s.plot, s.index.freq.rule_code) @pytest.mark.slow @@ -221,6 +231,11 @@ def test_line_plot_datetime_series(self): @pytest.mark.slow def test_line_plot_period_frame(self): for df in self.period_df: + _check_plot_works(df.plot, df.index.freq) + + @pytest.mark.slow + def test_line_plot_period_mlt_frame(self): + for df in self.period_mlt_df: freq = df.index.asfreq(df.index.freq.rule_code).freq _check_plot_works(df.plot, freq) From 5236468e70dc375ea77fdb75696eb905ad4a5f94 Mon Sep 17 00:00:00 2001 From: JoElfner Date: Fri, 17 May 2019 10:46:13 +0200 Subject: [PATCH 07/13] tests modified to be keep it self-contained --- pandas/tests/plotting/test_datetimelike.py | 37 ++++++++++++++-------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index 204e20f4d4aaa..d5870942f1ffb 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -28,19 +28,13 @@ 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']) for x in idx] - mults = [1, 23] # setup periods with multiples of freq.rule_code - freq = [str(mlt) + frq for frq, mlt in itertools.product(freq, mults)] - idx = [period_range('12/31/1999', freq=x, periods=100) for x in freq] - self.period_mlt_ser = [Series(np.random.randn(len(x)), x) for x in idx] - self.period_mlt_df = [DataFrame(np.random.randn(len(x), 3), index=x, - columns=['A', 'B', 'C']) - for x in idx] freq = ['S', 'T', 'H', 'D', 'W', 'M', 'Q-DEC', 'A', '1B30Min'] idx = [date_range('12/31/1999', freq=x, periods=100) for x in freq] @@ -220,8 +214,15 @@ def test_line_plot_period_series(self): @pytest.mark.slow def test_line_plot_period_mlt_series(self): - for s in self.period_mlt_ser: - _check_plot_works(s.plot, s.index.freq.rule_code) + # test period index line plot for series with multiples (`mlt`) of the + # frequency rule code + multiplier = [1, 9] # multiplier for freq.rule_code + for mult in multiplier: + for base in self.freq: + freq_code = str(mult) + base # construct frequency code + idx = period_range('12/31/1999', freq=freq_code, 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): @@ -235,9 +236,17 @@ def test_line_plot_period_frame(self): @pytest.mark.slow def test_line_plot_period_mlt_frame(self): - for df in self.period_mlt_df: - freq = df.index.asfreq(df.index.freq.rule_code).freq - _check_plot_works(df.plot, freq) + # test period index line plot for DataFrames with multiples (`mlt`) + # of the frequency rule code + multiplier = [1, 9] # multiplier for freq.rule_code + for mult in multiplier: + for base in self.freq: + freq_code = str(mult) + base # construct frequency code + idx = period_range('12/31/1999', freq=freq_code, 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): From 8466a85f5574c3f4aa680fccb22bb4cde9d5d55b Mon Sep 17 00:00:00 2001 From: JoElfner Date: Fri, 17 May 2019 10:47:59 +0200 Subject: [PATCH 08/13] unused itertools import removed --- pandas/tests/plotting/test_datetimelike.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index d5870942f1ffb..ef72e527ca23a 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -1,6 +1,5 @@ """ Test cases for time series specific (freq conversion, etc) """ from datetime import date, datetime, time, timedelta -import itertools import pickle import sys From b9457b65596d7a8dd56f5449d8a910082eccf60c Mon Sep 17 00:00:00 2001 From: JoElfner Date: Fri, 17 May 2019 11:06:12 +0200 Subject: [PATCH 09/13] release note added in 0.25.0.rst --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index b2a379d9fe6f5..9cea4070a65ca 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -374,7 +374,7 @@ 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`) -- +- Fixed bug in :func:`pandas.plotting._timeseries._maybe_convert_index` causing plots of PeriodIndex timeseries to fail if the frequency is a multiple of the frequency rule code (:issue:`14763`) - - From 5182611e5923e9528bea46a5d1a08cc1260d99a1 Mon Sep 17 00:00:00 2001 From: JoElfner Date: Fri, 17 May 2019 11:20:54 +0200 Subject: [PATCH 10/13] release note conflicts resolved --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 9cea4070a65ca..73ae56470ddea 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -374,8 +374,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`) -- Fixed bug in :func:`pandas.plotting._timeseries._maybe_convert_index` causing plots of PeriodIndex timeseries to fail if the frequency is a multiple of the frequency rule code (:issue:`14763`) - +- Fixed bug in :func:`pandas.plotting._timeseries._maybe_convert_index` causing plots of PeriodIndex timeseries to fail if the frequency is a multiple of the frequency rule code (:issue:`14763`) - Groupby/Resample/Rolling From 6f31b73218bb6341282061a89ec9af0fc8b7b43f Mon Sep 17 00:00:00 2001 From: JoElfner Date: Fri, 17 May 2019 19:14:15 +0200 Subject: [PATCH 11/13] release note conflicts resolved --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index b8f7552da1a23..1ac198578c5cb 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -423,7 +423,7 @@ 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 in :func:`pandas.plotting._timeseries._maybe_convert_index` causing plots of PeriodIndex timeseries to fail if the frequency is a multiple of the frequency rule code (:issue:`14763`) +- Fixed bug causing plots of PeriodIndex timeseries to fail if the frequency is a multiple of the frequency rule code (:issue:`14763`) - - - From bd09ff4b6d8133e3bccdfc3505513defd89e875f Mon Sep 17 00:00:00 2001 From: JoElfner Date: Mon, 27 May 2019 12:49:16 +0200 Subject: [PATCH 12/13] tests parametrized --- pandas/tests/plotting/test_datetimelike.py | 37 ++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index 1aecaec0ad454..6a8ef608717df 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -211,16 +211,14 @@ def test_line_plot_period_series(self): _check_plot_works(s.plot, s.index.freq) @pytest.mark.slow - def test_line_plot_period_mlt_series(self): + @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 rule code. tests resolution of issue #14763 - multiplier = [1, 9] # multiplier for freq.rule_code - for mult in multiplier: - for base in self.freq: - freq_code = str(mult) + base # construct frequency code - idx = period_range('12/31/1999', freq=freq_code, periods=100) - s = Series(np.random.randn(len(idx)), idx) - _check_plot_works(s.plot, s.index.freq.rule_code) + # 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): @@ -233,18 +231,17 @@ def test_line_plot_period_frame(self): _check_plot_works(df.plot, df.index.freq) @pytest.mark.slow - def test_line_plot_period_mlt_frame(self): + @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 rule code. tests resolution of issue #14763 - multiplier = [1, 9] # multiplier for freq.rule_code - for mult in multiplier: - for base in self.freq: - freq_code = str(mult) + base # construct frequency code - idx = period_range('12/31/1999', freq=freq_code, 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) + # 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): From 0370300385b2d6fe55ecb18e7b12225b61962981 Mon Sep 17 00:00:00 2001 From: JoElfner Date: Mon, 27 May 2019 12:53:30 +0200 Subject: [PATCH 13/13] tests parametrized --- pandas/tests/plotting/test_datetimelike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index 6a8ef608717df..db5316eb3c15d 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -212,7 +212,7 @@ def test_line_plot_period_series(self): @pytest.mark.slow @pytest.mark.parametrize( - 'frqncy', ['1S', '3S', '5T', '7H', '4D', '8W', '11M', '3A']) + '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