-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
ENH: Implement weighted rolling var and std #27682
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 10 commits
04e1f70
f62b06e
ea0b2db
338754e
ff1aa8f
e58548e
1273cf9
f13f0aa
d25561c
2a4944a
7840b29
6922e4d
7510daf
8347185
8c6d4ae
4b3e5eb
1221ccd
b260ff7
6f0f1a5
b54dea0
4bb954f
565097e
3fa6028
221a522
b40dc0a
c06f2b6
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 |
---|---|---|
|
@@ -1754,6 +1754,152 @@ def _roll_weighted_sum_mean(float64_t[:] values, float64_t[:] weights, | |
return output | ||
|
||
|
||
# ---------------------------------------------------------------------- | ||
# Rolling var for weighted window | ||
|
||
|
||
cdef inline float64_t calc_weighted_var(float64_t t, | ||
float64_t sum_w, | ||
Py_ssize_t win_n, | ||
unsigned int ddof, | ||
float64_t nobs, | ||
int64_t minp) nogil: | ||
cdef: | ||
float64_t result | ||
|
||
ihsansecer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# Variance is unchanged if no observation is added or removed | ||
if (nobs >= minp) and (nobs > ddof): | ||
|
||
# pathological case | ||
if nobs == 1: | ||
result = 0 | ||
else: | ||
result = t * win_n / ((win_n - ddof) * sum_w) | ||
if result < 0: | ||
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. Would this only be negative if the weight was negative which we should validate much earlier? 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. I guess it might be if one weight is negative and another is positive but not necessarily |
||
result = 0 | ||
else: | ||
result = NaN | ||
|
||
return result | ||
|
||
|
||
cdef inline void add_weighted_var(float64_t val, | ||
ihsansecer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
float64_t w, | ||
float64_t *t, | ||
float64_t *sum_w, | ||
float64_t *mean, | ||
float64_t *nobs) nogil: | ||
""" | ||
Update weighted mean (mean), sum of weights (sum_w) and sum of | ||
weighted squared differences (t) to include value (val) and | ||
weight (w) pair in variance calculation using West's method. | ||
|
||
ihsansecer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Paper: https://dl.acm.org/citation.cfm?id=359153 | ||
""" | ||
|
||
cdef: | ||
float64_t temp, q, r | ||
|
||
if isnan(val): | ||
return | ||
|
||
nobs[0] = nobs[0] + 1 | ||
|
||
q = val - mean[0] | ||
temp = sum_w[0] + w | ||
r = q * w / temp | ||
|
||
mean[0] = mean[0] + r | ||
t[0] = t[0] + r * sum_w[0] * q | ||
sum_w[0] = temp | ||
|
||
|
||
cdef inline void remove_weighted_var(float64_t val, | ||
ihsansecer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
float64_t w, | ||
float64_t *t, | ||
float64_t *sum_w, | ||
float64_t *mean, | ||
float64_t *nobs) nogil: | ||
""" | ||
ihsansecer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Update weighted mean (mean), sum of weights (sum_w) and sum | ||
of weighted squared differences (t) to remove value (val) and | ||
weight (w) pair from variance calculation using West's method. | ||
|
||
Paper: https://dl.acm.org/citation.cfm?id=359153 | ||
""" | ||
|
||
cdef: | ||
float64_t temp, q, r | ||
|
||
if notnan(val): | ||
nobs[0] = nobs[0] - 1 | ||
|
||
if nobs[0]: | ||
q = val - mean[0] | ||
temp = sum_w[0] - w | ||
r = q * w / temp | ||
|
||
mean[0] = mean[0] - r | ||
t[0] = t[0] - r * sum_w[0] * q | ||
sum_w[0] = temp | ||
|
||
else: | ||
t[0] = 0 | ||
sum_w[0] = 0 | ||
mean[0] = 0 | ||
|
||
|
||
def roll_weighted_var(float64_t[:] values, float64_t[:] weights, | ||
int64_t minp, unsigned int ddof): | ||
""" | ||
Calculates weighted rolling variance using West's online algorithm. | ||
|
||
Paper: https://dl.acm.org/citation.cfm?id=359153 | ||
""" | ||
|
||
cdef: | ||
float64_t t = 0, sum_w = 0, mean = 0, nobs = 0 | ||
float64_t val, pre_val, w, pre_w | ||
Py_ssize_t i, n, win_n | ||
float64_t[:] output | ||
|
||
n = len(values) | ||
win_n = len(weights) | ||
output = np.empty(n, dtype=float) | ||
|
||
with nogil: | ||
|
||
for i in range(win_n): | ||
add_weighted_var(values[i], weights[i], &t, | ||
&sum_w, &mean, &nobs) | ||
|
||
output[i] = calc_weighted_var(t, sum_w, win_n, | ||
ddof, nobs, minp) | ||
|
||
for i in range(win_n, n): | ||
val = values[i] | ||
pre_val = values[i - win_n] | ||
|
||
w = weights[i % win_n] | ||
pre_w = weights[(i - win_n) % win_n] | ||
|
||
if notnan(val): | ||
if pre_val == pre_val: | ||
remove_weighted_var(pre_val, pre_w, &t, | ||
&sum_w, &mean, &nobs) | ||
|
||
add_weighted_var(val, w, &t, &sum_w, &mean, &nobs) | ||
|
||
elif pre_val == pre_val: | ||
remove_weighted_var(pre_val, pre_w, &t, | ||
&sum_w, &mean, &nobs) | ||
|
||
output[i] = calc_weighted_var(t, sum_w, win_n, | ||
ddof, nobs, minp) | ||
|
||
return output | ||
|
||
|
||
# ---------------------------------------------------------------------- | ||
# Exponentially weighted moving average | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.