diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 6bf6fd65f5633..0f361ca9a74a9 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -862,6 +862,7 @@ Groupby/resample/rolling - Bug in :meth:`Rolling.skew` and :meth:`Rolling.kurt` would give NaN with window of same values (:issue:`30993`) - Bug in :meth:`.Rolling.var` would segfault calculating weighted variance when window size was larger than data size (:issue:`46760`) - Bug in :meth:`Grouper.__repr__` where ``dropna`` was not included. Now it is (:issue:`46754`) +- Bug in :meth:`Rolling.aggregate` raise ValueError when ``func`` was a list of functions and ``axis=1`` (:issue:`46132`) - Bug in :meth:`DataFrame.rolling` gives ValueError when center=True, axis=1 and win_type is specified (:issue:`46135`) - Bug in :meth:`.DataFrameGroupBy.describe` and :meth:`.SeriesGroupBy.describe` produces inconsistent results for empty datasets (:issue:`41575`) - Bug in :meth:`DataFrame.resample` reduction methods when used with ``on`` would attempt to aggregate the provided column (:issue:`47079`) diff --git a/pandas/core/window/rolling.py b/pandas/core/window/rolling.py index 9e8f95cf340c4..42343ad0c42c0 100644 --- a/pandas/core/window/rolling.py +++ b/pandas/core/window/rolling.py @@ -656,9 +656,28 @@ def _numba_apply( return self._resolve_output(out, obj) def aggregate(self, func, *args, **kwargs): - result = ResamplerWindowApply(self, func, args=args, kwargs=kwargs).agg() + # GH46132 + # modifying axis and transposing dataframe should not be needed + # once ReamplerWindow supports axis = 1 + + obj = self.obj + axis = self.axis + + self.obj = self.obj.T if self.axis == 1 else self.obj + self.axis = 0 + + try: + result = ResamplerWindowApply(self, func, args=args, kwargs=kwargs).agg() + finally: + self.obj = obj + self.axis = axis + + if axis == 1: + result = result.T if result is not None else result + if result is None: return self.apply(func, raw=False, args=args, kwargs=kwargs) + return result agg = aggregate diff --git a/pandas/tests/window/test_rolling.py b/pandas/tests/window/test_rolling.py index 4c26cfb95fd85..35bd4853df515 100644 --- a/pandas/tests/window/test_rolling.py +++ b/pandas/tests/window/test_rolling.py @@ -1851,6 +1851,22 @@ def test_rolling_var_same_value_count_logic(values, window, min_periods, expecte tm.assert_series_equal(expected == 0, result_std == 0) +def test_rolling_agg_on_columns(): + # GH 46132 + + df = DataFrame({"a": [1, 3], "b": [2, 4]}) + res = df.rolling(window=2, axis=1, min_periods=1).aggregate([np.sum, np.mean]) + expected_val = np.array([[1, 3.0], [1, 1.5], [3, 7], [3, 3.5]]) + + expected_index = MultiIndex.from_tuples( + [(0, "sum"), (0, "mean"), (1, "sum"), (1, "mean")] + ) + + expected_frame = DataFrame(expected_val, index=expected_index, columns=["a", "b"]) + + tm.assert_frame_equal(expected_frame, res) + + def test_rolling_mean_sum_floating_artifacts(): # GH 42064. @@ -1862,6 +1878,24 @@ def test_rolling_mean_sum_floating_artifacts(): assert (result[-3:] == 0).all() +def test_rolling_agg_when_agg_fail(): + # GH 46132 + df = DataFrame({"a": [1, 3], "b": [2, 4]}) + win = df.rolling(window=2, axis=1, min_periods=1) + try: + win.aggregate([np.log, np.sqrt]) + except ValueError: + pass + tm.assert_frame_equal(win.obj, df) + # make sure if aggregate fails the attribute of Rolling/Window will not be changed + assert win.axis == 1 + # make sure the attribute of Rolling/Window will not be changed + # when aggregate runs successfully + win.aggregate([np.mean, np.sum]) + tm.assert_frame_equal(win.obj, df) + assert win.axis == 1 + + def test_rolling_skew_kurt_floating_artifacts(): # GH 42064 46431