33
33
assert_array_equal ,
34
34
assert_equal ,
35
35
assert_identical ,
36
+ get_expected_rolling_indices ,
36
37
has_dask ,
37
38
raise_if_dask_computes ,
38
39
requires_bottleneck ,
@@ -6505,7 +6506,7 @@ def test_isin(da):
6505
6506
6506
6507
@pytest .mark .parametrize ("da" , (1 , 2 ), indirect = True )
6507
6508
@pytest .mark .parametrize ("center" , (True , False , None ))
6508
- @pytest .mark .parametrize ("pad" , (True , False , None ))
6509
+ @pytest .mark .parametrize ("pad" , (True , False ))
6509
6510
@pytest .mark .parametrize ("min_periods" , (1 , 6 , None ))
6510
6511
@pytest .mark .parametrize ("window" , (6 , 7 ))
6511
6512
def test_rolling_iter (da , center , pad , min_periods , window ):
@@ -6610,40 +6611,42 @@ def test_rolling_wrapped_bottleneck(da, name, min_periods):
6610
6611
6611
6612
@pytest .mark .parametrize ("name" , ("sum" , "mean" , "std" , "min" , "max" , "median" ))
6612
6613
@pytest .mark .parametrize ("center" , (True , False , None ))
6613
- @pytest .mark .parametrize ("pad" , (True , False , None ))
6614
+ @pytest .mark .parametrize ("pad" , (True , False ))
6614
6615
@pytest .mark .parametrize ("backend" , ["numpy" ], indirect = True )
6615
6616
def test_rolling_wrapped_bottleneck_center_pad (da , name , center , pad ):
6616
6617
pytest .importorskip ("bottleneck" , minversion = "1.1" )
6617
6618
6619
+ window = 7
6620
+ count = len (da ["time" ])
6618
6621
rolling_obj = da .rolling (time = 7 , center = center , pad = pad )
6619
6622
actual = getattr (rolling_obj , name )()["time" ]
6620
6623
6621
- if pad :
6622
- expected = da ["time" ]
6623
- else :
6624
- if center :
6625
- expected = da ["time" ][slice (3 , - 3 )]
6626
- else :
6627
- expected = da ["time" ][slice (6 , None )]
6624
+ expected_index = get_expected_rolling_indices (count , window , center , pad )
6625
+ expected = da ["time" ][expected_index ]
6628
6626
6629
6627
assert_equal (actual , expected )
6630
6628
6631
6629
6632
6630
@requires_dask
6633
6631
@pytest .mark .parametrize ("name" , ("mean" , "count" ))
6634
6632
@pytest .mark .parametrize ("center" , (True , False , None ))
6633
+ @pytest .mark .parametrize ("pad" , (True , False ))
6635
6634
@pytest .mark .parametrize ("min_periods" , (1 , None ))
6636
6635
@pytest .mark .parametrize ("window" , (7 , 8 ))
6637
6636
@pytest .mark .parametrize ("backend" , ["dask" ], indirect = True )
6638
- def test_rolling_wrapped_dask (da , name , center , min_periods , window ):
6637
+ def test_rolling_wrapped_dask (da , name , center , pad , min_periods , window ):
6639
6638
# dask version
6640
- rolling_obj = da .rolling (time = window , min_periods = min_periods , center = center )
6639
+ rolling_obj = da .rolling (
6640
+ time = window , min_periods = min_periods , center = center , pad = pad
6641
+ )
6641
6642
actual = getattr (rolling_obj , name )().load ()
6642
6643
if name != "count" :
6643
6644
with pytest .warns (DeprecationWarning , match = "Reductions are applied" ):
6644
6645
getattr (rolling_obj , name )(dim = "time" )
6645
6646
# numpy version
6646
- rolling_obj = da .load ().rolling (time = window , min_periods = min_periods , center = center )
6647
+ rolling_obj = da .load ().rolling (
6648
+ time = window , min_periods = min_periods , center = center , pad = pad
6649
+ )
6647
6650
expected = getattr (rolling_obj , name )()
6648
6651
6649
6652
# using all-close because rolling over ghost cells introduces some
@@ -6652,39 +6655,52 @@ def test_rolling_wrapped_dask(da, name, center, min_periods, window):
6652
6655
6653
6656
# with zero chunked array GH:2113
6654
6657
rolling_obj = da .chunk ().rolling (
6655
- time = window , min_periods = min_periods , center = center
6658
+ time = window ,
6659
+ min_periods = min_periods ,
6660
+ center = center ,
6661
+ pad = pad ,
6656
6662
)
6657
6663
actual = getattr (rolling_obj , name )().load ()
6658
6664
assert_allclose (actual , expected )
6659
6665
6660
6666
6661
6667
@pytest .mark .parametrize ("center" , (True , None ))
6662
- def test_rolling_wrapped_dask_nochunk (center ):
6668
+ @pytest .mark .parametrize ("pad" , (True , False ))
6669
+ def test_rolling_wrapped_dask_nochunk (center , pad ):
6663
6670
# GH:2113
6664
6671
pytest .importorskip ("dask.array" )
6665
6672
6666
6673
da_day_clim = xr .DataArray (
6667
6674
np .arange (1 , 367 ), coords = [np .arange (1 , 367 )], dims = "dayofyear"
6668
6675
)
6669
- expected = da_day_clim .rolling (dayofyear = 31 , center = center ).mean ()
6670
- actual = da_day_clim .chunk ().rolling (dayofyear = 31 , center = center ).mean ()
6676
+ expected = da_day_clim .rolling (dayofyear = 31 , center = center , pad = pad ).mean ()
6677
+ actual = da_day_clim .chunk ().rolling (dayofyear = 31 , center = center , pad = pad ).mean ()
6671
6678
assert_allclose (actual , expected )
6672
6679
6673
6680
6674
6681
@pytest .mark .parametrize ("center" , (True , False ))
6682
+ @pytest .mark .parametrize ("pad" , (False ,))
6675
6683
@pytest .mark .parametrize ("min_periods" , (None , 1 , 2 , 3 ))
6676
- @pytest .mark .parametrize ("window" , (1 , 2 , 3 , 4 ))
6677
- def test_rolling_pandas_compat (center , window , min_periods ):
6684
+ @pytest .mark .parametrize ("window" , (2 , 3 , 4 ))
6685
+ def test_rolling_pandas_compat (center , pad , window , min_periods ):
6678
6686
s = pd .Series (np .arange (10 ))
6679
6687
da = DataArray .from_series (s )
6680
6688
6681
6689
if min_periods is not None and window < min_periods :
6682
6690
min_periods = window
6683
6691
6684
- s_rolling = s .rolling (window , center = center , min_periods = min_periods ).mean ()
6685
- da_rolling = da .rolling (index = window , center = center , min_periods = min_periods ).mean ()
6692
+ expected_index = get_expected_rolling_indices (10 , window , center , pad )
6693
+
6694
+ s_rolling = (
6695
+ s .rolling (window , center = center , min_periods = min_periods )
6696
+ .mean ()
6697
+ .iloc [expected_index ]
6698
+ )
6699
+ da_rolling = da .rolling (
6700
+ index = window , center = center , pad = pad , min_periods = min_periods
6701
+ ).mean ()
6686
6702
da_rolling_np = da .rolling (
6687
- index = window , center = center , min_periods = min_periods
6703
+ index = window , center = center , pad = pad , min_periods = min_periods
6688
6704
).reduce (np .nanmean )
6689
6705
6690
6706
np .testing .assert_allclose (s_rolling .values , da_rolling .values )
@@ -6694,10 +6710,12 @@ def test_rolling_pandas_compat(center, window, min_periods):
6694
6710
6695
6711
6696
6712
@pytest .mark .parametrize ("center" , (True , False ))
6697
- @pytest .mark .parametrize ("window" , (1 , 2 , 3 , 4 ))
6713
+ @pytest .mark .parametrize ("window" , (2 , 3 , 4 ))
6698
6714
def test_rolling_construct (center , window ):
6699
- s = pd .Series (np .arange (10 ))
6715
+ count = 10
6716
+ s = pd .Series (np .arange (count ))
6700
6717
da = DataArray .from_series (s )
6718
+ da = da .assign_coords (time = ("index" , np .arange (1 , count + 1 )))
6701
6719
6702
6720
s_rolling = s .rolling (window , center = center , min_periods = 1 ).mean ()
6703
6721
da_rolling = da .rolling (index = window , center = center , min_periods = 1 )
@@ -6718,21 +6736,41 @@ def test_rolling_construct(center, window):
6718
6736
assert da_rolling_mean .isnull ().sum () == 0
6719
6737
assert (da_rolling_mean == 0.0 ).sum () >= 0
6720
6738
6739
+ # with no padding
6740
+ da_rolling = da .rolling (index = window , center = center , min_periods = 1 , pad = False )
6741
+ da_rolling_mean = da_rolling .construct ("window" , stride = 2 ).mean ("window" )
6742
+
6743
+ expected_index = get_expected_rolling_indices (
6744
+ count , window , center , pad = False , stride = 2
6745
+ )
6746
+
6747
+ assert da_rolling_mean .sizes ["index" ] == len (expected_index )
6748
+ assert (da_rolling_mean .index .values == expected_index ).all ()
6749
+ assert (da_rolling_mean .time .values == expected_index + 1 ).all ()
6750
+
6751
+ np .testing .assert_allclose (s_rolling .values [expected_index ], da_rolling_mean .values )
6752
+ np .testing .assert_allclose (
6753
+ s_rolling .index [expected_index ], da_rolling_mean ["index" ]
6754
+ )
6755
+
6721
6756
6722
6757
@pytest .mark .parametrize ("da" , (1 , 2 ), indirect = True )
6723
6758
@pytest .mark .parametrize ("center" , (True , False ))
6759
+ @pytest .mark .parametrize ("pad" , (True , False ))
6724
6760
@pytest .mark .parametrize ("min_periods" , (None , 1 , 2 , 3 ))
6725
6761
@pytest .mark .parametrize ("window" , (1 , 2 , 3 , 4 ))
6726
6762
@pytest .mark .parametrize ("name" , ("sum" , "mean" , "std" , "max" ))
6727
- def test_rolling_reduce (da , center , min_periods , window , name ):
6763
+ def test_rolling_reduce (da , center , pad , min_periods , window , name ):
6728
6764
if min_periods is not None and window < min_periods :
6729
6765
min_periods = window
6730
6766
6731
6767
if da .isnull ().sum () > 1 and window == 1 :
6732
6768
# this causes all nan slices
6733
6769
window = 2
6734
6770
6735
- rolling_obj = da .rolling (time = window , center = center , min_periods = min_periods )
6771
+ rolling_obj = da .rolling (
6772
+ time = window , center = center , pad = pad , min_periods = min_periods
6773
+ )
6736
6774
6737
6775
# add nan prefix to numpy methods to get similar # behavior as bottleneck
6738
6776
actual = rolling_obj .reduce (getattr (np , "nan%s" % name ))
@@ -6742,18 +6780,21 @@ def test_rolling_reduce(da, center, min_periods, window, name):
6742
6780
6743
6781
6744
6782
@pytest .mark .parametrize ("center" , (True , False ))
6783
+ @pytest .mark .parametrize ("pad" , (True , False ))
6745
6784
@pytest .mark .parametrize ("min_periods" , (None , 1 , 2 , 3 ))
6746
6785
@pytest .mark .parametrize ("window" , (1 , 2 , 3 , 4 ))
6747
6786
@pytest .mark .parametrize ("name" , ("sum" , "max" ))
6748
- def test_rolling_reduce_nonnumeric (center , min_periods , window , name ):
6787
+ def test_rolling_reduce_nonnumeric (center , pad , min_periods , window , name ):
6749
6788
da = DataArray (
6750
6789
[0 , np .nan , 1 , 2 , np .nan , 3 , 4 , 5 , np .nan , 6 , 7 ], dims = "time"
6751
6790
).isnull ()
6752
6791
6753
6792
if min_periods is not None and window < min_periods :
6754
6793
min_periods = window
6755
6794
6756
- rolling_obj = da .rolling (time = window , center = center , min_periods = min_periods )
6795
+ rolling_obj = da .rolling (
6796
+ time = window , center = center , pad = pad , min_periods = min_periods
6797
+ )
6757
6798
6758
6799
# add nan prefix to numpy methods to get similar behavior as bottleneck
6759
6800
actual = rolling_obj .reduce (getattr (np , "nan%s" % name ))
@@ -6767,11 +6808,15 @@ def test_rolling_count_correct():
6767
6808
6768
6809
kwargs = [
6769
6810
{"time" : 11 , "min_periods" : 1 },
6811
+ {"time" : 11 , "min_periods" : 1 , "pad" : False },
6770
6812
{"time" : 11 , "min_periods" : None },
6813
+ {"time" : 11 , "min_periods" : None , "pad" : False },
6771
6814
{"time" : 7 , "min_periods" : 2 },
6815
+ {"time" : 7 , "min_periods" : 2 , "pad" : False },
6772
6816
]
6773
6817
expecteds = [
6774
6818
DataArray ([1 , 1 , 2 , 3 , 3 , 4 , 5 , 6 , 6 , 7 , 8 ], dims = "time" ),
6819
+ DataArray ([8 ], dims = "time" ),
6775
6820
DataArray (
6776
6821
[
6777
6822
np .nan ,
@@ -6788,7 +6833,9 @@ def test_rolling_count_correct():
6788
6833
],
6789
6834
dims = "time" ,
6790
6835
),
6836
+ DataArray ([np .nan ], dims = "time" ),
6791
6837
DataArray ([np .nan , np .nan , 2 , 3 , 3 , 4 , 5 , 5 , 5 , 5 , 5 ], dims = "time" ),
6838
+ DataArray ([5 , 5 , 5 , 5 , 5 ], dims = "time" ),
6792
6839
]
6793
6840
6794
6841
for kwarg , expected in zip (kwargs , expecteds ):
@@ -6800,17 +6847,20 @@ def test_rolling_count_correct():
6800
6847
6801
6848
6802
6849
@pytest .mark .parametrize ("da" , (1 ,), indirect = True )
6803
- @pytest .mark .parametrize ("center" , (True , False ))
6850
+ @pytest .mark .parametrize ("center" , (True , False , {"time" : True , "x" : False }))
6851
+ @pytest .mark .parametrize ("pad" , (True , False , {"time" : True , "x" : False }))
6804
6852
@pytest .mark .parametrize ("min_periods" , (None , 1 ))
6805
6853
@pytest .mark .parametrize ("name" , ("sum" , "mean" , "max" ))
6806
- def test_ndrolling_reduce (da , center , min_periods , name ):
6807
- rolling_obj = da .rolling (time = 3 , x = 2 , center = center , min_periods = min_periods )
6854
+ def test_ndrolling_reduce (da , center , pad , min_periods , name ):
6855
+ rolling_obj = da .rolling (
6856
+ time = 3 , x = 2 , center = center , pad = pad , min_periods = min_periods
6857
+ )
6808
6858
6809
6859
actual = getattr (rolling_obj , name )()
6810
6860
expected = getattr (
6811
6861
getattr (
6812
- da .rolling (time = 3 , center = center , min_periods = min_periods ), name
6813
- )().rolling (x = 2 , center = center , min_periods = min_periods ),
6862
+ da .rolling (time = 3 , center = center , pad = pad , min_periods = min_periods ), name
6863
+ )().rolling (x = 2 , center = center , pad = pad , min_periods = min_periods ),
6814
6864
name ,
6815
6865
)()
6816
6866
@@ -6828,23 +6878,33 @@ def test_ndrolling_reduce(da, center, min_periods, name):
6828
6878
assert_allclose (actual , expected .where (count >= min_periods ))
6829
6879
6830
6880
6831
- @pytest .mark .parametrize ("center" , (True , False , (True , False )))
6881
+ @pytest .mark .parametrize ("center" , (True , False , {"x" : True , "z" : False }))
6882
+ @pytest .mark .parametrize (
6883
+ "pad" ,
6884
+ (
6885
+ True ,
6886
+ False ,
6887
+ {"x" : True , "z" : False },
6888
+ ),
6889
+ )
6832
6890
@pytest .mark .parametrize ("fill_value" , (np .nan , 0.0 ))
6833
- def test_ndrolling_construct (center , fill_value ):
6891
+ def test_ndrolling_construct (center , pad , fill_value ):
6834
6892
da = DataArray (
6835
6893
np .arange (5 * 6 * 7 ).reshape (5 , 6 , 7 ).astype (float ),
6836
6894
dims = ["x" , "y" , "z" ],
6837
6895
coords = {"x" : ["a" , "b" , "c" , "d" , "e" ], "y" : np .arange (6 )},
6838
6896
)
6839
- actual = da .rolling (x = 3 , z = 2 , center = center ).construct (
6897
+ actual = da .rolling (x = 3 , z = 2 , center = center , pad = pad ).construct (
6840
6898
x = "x1" , z = "z1" , fill_value = fill_value
6841
6899
)
6842
- if not isinstance (center , tuple ):
6843
- center = (center , center )
6900
+ if not isinstance (center , dict ):
6901
+ center = {"x" : center , "z" : center }
6902
+ if not isinstance (pad , dict ):
6903
+ pad = {"x" : pad , "z" : pad }
6844
6904
expected = (
6845
- da .rolling (x = 3 , center = center [0 ])
6905
+ da .rolling (x = 3 , center = center ["x" ], pad = pad [ "x" ])
6846
6906
.construct (x = "x1" , fill_value = fill_value )
6847
- .rolling (z = 2 , center = center [1 ])
6907
+ .rolling (z = 2 , center = center ["z" ], pad = pad [ "z" ])
6848
6908
.construct (z = "z1" , fill_value = fill_value )
6849
6909
)
6850
6910
assert_allclose (actual , expected )
0 commit comments