From 223f59f585b96fcea2ddb70166ce73ca76b9325b Mon Sep 17 00:00:00 2001 From: Thomas Kluiters Date: Tue, 2 Apr 2019 22:36:10 +0200 Subject: [PATCH 1/4] BUG: Fix apply on series backed by datetime aware values (#25959) --- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/core/series.py | 6 ++++-- pandas/tests/series/test_apply.py | 13 +++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 1ef05ae5f9c6b..35d4ae6f8070a 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -387,6 +387,7 @@ Reshaping - Bug in :func:`concat` where order of ``OrderedDict`` (and ``dict`` in Python 3.6+) is not respected, when passed in as ``objs`` argument (:issue:`21510`) - Bug in :func:`concat` where the resulting ``freq`` of two :class:`DatetimeIndex` with the same ``freq`` would be dropped (:issue:`3232`). - Bug in :func:`merge` where merging with equivalent Categorical dtypes was raising an error (:issue:`22501`) +- Bug in :func:`Series.apply` failed when the series is a timezone aware :class:`DatetimeIndex` (:issue:`25959`) Sparse ^^^^^^ diff --git a/pandas/core/series.py b/pandas/core/series.py index 1a7d9ff70be3a..f579a918750ff 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -7,7 +7,7 @@ import warnings import numpy as np - +import pandas as pd from pandas._config import get_option from pandas._libs import iNaT, index as libindex, lib, tslibs @@ -3687,7 +3687,9 @@ def f(x): if len(mapped) and isinstance(mapped[0], Series): from pandas.core.frame import DataFrame - return DataFrame(mapped.tolist(), index=self.index) + # GH 25959 use pd.array instead of tolist + # so extension arrays can be used + return DataFrame(pd.array(mapped), index=self.index) else: return self._constructor(mapped, index=self.index).__finalize__(self) diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index 162a27db34cb1..e70d594d1e370 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -677,3 +677,16 @@ def test_map_missing_mixed(self, vals, mapping, exp): result = s.map(mapping) tm.assert_series_equal(result, pd.Series(exp)) + + @pytest.mark.parametrize("dti,exp", [ + (Series([1, 2], index=pd.DatetimeIndex([0, 31536000000])), + DataFrame(np.repeat([[1, 2]], 2, axis=0), dtype='int64')), + (tm.makeTimeSeries(nper=30), + DataFrame(np.repeat([[1, 2]], 30, axis=0), dtype='int64')) + ]) + def test_apply_on_date_time_index_aware_series(self, dti, exp): + # GH 25959 + # Calling apply on a localized time series should not cause an error + index = dti.tz_localize('UTC').index + result = pd.Series(index).apply(lambda x: pd.Series([1, 2])) + assert_frame_equal(result, exp) From 339b155cfab6f01e9f4d65ea88b2b649eb010d70 Mon Sep 17 00:00:00 2001 From: Thomas Kluiters Date: Tue, 7 May 2019 14:17:01 +0200 Subject: [PATCH 2/4] Fix isort issue --- pandas/core/series.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 1d067e710e85a..797c4025b32c8 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -8,7 +8,7 @@ import warnings import numpy as np -import pandas as pd + from pandas._config import get_option from pandas._libs import iNaT, index as libindex, lib, tslibs @@ -28,6 +28,7 @@ from pandas.core.dtypes.missing import ( isna, na_value_for_dtype, notna, remove_na_arraylike) +import pandas as pd from pandas.core import algorithms, base, generic, nanops, ops from pandas.core.accessor import CachedAccessor from pandas.core.arrays import ExtensionArray, SparseArray @@ -3679,10 +3680,9 @@ def f(x): mapped = lib.map_infer(values, f, convert=convert_dtype) if len(mapped) and isinstance(mapped[0], Series): - from pandas.core.frame import DataFrame # GH 25959 use pd.array instead of tolist # so extension arrays can be used - return DataFrame(pd.array(mapped), index=self.index) + return pd.DataFrame(pd.array(mapped), index=self.index) else: return self._constructor(mapped, index=self.index).__finalize__(self) From 4ad0baee8ca7f75bab681880d8f60dd6d7fc3f5f Mon Sep 17 00:00:00 2001 From: Thomas Kluiters Date: Sat, 18 May 2019 08:02:51 +0200 Subject: [PATCH 3/4] Use constructor_expanddim instead of DataFrame --- pandas/core/series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index a871bf562b281..f5f00f1f065b8 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3705,7 +3705,7 @@ def f(x): if len(mapped) and isinstance(mapped[0], Series): # GH 25959 use pd.array instead of tolist # so extension arrays can be used - return pd.DataFrame(pd.array(mapped), index=self.index) + return self._constructor_expanddim(pd.array(mapped), index=self.index) else: return self._constructor(mapped, index=self.index).__finalize__(self) From 36429a04c240577f17fc8eefdaa1fa50a19125c3 Mon Sep 17 00:00:00 2001 From: Thomas Kluiters Date: Sat, 18 May 2019 21:08:50 +0200 Subject: [PATCH 4/4] Add regression test and fix PEP --- pandas/core/series.py | 3 ++- pandas/tests/series/test_apply.py | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index f5f00f1f065b8..f0a21637699f2 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3705,7 +3705,8 @@ def f(x): if len(mapped) and isinstance(mapped[0], Series): # GH 25959 use pd.array instead of tolist # so extension arrays can be used - return self._constructor_expanddim(pd.array(mapped), index=self.index) + return self._constructor_expanddim(pd.array(mapped), + index=self.index) else: return self._constructor(mapped, index=self.index).__finalize__(self) diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index fdf92eb614ac8..4551453499455 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -678,9 +678,16 @@ def test_map_missing_mixed(self, vals, mapping, exp): (tm.makeTimeSeries(nper=30), DataFrame(np.repeat([[1, 2]], 30, axis=0), dtype='int64')) ]) - def test_apply_on_date_time_index_aware_series(self, dti, exp): + def test_apply_series_on_date_time_index_aware_series(self, dti, exp): # GH 25959 # Calling apply on a localized time series should not cause an error index = dti.tz_localize('UTC').index result = pd.Series(index).apply(lambda x: pd.Series([1, 2])) assert_frame_equal(result, exp) + + def test_apply_scaler_on_date_time_index_aware_series(self): + # GH 25959 + # Calling apply on a localized time series should not cause an error + series = tm.makeTimeSeries(nper=30).tz_localize('UTC') + result = pd.Series(series.index).apply(lambda x: 1) + assert_series_equal(result, pd.Series(np.ones(30), dtype='int64'))