Skip to content

Commit

Permalink
Add corr, cov, std & var to .rolling_exp
Browse files Browse the repository at this point in the history
From the new routines in numbagg.

Maybe needs better tests (though these are quite heavily tested in numbagg), docs, and potentially need to think about types (maybe existing binary ops can help here?)
  • Loading branch information
max-sixty committed Oct 14, 2023
1 parent dafd726 commit 56e0dd7
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 1 deletion.
130 changes: 130 additions & 0 deletions xarray/core/rolling_exp.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,133 @@ def sum(self, keep_attrs: bool | None = None) -> T_DataWithCoords:
on_missing_core_dim="copy",
dask="parallelized",
).transpose(*dim_order)

def std(self) -> T_DataWithCoords:
"""
Exponentially weighted moving standard deviation.
`keep_attrs` is always True for this method. Drop attrs separately to remove attrs.
Examples
--------
>>> da = xr.DataArray([1, 1, 2, 2, 2], dims="x")
>>> da.rolling_exp(x=2, window_type="span").std()
<xarray.DataArray (x: 5)>
array([ nan, 0. , 0.67936622, 0.42966892, 0.25389527])
Dimensions without coordinates: x
"""

if has_numbagg is False or has_numbagg < "0.4.0":
raise ImportError(
f"numbagg >= 0.4.0 is required for rolling_exp().std(), currently {has_numbagg} is installed"
)
dim_order = self.obj.dims

return apply_ufunc(
numbagg.move_exp_nanstd,
self.obj,
input_core_dims=[[self.dim]],
kwargs=self.kwargs,
output_core_dims=[[self.dim]],
keep_attrs=True,
on_missing_core_dim="copy",
dask="parallelized",
).transpose(*dim_order)

def var(self) -> T_DataWithCoords:
"""
Exponentially weighted moving variance.
`keep_attrs` is always True for this method. Drop attrs separately to remove attrs.
Examples
--------
>>> da = xr.DataArray([1, 1, 2, 2, 2], dims="x")
>>> da.rolling_exp(x=2, window_type="span").var()
<xarray.DataArray (x: 5)>
array([ nan, 0. , 0.46153846, 0.18461538, 0.06446281])
Dimensions without coordinates: x
"""

if has_numbagg is False or has_numbagg < "0.4.0":
raise ImportError(
f"numbagg >= 0.4.0 is required for rolling_exp().var(), currently {has_numbagg} is installed"
)
dim_order = self.obj.dims

return apply_ufunc(
numbagg.move_exp_nanvar,
self.obj,
input_core_dims=[[self.dim]],
kwargs=self.kwargs,
output_core_dims=[[self.dim]],
keep_attrs=True,
on_missing_core_dim="copy",
dask="parallelized",
).transpose(*dim_order)

def cov(self, other: T_DataWithCoords) -> T_DataWithCoords:
"""
Exponentially weighted moving covariance.
`keep_attrs` is always True for this method. Drop attrs separately to remove attrs.
Examples
--------
>>> da = xr.DataArray([1, 1, 2, 2, 2], dims="x")
>>> da.rolling_exp(x=2, window_type="span").cov(da**2)
<xarray.DataArray (x: 5)>
array([ nan, 0. , 1.38461538, 0.55384615, 0.19338843])
Dimensions without coordinates: x
"""

if has_numbagg is False or has_numbagg < "0.4.0":
raise ImportError(
f"numbagg >= 0.4.0 is required for rolling_exp().cov(), currently {has_numbagg} is installed"
)
dim_order = self.obj.dims

return apply_ufunc(
numbagg.move_exp_nancov,
self.obj,
other,
input_core_dims=[[self.dim], [self.dim]],
kwargs=self.kwargs,
output_core_dims=[[self.dim]],
keep_attrs=True,
on_missing_core_dim="copy",
dask="parallelized",
).transpose(*dim_order)

def corr(self, other: T_DataWithCoords) -> T_DataWithCoords:
"""
Exponentially weighted moving correlation.
`keep_attrs` is always True for this method. Drop attrs separately to remove attrs.
Examples
--------
>>> da = xr.DataArray([1, 1, 2, 2, 2], dims="x")
>>> da.rolling_exp(x=2, window_type="span").corr(da.shift(x=1))
<xarray.DataArray (x: 5)>
array([ nan, nan, nan, 0.4330127 , 0.48038446])
Dimensions without coordinates: x
"""

if has_numbagg is False or has_numbagg < "0.4.0":
raise ImportError(
f"numbagg >= 0.4.0 is required for rolling_exp().cov(), currently {has_numbagg} is installed"
)
dim_order = self.obj.dims

return apply_ufunc(
numbagg.move_exp_nancorr,
self.obj,
other,
input_core_dims=[[self.dim], [self.dim]],
kwargs=self.kwargs,
output_core_dims=[[self.dim]],
keep_attrs=True,
on_missing_core_dim="copy",
dask="parallelized",
).transpose(*dim_order)
2 changes: 1 addition & 1 deletion xarray/tests/test_rolling.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ class TestDataArrayRollingExp:
[["span", 5], ["alpha", 0.5], ["com", 0.5], ["halflife", 5]],
)
@pytest.mark.parametrize("backend", ["numpy"], indirect=True)
@pytest.mark.parametrize("func", ["mean", "sum"])
@pytest.mark.parametrize("func", ["mean", "sum", "var", "std"])
def test_rolling_exp_runs(self, da, dim, window_type, window, func) -> None:
da = da.where(da > 0.2)

Expand Down

0 comments on commit 56e0dd7

Please sign in to comment.