diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 9f1c4755bc54f..4681257dcfca0 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -150,6 +150,7 @@ Other enhancements - Added ``validate`` argument to :meth:`DataFrame.join` (:issue:`46622`) - A :class:`errors.PerformanceWarning` is now thrown when using ``string[pyarrow]`` dtype with methods that don't dispatch to ``pyarrow.compute`` methods (:issue:`42613`) - Added ``numeric_only`` argument to :meth:`Resampler.sum`, :meth:`Resampler.prod`, :meth:`Resampler.min`, :meth:`Resampler.max`, :meth:`Resampler.first`, and :meth:`Resampler.last` (:issue:`46442`) +- ``times`` argument in :class:`.ExponentialMovingWindow` now accepts ``np.timedelta64`` (:issue:`47003`) .. --------------------------------------------------------------------------- .. _whatsnew_150.notable_bug_fixes: diff --git a/pandas/core/window/ewm.py b/pandas/core/window/ewm.py index 32cb4938344c4..922d194f04c55 100644 --- a/pandas/core/window/ewm.py +++ b/pandas/core/window/ewm.py @@ -134,8 +134,9 @@ class ExponentialMovingWindow(BaseWindow): r""" Provide exponentially weighted (EW) calculations. - Exactly one parameter: ``com``, ``span``, ``halflife``, or ``alpha`` must be - provided. + Exactly one of ``com``, ``span``, ``halflife``, or ``alpha`` must be + provided if ``times`` is not provided. If ``times`` is provided, + ``halflife`` and one of ``com``, ``span`` or ``alpha`` may be provided. Parameters ---------- @@ -155,7 +156,7 @@ class ExponentialMovingWindow(BaseWindow): :math:`\alpha = 1 - \exp\left(-\ln(2) / halflife\right)`, for :math:`halflife > 0`. - If ``times`` is specified, the time unit (str or timedelta) over which an + If ``times`` is specified, a timedelta convertible unit over which an observation decays to half its value. Only applicable to ``mean()``, and halflife value will not apply to the other functions. @@ -389,10 +390,8 @@ def __init__( raise ValueError("times must be datetime64[ns] dtype.") if len(self.times) != len(obj): raise ValueError("times must be the same length as the object.") - if not isinstance(self.halflife, (str, datetime.timedelta)): - raise ValueError( - "halflife must be a string or datetime.timedelta object" - ) + if not isinstance(self.halflife, (str, datetime.timedelta, np.timedelta64)): + raise ValueError("halflife must be a timedelta convertible object") if isna(self.times).any(): raise ValueError("Cannot convert NaT values to integer") self._deltas = _calculate_deltas(self.times, self.halflife) @@ -404,7 +403,7 @@ def __init__( self._com = 1.0 else: if self.halflife is not None and isinstance( - self.halflife, (str, datetime.timedelta) + self.halflife, (str, datetime.timedelta, np.timedelta64) ): raise ValueError( "halflife can only be a timedelta convertible argument if " diff --git a/pandas/tests/window/conftest.py b/pandas/tests/window/conftest.py index f42a1a5449c5c..8977d1a0d9d1b 100644 --- a/pandas/tests/window/conftest.py +++ b/pandas/tests/window/conftest.py @@ -102,7 +102,7 @@ def engine_and_raw(request): return request.param -@pytest.fixture(params=["1 day", timedelta(days=1)]) +@pytest.fixture(params=["1 day", timedelta(days=1), np.timedelta64(1, "D")]) def halflife_with_times(request): """Halflife argument for EWM when times is specified.""" return request.param diff --git a/pandas/tests/window/test_ewm.py b/pandas/tests/window/test_ewm.py index b1e8b43258750..66cd36d121750 100644 --- a/pandas/tests/window/test_ewm.py +++ b/pandas/tests/window/test_ewm.py @@ -90,7 +90,7 @@ def test_ewma_times_not_same_length(): def test_ewma_halflife_not_correct_type(): - msg = "halflife must be a string or datetime.timedelta object" + msg = "halflife must be a timedelta convertible object" with pytest.raises(ValueError, match=msg): Series(range(5)).ewm(halflife=1, times=np.arange(5).astype("datetime64[ns]"))