53
53
54
54
55
55
class _Window (PandasObject , SelectionMixin ):
56
- _attributes = ['window' , 'min_periods' , 'freq ' , 'center ' , 'win_type ' ,
57
- 'axis' , 'on' ]
56
+ _attributes = ['window' , 'min_periods' , 'min_weight ' , 'freq ' , 'center ' ,
57
+ 'win_type' , ' axis' , 'on' ]
58
58
exclusions = set ()
59
59
60
- def __init__ (self , obj , window = None , min_periods = None , freq = None ,
60
+ def __init__ (self , obj , window = None , min_periods = None , min_weight = None , freq = None ,
61
61
center = False , win_type = None , axis = 0 , on = None , ** kwargs ):
62
62
63
63
if freq is not None :
@@ -71,6 +71,7 @@ def __init__(self, obj, window=None, min_periods=None, freq=None,
71
71
self .on = on
72
72
self .window = window
73
73
self .min_periods = min_periods
74
+ self .min_weight = min_weight
74
75
self .freq = freq
75
76
self .center = center
76
77
self .win_type = win_type
@@ -744,7 +745,12 @@ def calc(x):
744
745
745
746
results .append (result )
746
747
747
- return self ._wrap_results (results , blocks , obj )
748
+ result = self ._wrap_results (results , blocks , obj )
749
+
750
+ if self .min_weight :
751
+ result = result .where (_min_weight_mask (self , self .min_weight ))
752
+
753
+ return result
748
754
749
755
750
756
class _Rolling_and_Expanding (_Rolling ):
@@ -1187,6 +1193,9 @@ class Expanding(_Rolling_and_Expanding):
1187
1193
min_periods : int, default None
1188
1194
Minimum number of observations in window required to have a value
1189
1195
(otherwise result is NA).
1196
+ min_weight : int, default None
1197
+ Minimum proportion of weight in available values in window required
1198
+ to have a value (otherwies result in NA)
1190
1199
freq : string or DateOffset object, optional (default None) (DEPRECATED)
1191
1200
Frequency to conform the data to before computing the statistic.
1192
1201
Specified as a frequency string or DateOffset object.
@@ -1227,12 +1236,13 @@ class Expanding(_Rolling_and_Expanding):
1227
1236
of :meth:`~pandas.Series.resample` (i.e. using the `mean`).
1228
1237
"""
1229
1238
1230
- _attributes = ['min_periods' , 'freq' , 'center' , 'axis' ]
1239
+ _attributes = ['min_periods' , 'min_weight' , ' freq' , 'center' , 'axis' ]
1231
1240
1232
- def __init__ (self , obj , min_periods = 1 , freq = None , center = False , axis = 0 ,
1233
- ** kwargs ):
1234
- super (Expanding , self ).__init__ (obj = obj , min_periods = min_periods ,
1235
- freq = freq , center = center , axis = axis )
1241
+ def __init__ (self , obj , min_periods = 1 , min_weight = None , freq = None ,
1242
+ center = False , axis = 0 , ** kwargs ):
1243
+ super (Expanding , self ).__init__ (
1244
+ obj = obj , min_periods = min_periods , min_weight = min_weight ,
1245
+ freq = freq , center = center , axis = axis )
1236
1246
1237
1247
@property
1238
1248
def _constructor (self ):
@@ -1473,14 +1483,16 @@ class EWM(_Rolling):
1473
1483
More details can be found at
1474
1484
http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows
1475
1485
"""
1476
- _attributes = ['com' , 'min_periods' , 'freq' , 'adjust' , 'ignore_na' , 'axis' ]
1486
+ _attributes = ['com' , 'min_periods' , 'min_weight' , 'freq' , 'adjust' ,
1487
+ 'ignore_na' , 'axis' ]
1477
1488
1478
1489
def __init__ (self , obj , com = None , span = None , halflife = None , alpha = None ,
1479
- min_periods = 0 , freq = None , adjust = True , ignore_na = False ,
1480
- axis = 0 ):
1490
+ min_periods = 0 , min_weight = None , freq = None , adjust = True ,
1491
+ ignore_na = False , axis = 0 ):
1481
1492
self .obj = obj
1482
1493
self .com = _get_center_of_mass (com , span , halflife , alpha )
1483
1494
self .min_periods = min_periods
1495
+ self .min_weight = min_weight
1484
1496
self .freq = freq
1485
1497
self .adjust = adjust
1486
1498
self .ignore_na = ignore_na
@@ -1540,7 +1552,12 @@ def func(arg):
1540
1552
1541
1553
results .append (np .apply_along_axis (func , self .axis , values ))
1542
1554
1543
- return self ._wrap_results (results , blocks , obj )
1555
+ result = self ._wrap_results (results , blocks , obj )
1556
+
1557
+ if self .min_weight :
1558
+ result = result .where (_min_weight_mask (self , self .min_weight ))
1559
+
1560
+ return result
1544
1561
1545
1562
@Substitution (name = 'ewm' )
1546
1563
@Appender (_doc_template )
@@ -1751,6 +1768,25 @@ def _check_func(minp, window):
1751
1768
return _check_func
1752
1769
1753
1770
1771
+ def _min_weight_mask (rolling , min_weight ):
1772
+ """
1773
+ Takes a rolling object and a min_weight proportion, and returns
1774
+ a pandas bool object with True where enough weight exists
1775
+ """
1776
+
1777
+ data = rolling .obj
1778
+ # all valid values have a value of 1 in valid_data
1779
+ valid_data = data .notnull ()
1780
+
1781
+ # This copies the rolling object, replacing obj with valid_data
1782
+ # The resulting values are the proportion of weight from values that _do_
1783
+ # contribute out of those that _could_
1784
+ valid_proportion = rolling ._shallow_copy (
1785
+ obj = valid_data , min_periods = 0 , min_weight = None ).mean ()
1786
+
1787
+ return valid_proportion >= min_weight
1788
+
1789
+
1754
1790
def _use_window (minp , window ):
1755
1791
if minp is None :
1756
1792
return window
0 commit comments