27
27
center passed from the top level rolling API
28
28
closed : str, default None
29
29
closed passed from the top level rolling API
30
+ step : int, default None
31
+ step passed from the top level rolling API
30
32
win_type : str, default None
31
33
win_type passed from the top level rolling API
32
34
33
35
Returns
34
36
-------
35
- A tuple of ndarray[int64]s, indicating the boundaries of each
36
- window
37
+ A tuple of ndarray[int64]s:
38
+ start : array of start boundaries
39
+ end : array of end boundaries
40
+ ref : array of window reference locations, or None indicating all
41
+ must be None if step is None or 1
37
42
"""
38
43
39
44
@@ -55,14 +60,25 @@ def __init__(
55
60
for key , value in kwargs .items ():
56
61
setattr (self , key , value )
57
62
63
+ def _get_default_ref (self , num_values : int = 0 , step : int | None = None ):
64
+ """
65
+ Returns the default window reference locations.
66
+ """
67
+ return (
68
+ None
69
+ if step is None or step == 1
70
+ else np .arange (0 , num_values , step , dtype = "int64" )
71
+ )
72
+
58
73
@Appender (get_window_bounds_doc )
59
74
def get_window_bounds (
60
75
self ,
61
76
num_values : int = 0 ,
62
77
min_periods : int | None = None ,
63
78
center : bool | None = None ,
64
79
closed : str | None = None ,
65
- ) -> tuple [np .ndarray , np .ndarray ]:
80
+ step : int | None = None ,
81
+ ) -> tuple [np .ndarray , np .ndarray , np .ndarray | None ]:
66
82
67
83
raise NotImplementedError
68
84
@@ -77,14 +93,15 @@ def get_window_bounds(
77
93
min_periods : int | None = None ,
78
94
center : bool | None = None ,
79
95
closed : str | None = None ,
80
- ) -> tuple [np .ndarray , np .ndarray ]:
96
+ step : int | None = None ,
97
+ ) -> tuple [np .ndarray , np .ndarray , np .ndarray | None ]:
81
98
82
99
if center :
83
100
offset = (self .window_size - 1 ) // 2
84
101
else :
85
102
offset = 0
86
103
87
- end = np .arange (1 + offset , num_values + 1 + offset , dtype = "int64" )
104
+ end = np .arange (1 + offset , num_values + 1 + offset , step , dtype = "int64" )
88
105
start = end - self .window_size
89
106
if closed in ["left" , "both" ]:
90
107
start -= 1
@@ -94,7 +111,8 @@ def get_window_bounds(
94
111
end = np .clip (end , 0 , num_values )
95
112
start = np .clip (start , 0 , num_values )
96
113
97
- return start , end
114
+ ref = self ._get_default_ref (num_values , step )
115
+ return start , end , ref
98
116
99
117
100
118
class VariableWindowIndexer (BaseIndexer ):
@@ -107,7 +125,8 @@ def get_window_bounds(
107
125
min_periods : int | None = None ,
108
126
center : bool | None = None ,
109
127
closed : str | None = None ,
110
- ) -> tuple [np .ndarray , np .ndarray ]:
128
+ step : int | None = None ,
129
+ ) -> tuple [np .ndarray , np .ndarray , np .ndarray | None ]:
111
130
112
131
# error: Argument 4 to "calculate_variable_window_bounds" has incompatible
113
132
# type "Optional[bool]"; expected "bool"
@@ -119,6 +138,7 @@ def get_window_bounds(
119
138
min_periods ,
120
139
center , # type: ignore[arg-type]
121
140
closed ,
141
+ step if step is not None else 1 ,
122
142
self .index_array , # type: ignore[arg-type]
123
143
)
124
144
@@ -145,11 +165,14 @@ def get_window_bounds(
145
165
min_periods : int | None = None ,
146
166
center : bool | None = None ,
147
167
closed : str | None = None ,
148
- ) -> tuple [np .ndarray , np .ndarray ]:
168
+ step : int | None = None ,
169
+ ) -> tuple [np .ndarray , np .ndarray , np .ndarray | None ]:
149
170
150
171
# if windows is variable, default is 'right', otherwise default is 'both'
151
172
if closed is None :
152
173
closed = "right" if self .index is not None else "both"
174
+ if step is None :
175
+ step = 1
153
176
154
177
right_closed = closed in ["right" , "both" ]
155
178
left_closed = closed in ["left" , "both" ]
@@ -202,7 +225,8 @@ def get_window_bounds(
202
225
if not right_closed :
203
226
end [i ] -= 1
204
227
205
- return start , end
228
+ ref = self ._get_default_ref (num_values , step )
229
+ return start [::step ], end [::step ], ref
206
230
207
231
208
232
class ExpandingIndexer (BaseIndexer ):
@@ -215,12 +239,15 @@ def get_window_bounds(
215
239
min_periods : int | None = None ,
216
240
center : bool | None = None ,
217
241
closed : str | None = None ,
218
- ) -> tuple [np .ndarray , np .ndarray ]:
242
+ step : int | None = None ,
243
+ ) -> tuple [np .ndarray , np .ndarray , np .ndarray | None ]:
219
244
220
- return (
221
- np .zeros (num_values , dtype = np .int64 ),
222
- np .arange (1 , num_values + 1 , dtype = np .int64 ),
223
- )
245
+ if step is None :
246
+ step = 1
247
+ end = np .arange (1 , num_values + 1 , step , dtype = np .int64 )
248
+ start = np .zeros (len (end ), dtype = np .int64 )
249
+ ref = self ._get_default_ref (num_values , step )
250
+ return start , end , ref
224
251
225
252
226
253
class FixedForwardWindowIndexer (BaseIndexer ):
@@ -256,21 +283,25 @@ def get_window_bounds(
256
283
min_periods : int | None = None ,
257
284
center : bool | None = None ,
258
285
closed : str | None = None ,
259
- ) -> tuple [np .ndarray , np .ndarray ]:
286
+ step : int | None = None ,
287
+ ) -> tuple [np .ndarray , np .ndarray , np .ndarray | None ]:
260
288
261
289
if center :
262
290
raise ValueError ("Forward-looking windows can't have center=True" )
263
291
if closed is not None :
264
292
raise ValueError (
265
293
"Forward-looking windows don't support setting the closed argument"
266
294
)
295
+ if step is None :
296
+ step = 1
267
297
268
- start = np .arange (num_values , dtype = "int64" )
298
+ start = np .arange (num_values , step = step , dtype = "int64" )
269
299
end = start + self .window_size
270
300
if self .window_size :
271
- end [ - self . window_size :] = num_values
301
+ end = np . clip ( end , 0 , num_values )
272
302
273
- return start , end
303
+ ref = self ._get_default_ref (num_values , step )
304
+ return start , end , ref
274
305
275
306
276
307
class GroupbyIndexer (BaseIndexer ):
@@ -319,12 +350,14 @@ def get_window_bounds(
319
350
min_periods : int | None = None ,
320
351
center : bool | None = None ,
321
352
closed : str | None = None ,
322
- ) -> tuple [np .ndarray , np .ndarray ]:
353
+ step : int | None = None ,
354
+ ) -> tuple [np .ndarray , np .ndarray , np .ndarray | None ]:
323
355
# 1) For each group, get the indices that belong to the group
324
356
# 2) Use the indices to calculate the start & end bounds of the window
325
357
# 3) Append the window bounds in group order
326
358
start_arrays = []
327
359
end_arrays = []
360
+ ref_arrays = []
328
361
window_indices_start = 0
329
362
for key , indices in self .groupby_indices .items ():
330
363
index_array : np .ndarray | None
@@ -338,11 +371,12 @@ def get_window_bounds(
338
371
window_size = self .window_size ,
339
372
** self .indexer_kwargs ,
340
373
)
341
- start , end = indexer .get_window_bounds (
342
- len (indices ), min_periods , center , closed
374
+ start , end , ref = indexer .get_window_bounds (
375
+ len (indices ), min_periods , center , closed , step
343
376
)
344
377
start = start .astype (np .int64 )
345
378
end = end .astype (np .int64 )
379
+ ref = None if ref is None else ref .astype (np .int64 )
346
380
assert len (start ) == len (
347
381
end
348
382
), "these should be equal in length from get_window_bounds"
@@ -358,9 +392,13 @@ def get_window_bounds(
358
392
)
359
393
start_arrays .append (window_indices .take (ensure_platform_int (start )))
360
394
end_arrays .append (window_indices .take (ensure_platform_int (end )))
395
+ ref_arrays .append (
396
+ None if ref is None else window_indices .take (ensure_platform_int (ref ))
397
+ )
361
398
start = np .concatenate (start_arrays )
362
399
end = np .concatenate (end_arrays )
363
- return start , end
400
+ ref = None if step is None or step == 1 else np .concatenate (ref_arrays )
401
+ return start , end , ref
364
402
365
403
366
404
class ExponentialMovingWindowIndexer (BaseIndexer ):
@@ -373,6 +411,11 @@ def get_window_bounds(
373
411
min_periods : int | None = None ,
374
412
center : bool | None = None ,
375
413
closed : str | None = None ,
376
- ) -> tuple [np .ndarray , np .ndarray ]:
414
+ step : int | None = None ,
415
+ ) -> tuple [np .ndarray , np .ndarray , np .ndarray | None ]:
377
416
378
- return np .array ([0 ], dtype = np .int64 ), np .array ([num_values ], dtype = np .int64 )
417
+ return (
418
+ np .array ([0 ], dtype = np .int64 ),
419
+ np .array ([num_values ], dtype = np .int64 ),
420
+ None ,
421
+ )
0 commit comments