From 9e8c5feecacc850c0ed3f0eebfd9810df45e9c4b Mon Sep 17 00:00:00 2001 From: Thomas Heavey Date: Wed, 28 Oct 2020 08:59:16 -0400 Subject: [PATCH 1/8] add test to demonstrate converter issue #37454 --- pandas/tests/plotting/test_converter.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pandas/tests/plotting/test_converter.py b/pandas/tests/plotting/test_converter.py index b2eeb649276d5..af7c4566d86c5 100644 --- a/pandas/tests/plotting/test_converter.py +++ b/pandas/tests/plotting/test_converter.py @@ -361,3 +361,16 @@ def test_format_timedelta_ticks(self, x, decimal, format_expected): tdc = converter.TimeSeries_TimedeltaFormatter result = tdc.format_timedelta_ticks(x, pos=None, n_decimals=decimal) assert result == format_expected + + @pytest.mark.parametrize("view_interval", [(1, 2), (2, 1)]) + def test_call(self, view_interval): + from unittest import mock + + tdc = converter.TimeSeries_TimedeltaFormatter() + mock_axis = mock.Mock() + mock_axis.get_view_interval.return_value = view_interval + with mock.patch( + "pandas.plotting._matplotlib.converter.TimeSeries_TimedeltaFormatter.axis", + mock_axis, + ): + tdc(0.0, 0) From 9677e5e1381dfe9171b3172056f0f86c946087b6 Mon Sep 17 00:00:00 2001 From: Thomas Heavey Date: Wed, 28 Oct 2020 08:59:39 -0400 Subject: [PATCH 2/8] add fix for issue #37454 --- pandas/plotting/_matplotlib/converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/plotting/_matplotlib/converter.py b/pandas/plotting/_matplotlib/converter.py index 27c7b931b7136..38789fffed8a0 100644 --- a/pandas/plotting/_matplotlib/converter.py +++ b/pandas/plotting/_matplotlib/converter.py @@ -1072,7 +1072,7 @@ def format_timedelta_ticks(x, pos, n_decimals: int) -> str: def __call__(self, x, pos=0) -> str: (vmin, vmax) = tuple(self.axis.get_view_interval()) - n_decimals = int(np.ceil(np.log10(100 * 1e9 / (vmax - vmin)))) + n_decimals = int(np.ceil(np.log10(100 * 1e9 / abs(vmax - vmin)))) if n_decimals > 9: n_decimals = 9 return self.format_timedelta_ticks(x, pos, n_decimals) From 78f8ae1497411f9e30d18f2e085ace035d2f9448 Mon Sep 17 00:00:00 2001 From: Thomas Heavey Date: Wed, 28 Oct 2020 10:22:57 -0400 Subject: [PATCH 3/8] TST use monkeypatch instead of mock --- pandas/tests/plotting/test_converter.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pandas/tests/plotting/test_converter.py b/pandas/tests/plotting/test_converter.py index af7c4566d86c5..79c451eb3b294 100644 --- a/pandas/tests/plotting/test_converter.py +++ b/pandas/tests/plotting/test_converter.py @@ -363,14 +363,11 @@ def test_format_timedelta_ticks(self, x, decimal, format_expected): assert result == format_expected @pytest.mark.parametrize("view_interval", [(1, 2), (2, 1)]) - def test_call(self, view_interval): - from unittest import mock + def test_call(self, view_interval, monkeypatch): + class mock_axis: + def get_view_interval(self): + return view_interval tdc = converter.TimeSeries_TimedeltaFormatter() - mock_axis = mock.Mock() - mock_axis.get_view_interval.return_value = view_interval - with mock.patch( - "pandas.plotting._matplotlib.converter.TimeSeries_TimedeltaFormatter.axis", - mock_axis, - ): - tdc(0.0, 0) + monkeypatch.setattr(tdc, 'axis', mock_axis()) + tdc(0.0, 0) From 531f1da956d3c56fd8c9c934736c0f33e013c992 Mon Sep 17 00:00:00 2001 From: Thomas Heavey Date: Wed, 28 Oct 2020 10:37:00 -0400 Subject: [PATCH 4/8] DOC add to whatsnew --- doc/source/whatsnew/v1.1.4.rst | 1 + pandas/tests/plotting/test_converter.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.4.rst b/doc/source/whatsnew/v1.1.4.rst index 6cb728800dc68..00a422816f43b 100644 --- a/doc/source/whatsnew/v1.1.4.rst +++ b/doc/source/whatsnew/v1.1.4.rst @@ -41,6 +41,7 @@ Bug fixes - Bug in :meth:`GroupBy.fillna` that introduced a performance regression after 1.0.5 (:issue:`36757`) - Bug in :meth:`DataFrame.info` was raising a ``KeyError`` when the DataFrame has integer column names (:issue:`37245`) - Bug in :meth:`DataFrameGroupby.apply` would drop a :class:`CategoricalIndex` when grouped on (:issue:`35792`) +- Bug in :meth:`Series.plot` that would break if ``xmin > xmax`` when using a fixed frequency :class:`TimedeltaIndex` (:issue:`37454`) .. --------------------------------------------------------------------------- diff --git a/pandas/tests/plotting/test_converter.py b/pandas/tests/plotting/test_converter.py index 79c451eb3b294..a1c8c08e47f3d 100644 --- a/pandas/tests/plotting/test_converter.py +++ b/pandas/tests/plotting/test_converter.py @@ -369,5 +369,5 @@ def get_view_interval(self): return view_interval tdc = converter.TimeSeries_TimedeltaFormatter() - monkeypatch.setattr(tdc, 'axis', mock_axis()) + monkeypatch.setattr(tdc, "axis", mock_axis()) tdc(0.0, 0) From 79bf3b1f2596ccbe8e6d2134666a32574cfca807 Mon Sep 17 00:00:00 2001 From: Thomas Heavey Date: Thu, 29 Oct 2020 09:08:23 -0400 Subject: [PATCH 5/8] DOC update whatsnew entry for clarity and detail --- doc/source/whatsnew/v1.1.4.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.4.rst b/doc/source/whatsnew/v1.1.4.rst index 00a422816f43b..7966eb2616481 100644 --- a/doc/source/whatsnew/v1.1.4.rst +++ b/doc/source/whatsnew/v1.1.4.rst @@ -41,7 +41,8 @@ Bug fixes - Bug in :meth:`GroupBy.fillna` that introduced a performance regression after 1.0.5 (:issue:`36757`) - Bug in :meth:`DataFrame.info` was raising a ``KeyError`` when the DataFrame has integer column names (:issue:`37245`) - Bug in :meth:`DataFrameGroupby.apply` would drop a :class:`CategoricalIndex` when grouped on (:issue:`35792`) -- Bug in :meth:`Series.plot` that would break if ``xmin > xmax`` when using a fixed frequency :class:`TimedeltaIndex` (:issue:`37454`) +- Bug in :meth:`Series.plot` and :meth:`DataFrame.plot` was throwing :exc:`ValueError` with a :class:`Series` or :class:`DataFrame` + indexed by a :class:`TimedeltaIndex` with a fixed frequency when x-axis lower limit was greater than upper limit (:issue:`37454`) .. --------------------------------------------------------------------------- From 7809661b9d62b7137227a3a3cf484c0c05e95e87 Mon Sep 17 00:00:00 2001 From: Thomas Heavey Date: Mon, 2 Nov 2020 08:08:51 -0500 Subject: [PATCH 6/8] TST: add higher-level test for reversed timedelta index plot --- pandas/tests/plotting/test_series.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pandas/tests/plotting/test_series.py b/pandas/tests/plotting/test_series.py index 777bc914069a6..0c9a7d3c51349 100644 --- a/pandas/tests/plotting/test_series.py +++ b/pandas/tests/plotting/test_series.py @@ -983,3 +983,16 @@ def test_xlabel_ylabel_series(self, kind, index_name, old_label, new_label): ax = ser.plot(kind=kind, ylabel=new_label, xlabel=new_label) assert ax.get_ylabel() == new_label assert ax.get_xlabel() == new_label + + @pytest.mark.parametrize( + "index", + [ + pd.timedelta_range(start=0, periods=2, freq="D"), + [pd.Timedelta(days=1), pd.Timedelta(days=2)], + ], + ) + def test_timedelta_index(self, index): + # GH37454 + xlims = (3, 1) + ax = pd.Series([1, 2], index=index).plot(xlim=(xlims)) + assert ax.get_xlim() == (3, 1) From 001873504d481817508ab36a7a93ffc76d5f6894 Mon Sep 17 00:00:00 2001 From: Thomas Heavey Date: Mon, 2 Nov 2020 08:15:31 -0500 Subject: [PATCH 7/8] DOC: move whatnew entry to newer version (missed cutoff) --- doc/source/whatsnew/v1.1.4.rst | 2 -- doc/source/whatsnew/v1.2.0.rst | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.1.4.rst b/doc/source/whatsnew/v1.1.4.rst index 028852a57530b..6353dbfafc9f1 100644 --- a/doc/source/whatsnew/v1.1.4.rst +++ b/doc/source/whatsnew/v1.1.4.rst @@ -44,8 +44,6 @@ Bug fixes - Bug in :meth:`GroupBy.fillna` that introduced a performance regression after 1.0.5 (:issue:`36757`) - Bug in :meth:`DataFrame.info` was raising a ``KeyError`` when the DataFrame has integer column names (:issue:`37245`) - Bug in :meth:`DataFrameGroupby.apply` would drop a :class:`CategoricalIndex` when grouped on (:issue:`35792`) -- Bug in :meth:`Series.plot` and :meth:`DataFrame.plot` was throwing :exc:`ValueError` with a :class:`Series` or :class:`DataFrame` - indexed by a :class:`TimedeltaIndex` with a fixed frequency when x-axis lower limit was greater than upper limit (:issue:`37454`) .. --------------------------------------------------------------------------- diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 6f9e8d6a98d80..84d7d05a3de7b 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -497,6 +497,8 @@ Plotting - Bug in :meth:`DataFrame.plot` was rotating xticklabels when ``subplots=True``, even if the x-axis wasn't an irregular time series (:issue:`29460`) - Bug in :meth:`DataFrame.plot` where a marker letter in the ``style`` keyword sometimes causes a ``ValueError`` (:issue:`21003`) - Twinned axes were losing their tick labels which should only happen to all but the last row or column of 'externally' shared axes (:issue:`33819`) +- Bug in :meth:`Series.plot` and :meth:`DataFrame.plot` was throwing :exc:`ValueError` with a :class:`Series` or :class:`DataFrame` + indexed by a :class:`TimedeltaIndex` with a fixed frequency when x-axis lower limit was greater than upper limit (:issue:`37454`) Groupby/resample/rolling ^^^^^^^^^^^^^^^^^^^^^^^^ From a4da1fff76304cc15cc862e40599aa6b9f7e45d2 Mon Sep 17 00:00:00 2001 From: Thomas Heavey Date: Mon, 2 Nov 2020 09:05:56 -0500 Subject: [PATCH 8/8] TST: comment on test; fix style --- pandas/tests/plotting/test_converter.py | 3 ++- pandas/tests/plotting/test_series.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/tests/plotting/test_converter.py b/pandas/tests/plotting/test_converter.py index a1c8c08e47f3d..6696c9c02bcec 100644 --- a/pandas/tests/plotting/test_converter.py +++ b/pandas/tests/plotting/test_converter.py @@ -363,7 +363,8 @@ def test_format_timedelta_ticks(self, x, decimal, format_expected): assert result == format_expected @pytest.mark.parametrize("view_interval", [(1, 2), (2, 1)]) - def test_call(self, view_interval, monkeypatch): + def test_call_w_different_view_intervals(self, view_interval, monkeypatch): + # previously broke on reversed xlmits; see GH37454 class mock_axis: def get_view_interval(self): return view_interval diff --git a/pandas/tests/plotting/test_series.py b/pandas/tests/plotting/test_series.py index 0c9a7d3c51349..f3289d0573de2 100644 --- a/pandas/tests/plotting/test_series.py +++ b/pandas/tests/plotting/test_series.py @@ -994,5 +994,5 @@ def test_xlabel_ylabel_series(self, kind, index_name, old_label, new_label): def test_timedelta_index(self, index): # GH37454 xlims = (3, 1) - ax = pd.Series([1, 2], index=index).plot(xlim=(xlims)) + ax = Series([1, 2], index=index).plot(xlim=(xlims)) assert ax.get_xlim() == (3, 1)