-
-
Notifications
You must be signed in to change notification settings - Fork 18.6k
Center rolling window for time offset #38780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
11c1bb7
3cac891
c73ffc4
58a8ebf
dca9d04
37cb6fa
81e0e4e
463c7f0
321f07c
9270bab
dd33e32
6e4da84
e0966e8
b82f514
18a7b5b
9c4cc58
c27f50e
abaa43b
dc046da
95e3f26
8d582a1
8d5a55c
525cc69
6ac79b9
9bf6ce3
4f98fc5
e5ae3b2
d106940
d4f6d22
c2a7333
73313e6
92f8992
648d2d3
278d33f
5e50f36
c11cf15
2e3f875
f63309b
9f76a41
f05ed61
6fbd080
dff6942
0520e18
a087a6b
5b9b8ff
fca3b4d
6c1c58a
b7e5035
f44c6e6
3dcad64
0f9f6df
315b320
f7d1110
fc88ae4
e07a1f2
cefbb16
edbfd21
bfc0f0d
43e04ed
1e724dc
47a3b14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,7 @@ def calculate_variable_window_bounds( | |
int64_t num_values, | ||
int64_t window_size, | ||
object min_periods, # unused but here to match get_window_bounds signature | ||
object center, # unused but here to match get_window_bounds signature | ||
bint center, | ||
object closed, | ||
const int64_t[:] index | ||
): | ||
|
@@ -30,7 +30,7 @@ def calculate_variable_window_bounds( | |
ignored, exists for compatibility | ||
|
||
center : object | ||
ignored, exists for compatibility | ||
center the rolling window on the current observation | ||
|
||
closed : str | ||
string of side of the window that should be closed | ||
|
@@ -43,7 +43,9 @@ def calculate_variable_window_bounds( | |
(ndarray[int64], ndarray[int64]) | ||
""" | ||
cdef: | ||
bint left_closed = False, right_closed = False | ||
bint left_closed = False | ||
bint right_closed = False | ||
bint center_window = False | ||
ndarray[int64_t, ndim=1] start, end | ||
int64_t start_bound, end_bound, index_growth_sign = 1 | ||
Py_ssize_t i, j | ||
|
@@ -60,6 +62,8 @@ def calculate_variable_window_bounds( | |
|
||
if index[num_values - 1] < index[0]: | ||
index_growth_sign = -1 | ||
if center: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sine we typed |
||
center_window = True | ||
|
||
start = np.empty(num_values, dtype='int64') | ||
start.fill(-1) | ||
|
@@ -74,14 +78,27 @@ def calculate_variable_window_bounds( | |
# right endpoint is open | ||
else: | ||
end[0] = 0 | ||
if center_window: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should just be |
||
for j in range(0, num_values+1): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (index[j] == index[0] + index_growth_sign*window_size/2 and | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use spaces around operators & parens to format this (we don't lint these files automatically) |
||
right_closed): | ||
end[0] = j+1 | ||
break | ||
elif index[j] >= index[0] + index_growth_sign * window_size/2: | ||
end[0] = j | ||
break | ||
|
||
with nogil: | ||
|
||
# start is start of slice interval (including) | ||
# end is end of slice interval (not including) | ||
for i in range(1, num_values): | ||
end_bound = index[i] | ||
start_bound = index[i] - index_growth_sign * window_size | ||
if center_window: | ||
end_bound = index[i] + index_growth_sign * window_size/2 | ||
start_bound = index[i] - index_growth_sign * window_size/2 | ||
else: | ||
end_bound = index[i] | ||
start_bound = index[i] - index_growth_sign * window_size | ||
|
||
# left endpoint is closed | ||
if left_closed: | ||
|
@@ -95,14 +112,27 @@ def calculate_variable_window_bounds( | |
start[i] = j | ||
break | ||
|
||
# for centered window advance the end bound until we are | ||
# outside the constraint | ||
if center_window: | ||
for j in range(end[i - 1], num_values+1): | ||
if j == num_values: | ||
end[i] = j | ||
elif ((index[j] - end_bound) * index_growth_sign == 0 and | ||
right_closed): | ||
end[i] = j+1 | ||
break | ||
elif (index[j] - end_bound) * index_growth_sign >= 0: | ||
end[i] = j | ||
break | ||
# end bound is previous end | ||
# or current index | ||
if (index[end[i - 1]] - end_bound) * index_growth_sign <= 0: | ||
elif (index[end[i - 1]] - end_bound) * index_growth_sign <= 0: | ||
end[i] = i + 1 | ||
else: | ||
end[i] = end[i - 1] | ||
|
||
# right endpoint is open | ||
if not right_closed: | ||
if not right_closed and not center_window: | ||
end[i] -= 1 | ||
return start, end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -334,9 +334,12 @@ def _get_window_indexer(self) -> BaseIndexer: | |
""" | ||
if isinstance(self.window, BaseIndexer): | ||
return self.window | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you undo this white space? |
||
if self._win_freq_i8 is not None: | ||
return VariableWindowIndexer( | ||
index_array=self._index_array, window_size=self._win_freq_i8 | ||
index_array=self._index_array, | ||
window_size=self._win_freq_i8, | ||
center=self.center, | ||
) | ||
return FixedWindowIndexer(window_size=self.window) | ||
|
||
|
@@ -450,7 +453,34 @@ def homogeneous_func(values: np.ndarray): | |
if values.size == 0: | ||
return values.copy() | ||
|
||
def _calculate_center_offset(window) -> int: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. None of this code below should be needed. The |
||
""" | ||
Calculate an offset necessary to have the window label to be centered. | ||
|
||
Parameters | ||
---------- | ||
window: ndarray or int | ||
window weights or window | ||
|
||
Returns | ||
------- | ||
int | ||
""" | ||
if not is_integer(window): | ||
window = len(window) | ||
return int((window - 1) / 2.0) | ||
|
||
offset = ( | ||
_calculate_center_offset(self.window) | ||
if self.center | ||
and not isinstance(self._get_window_indexer(), VariableWindowIndexer) | ||
else 0 | ||
) | ||
additional_nans = np.array([np.nan] * offset) | ||
|
||
def calc(x): | ||
x = np.concatenate((x, additional_nans)) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you undo this white space? |
||
start, end = window_indexer.get_window_bounds( | ||
num_values=len(x), | ||
min_periods=min_periods, | ||
|
@@ -469,6 +499,15 @@ def calc(x): | |
if numba_cache_key is not None: | ||
NUMBA_FUNC_CACHE[numba_cache_key] = func | ||
|
||
# TODO: is this equivalent to the above? | ||
# if use_numba_cache: | ||
# NUMBA_FUNC_CACHE[(kwargs["original_func"], "rolling_apply")] = func | ||
|
||
if self.center and not isinstance( | ||
self._get_window_indexer(), VariableWindowIndexer | ||
): | ||
result = self._center_window(result, self.window) | ||
|
||
return result | ||
|
||
if self.method == "single": | ||
|
@@ -1880,20 +1919,14 @@ def validate(self): | |
# we allow rolling on a datetimelike index | ||
if ( | ||
self.obj.empty | ||
# TODO: add "or self.is_datetimelike"? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't need this TODO |
||
or isinstance( | ||
self._on, (ABCDatetimeIndex, ABCTimedeltaIndex, ABCPeriodIndex) | ||
) | ||
) and isinstance(self.window, (str, BaseOffset, timedelta)): | ||
|
||
self._validate_monotonic() | ||
|
||
# we don't allow center | ||
if self.center: | ||
raise NotImplementedError( | ||
"center is not implemented for " | ||
"datetimelike and offset based windows" | ||
) | ||
|
||
# this will raise ValueError on non-fixed freqs | ||
try: | ||
freq = to_offset(self.window) | ||
|
Uh oh!
There was an error while loading. Please reload this page.