diff --git a/doc/source/reference/groupby.rst b/doc/source/reference/groupby.rst index fc180c8161a7e..ce9aeeb358c19 100644 --- a/doc/source/reference/groupby.rst +++ b/doc/source/reference/groupby.rst @@ -79,6 +79,7 @@ Function application DataFrameGroupBy.cumsum DataFrameGroupBy.describe DataFrameGroupBy.diff + DataFrameGroupBy.ewm DataFrameGroupBy.ffill DataFrameGroupBy.first DataFrameGroupBy.head @@ -130,6 +131,7 @@ Function application SeriesGroupBy.cumsum SeriesGroupBy.describe SeriesGroupBy.diff + SeriesGroupBy.ewm SeriesGroupBy.ffill SeriesGroupBy.first SeriesGroupBy.head diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index 9cfeb53821fbc..12fc6896bdb8f 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -152,7 +152,6 @@ class providing the base-class of operations. from pandas.core.indexers.objects import BaseIndexer from pandas.core.resample import Resampler from pandas.core.window import ( - ExpandingGroupby, ExponentialMovingWindowGroupby, RollingGroupby, ) @@ -3805,35 +3804,93 @@ def rolling( @final @Substitution(name="groupby") @Appender(_common_see_also) - def expanding(self, *args, **kwargs) -> ExpandingGroupby: + def ewm(self, *args, **kwargs) -> ExponentialMovingWindowGroupby: """ - Return an expanding grouper, providing expanding - functionality per group. + Provide exponential weighted functions for the groupby. + + Parameters + ---------- + *args + Arguments to be passed to + :meth:`~pandas.core.window.ExponentialMovingWindow`. + **kwargs + Keyword arguments to be passed to + :meth:`~pandas.core.window.ExponentialMovingWindow`. + These can include: + - com : float, optional + - span : float, optional + - halflife : float, optional + - alpha : float, optional + - min_periods : int, default 0 + - adjust : bool, default True + - ignore_na : bool, default False + - axis : {0 or 'index', 1 or 'columns'}, default 0 Returns ------- - pandas.api.typing.ExpandingGroupby - """ - from pandas.core.window import ExpandingGroupby + pandas.api.typing.ExponentialMovingWindowGroupby + Return a new grouper with exponential moving window capabilities. - return ExpandingGroupby( - self._selected_obj, - *args, - _grouper=self._grouper, - **kwargs, - ) + Notes + ----- + Each group is treated independently, and the exponential weighted calculations + are applied separately to each group. - @final - @Substitution(name="groupby") - @Appender(_common_see_also) - def ewm(self, *args, **kwargs) -> ExponentialMovingWindowGroupby: - """ - Return an ewm grouper, providing ewm functionality per group. + When ``adjust=True``, weighted averages are calculated using weights + :math:`w_i = (1-\\alpha)^i` where :math:`i` is the number of periods from the + observations being weighted to the current period. - Returns - ------- - pandas.api.typing.ExponentialMovingWindowGroupby + When ``adjust=False``, the calculation follows the recursive formula: + :math:`y_t = (1 - \\alpha) y_{t-1} + \\alpha x_t`. + + Examples + -------- + >>> df = pd.DataFrame( + ... { + ... "Class": ["A", "A", "A", "B", "B", "B"], + ... "Value": [10, 20, 30, 40, 50, 60], + ... } + ... ) + >>> df + Class Value + 0 A 10 + 1 A 20 + 2 A 30 + 3 B 40 + 4 B 50 + 5 B 60 + + >>> df.groupby("Class").ewm(span=2).mean() + Value + Class + A 0 10.000000 + 1 17.500000 + 2 26.153846 + B 3 40.000000 + 4 47.500000 + 5 56.153846 + + >>> df.groupby("Class").ewm(alpha=0.5, adjust=False).mean() + Value + Class + A 0 10.0 + 1 15.0 + 2 22.5 + B 3 40.0 + 4 45.0 + 5 52.5 + + >>> df.groupby("Class").ewm(com=1.0, min_periods=1).std() + Value + Class + A 0 NaN + 1 7.071068 + 2 9.636241 + B 3 NaN + 4 7.071068 + 5 9.636241 """ + from pandas.core.window import ExponentialMovingWindowGroupby return ExponentialMovingWindowGroupby( diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index f93105498ac79..b599be5d042fe 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -168,7 +168,7 @@ def test_query_duplicate_column_name(self, engine, parser): } ).rename(columns={"B": "A"}) - res = df.query('C == 1', engine=engine, parser=parser) + res = df.query("C == 1", engine=engine, parser=parser) expect = DataFrame( [[1, 1, 1]], @@ -1411,7 +1411,7 @@ def test_expr_with_column_name_with_backtick_and_hash(self): def test_expr_with_column_name_with_backtick(self): # GH 59285 df = DataFrame({"a`b": (1, 2, 3), "ab": (4, 5, 6)}) - result = df.query("`a``b` < 2") # noqa + result = df.query("`a``b` < 2") # Note: Formatting checks may wrongly consider the above ``inline code``. expected = df[df["a`b"] < 2] tm.assert_frame_equal(result, expected)