From ddd2f84e3da554235ddcbeb79320589cb1d9fb52 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Tue, 18 Jan 2022 17:06:33 +0100 Subject: [PATCH 1/4] restore array_wrap --- pandas/core/generic.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 9b713e626ad24..92080324f6746 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -2068,6 +2068,40 @@ def empty(self) -> bool_t: def __array__(self, dtype: npt.DTypeLike | None = None) -> np.ndarray: return np.asarray(self._values, dtype=dtype) + def __array_wrap__( + self, + result: np.ndarray, + context: tuple[Callable, tuple[Any, ...], int] | None = None, + ): + """ + Gets called after a ufunc and other functions. + + Parameters + ---------- + result: np.ndarray + The result of the ufunc or other function called on the NumPy array + returned by __array__ + context: tuple of (func, tuple, int) + This parameter is returned by ufuncs as a 3-element tuple: (name of the + ufunc, arguments of the ufunc, domain of the ufunc), but is not set by + other numpy functions.q + + Notes + ----- + Series implements __array_ufunc_ so this not called for ufunc on Series. + """ + res = lib.item_from_zerodim(result) + if is_scalar(res): + # e.g. we get here with np.ptp(series) + # ptp also requires the item_from_zerodim + return res + d = self._construct_axes_dict(self._AXIS_ORDERS, copy=False) + # error: Argument 1 to "NDFrame" has incompatible type "ndarray"; + # expected "BlockManager" + return self._constructor(res, **d).__finalize__( # type: ignore[arg-type] + self, method="__array_wrap__" + ) + @final def __array_ufunc__( self, ufunc: np.ufunc, method: str, *inputs: Any, **kwargs: Any From f52f04139e2398e4f3c0423d639292689a75db7c Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Tue, 18 Jan 2022 17:12:32 +0100 Subject: [PATCH 2/4] add comment / test --- pandas/core/generic.py | 1 + pandas/tests/base/test_misc.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 92080324f6746..554c20bb93e8e 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -2090,6 +2090,7 @@ def __array_wrap__( ----- Series implements __array_ufunc_ so this not called for ufunc on Series. """ + # Note: at time of dask 2022.01.0, this is still used by dask res = lib.item_from_zerodim(result) if is_scalar(res): # e.g. we get here with np.ptp(series) diff --git a/pandas/tests/base/test_misc.py b/pandas/tests/base/test_misc.py index dca36f6ba89fb..7964a220679a6 100644 --- a/pandas/tests/base/test_misc.py +++ b/pandas/tests/base/test_misc.py @@ -84,6 +84,16 @@ def test_ndarray_compat_properties(index_or_series_obj): assert Series([1]).item() == 1 +def test_array_wrap_compat(): + # Note: at time of dask 2022.01.0, this is still used by eg dask + # (https://github.com/dask/dask/issues/8580). + # This test is a small dummy ensuring coverage + orig = Series([1, 2, 3], index=["a", "b", "c"]) + result = orig.__array_wrap__(np.array([2, 4, 6])) + expected = orig * 2 + tm.assert_series_equal(result, expected) + + @pytest.mark.skipif(PYPY, reason="not relevant for PyPy") def test_memory_usage(index_or_series_obj): obj = index_or_series_obj From c4bc2eba8821fcec356bd5566ab2afbb4a094796 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Wed, 19 Jan 2022 09:08:28 +0100 Subject: [PATCH 3/4] add dask downstream test --- pandas/tests/test_downstream.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pandas/tests/test_downstream.py b/pandas/tests/test_downstream.py index cf5ba9a9fc112..0ec5745fcea92 100644 --- a/pandas/tests/test_downstream.py +++ b/pandas/tests/test_downstream.py @@ -55,6 +55,30 @@ def test_dask(df): pd.set_option("compute.use_numexpr", olduse) +@pytest.mark.filterwarnings("ignore:.*64Index is deprecated:FutureWarning") +def test_dask_ufunc(): + # At the time of dask 2022.01.0, dask is still directly using __array_wrap__ + # for some ufuncs (https://github.com/dask/dask/issues/8580). + + # dask sets "compute.use_numexpr" to False, so catch the current value + # and ensure to reset it afterwards to avoid impacting other tests + olduse = pd.get_option("compute.use_numexpr") + + try: + dask = import_module("dask") # noqa:F841 + import dask.array as da + import dask.dataframe as dd + + s = pd.Series([1.5, 2.3, 3.7, 4.0]) + ds = dd.from_pandas(s, npartitions=2) + + result = da.fix(ds).compute() + expected = np.fix(s) + tm.assert_series_equal(result, expected) + finally: + pd.set_option("compute.use_numexpr", olduse) + + def test_xarray(df): xarray = import_module("xarray") # noqa:F841 From f90d8492b818ed669718ed694c9234673c5521d6 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Wed, 19 Jan 2022 16:03:51 +0100 Subject: [PATCH 4/4] fix dtype in test --- pandas/tests/base/test_misc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/base/test_misc.py b/pandas/tests/base/test_misc.py index 7964a220679a6..153981f459ab2 100644 --- a/pandas/tests/base/test_misc.py +++ b/pandas/tests/base/test_misc.py @@ -88,8 +88,8 @@ def test_array_wrap_compat(): # Note: at time of dask 2022.01.0, this is still used by eg dask # (https://github.com/dask/dask/issues/8580). # This test is a small dummy ensuring coverage - orig = Series([1, 2, 3], index=["a", "b", "c"]) - result = orig.__array_wrap__(np.array([2, 4, 6])) + orig = Series([1, 2, 3], dtype="int64", index=["a", "b", "c"]) + result = orig.__array_wrap__(np.array([2, 4, 6], dtype="int64")) expected = orig * 2 tm.assert_series_equal(result, expected)