@@ -161,27 +161,42 @@ cdef inline float64_t calc_sum(int64_t minp, int64_t nobs, float64_t sum_x) nogi
161
161
return result
162
162
163
163
164
- cdef inline void add_sum(float64_t val, int64_t * nobs, float64_t * sum_x) nogil:
165
- """ add a value from the sum calc """
164
+ cdef inline void add_sum(float64_t val, int64_t * nobs, float64_t * sum_x,
165
+ float64_t * compensation) nogil:
166
+ """ add a value from the sum calc using Kahan summation """
167
+
168
+ cdef:
169
+ float64_t y, t
166
170
167
171
# Not NaN
168
172
if notnan(val):
169
173
nobs[0 ] = nobs[0 ] + 1
170
- sum_x[0 ] = sum_x[0 ] + val
174
+ y = val - compensation[0 ]
175
+ t = sum_x[0 ] + y
176
+ compensation[0 ] = t - sum_x[0 ] - y
177
+ sum_x[0 ] = t
171
178
172
179
173
- cdef inline void remove_sum(float64_t val, int64_t * nobs, float64_t * sum_x) nogil:
174
- """ remove a value from the sum calc """
180
+ cdef inline void remove_sum(float64_t val, int64_t * nobs, float64_t * sum_x,
181
+ float64_t * compensation) nogil:
182
+ """ remove a value from the sum calc using Kahan summation """
183
+
184
+ cdef:
185
+ float64_t y, t
175
186
187
+ # Not NaN
176
188
if notnan(val):
177
189
nobs[0 ] = nobs[0 ] - 1
178
- sum_x[0 ] = sum_x[0 ] - val
190
+ y = - val - compensation[0 ]
191
+ t = sum_x[0 ] + y
192
+ compensation[0 ] = t - sum_x[0 ] - y
193
+ sum_x[0 ] = t
179
194
180
195
181
196
def roll_sum_variable (ndarray[float64_t] values , ndarray[int64_t] start ,
182
197
ndarray[int64_t] end , int64_t minp ):
183
198
cdef:
184
- float64_t sum_x = 0
199
+ float64_t sum_x = 0 , compensation_add = 0 , compensation_remove = 0
185
200
int64_t s, e
186
201
int64_t nobs = 0 , i, j, N = len (values)
187
202
ndarray[float64_t] output
@@ -201,31 +216,31 @@ def roll_sum_variable(ndarray[float64_t] values, ndarray[int64_t] start,
201
216
# setup
202
217
203
218
for j in range (s, e):
204
- add_sum(values[j], & nobs, & sum_x)
219
+ add_sum(values[j], & nobs, & sum_x, & compensation_add )
205
220
206
221
else :
207
222
208
223
# calculate deletes
209
224
for j in range (start[i - 1 ], s):
210
- remove_sum(values[j], & nobs, & sum_x)
225
+ remove_sum(values[j], & nobs, & sum_x, & compensation_remove )
211
226
212
227
# calculate adds
213
228
for j in range (end[i - 1 ], e):
214
- add_sum(values[j], & nobs, & sum_x)
229
+ add_sum(values[j], & nobs, & sum_x, & compensation_add )
215
230
216
231
output[i] = calc_sum(minp, nobs, sum_x)
217
232
218
233
if not is_monotonic_bounds:
219
234
for j in range (s, e):
220
- remove_sum(values[j], & nobs, & sum_x)
235
+ remove_sum(values[j], & nobs, & sum_x, & compensation_remove )
221
236
222
237
return output
223
238
224
239
225
240
def roll_sum_fixed (ndarray[float64_t] values , ndarray[int64_t] start ,
226
241
ndarray[int64_t] end , int64_t minp , int64_t win ):
227
242
cdef:
228
- float64_t val, prev_x, sum_x = 0
243
+ float64_t val, prev_x, sum_x = 0 , compensation_add = 0 , compensation_remove = 0
229
244
int64_t range_endpoint
230
245
int64_t nobs = 0 , i, N = len (values)
231
246
ndarray[float64_t] output
@@ -237,16 +252,16 @@ def roll_sum_fixed(ndarray[float64_t] values, ndarray[int64_t] start,
237
252
with nogil:
238
253
239
254
for i in range (0 , range_endpoint):
240
- add_sum(values[i], & nobs, & sum_x)
255
+ add_sum(values[i], & nobs, & sum_x, & compensation_add )
241
256
output[i] = NaN
242
257
243
258
for i in range (range_endpoint, N):
244
259
val = values[i]
245
- add_sum(val, & nobs, & sum_x)
260
+ add_sum(val, & nobs, & sum_x, & compensation_add )
246
261
247
262
if i > win - 1 :
248
263
prev_x = values[i - win]
249
- remove_sum(prev_x, & nobs, & sum_x)
264
+ remove_sum(prev_x, & nobs, & sum_x, & compensation_remove )
250
265
251
266
output[i] = calc_sum(minp, nobs, sum_x)
252
267
@@ -277,32 +292,42 @@ cdef inline float64_t calc_mean(int64_t minp, Py_ssize_t nobs,
277
292
278
293
279
294
cdef inline void add_mean(float64_t val, Py_ssize_t * nobs, float64_t * sum_x,
280
- Py_ssize_t * neg_ct) nogil:
281
- """ add a value from the mean calc """
295
+ Py_ssize_t * neg_ct, float64_t * compensation) nogil:
296
+ """ add a value from the mean calc using Kahan summation """
297
+ cdef:
298
+ float64_t y, t
282
299
283
300
# Not NaN
284
301
if notnan(val):
285
302
nobs[0 ] = nobs[0 ] + 1
286
- sum_x[0 ] = sum_x[0 ] + val
303
+ y = val - compensation[0 ]
304
+ t = sum_x[0 ] + y
305
+ compensation[0 ] = t - sum_x[0 ] - y
306
+ sum_x[0 ] = t
287
307
if signbit(val):
288
308
neg_ct[0 ] = neg_ct[0 ] + 1
289
309
290
310
291
311
cdef inline void remove_mean(float64_t val, Py_ssize_t * nobs, float64_t * sum_x,
292
- Py_ssize_t * neg_ct) nogil:
293
- """ remove a value from the mean calc """
312
+ Py_ssize_t * neg_ct, float64_t * compensation) nogil:
313
+ """ remove a value from the mean calc using Kahan summation """
314
+ cdef:
315
+ float64_t y, t
294
316
295
317
if notnan(val):
296
318
nobs[0 ] = nobs[0 ] - 1
297
- sum_x[0 ] = sum_x[0 ] - val
319
+ y = - val - compensation[0 ]
320
+ t = sum_x[0 ] + y
321
+ compensation[0 ] = t - sum_x[0 ] - y
322
+ sum_x[0 ] = t
298
323
if signbit(val):
299
324
neg_ct[0 ] = neg_ct[0 ] - 1
300
325
301
326
302
327
def roll_mean_fixed (ndarray[float64_t] values , ndarray[int64_t] start ,
303
328
ndarray[int64_t] end , int64_t minp , int64_t win ):
304
329
cdef:
305
- float64_t val, prev_x, sum_x = 0
330
+ float64_t val, prev_x, sum_x = 0 , compensation_add = 0 , compensation_remove = 0
306
331
Py_ssize_t nobs = 0 , i, neg_ct = 0 , N = len (values)
307
332
ndarray[float64_t] output
308
333
@@ -311,16 +336,16 @@ def roll_mean_fixed(ndarray[float64_t] values, ndarray[int64_t] start,
311
336
with nogil:
312
337
for i in range (minp - 1 ):
313
338
val = values[i]
314
- add_mean(val, & nobs, & sum_x, & neg_ct)
339
+ add_mean(val, & nobs, & sum_x, & neg_ct, & compensation_add )
315
340
output[i] = NaN
316
341
317
342
for i in range (minp - 1 , N):
318
343
val = values[i]
319
- add_mean(val, & nobs, & sum_x, & neg_ct)
344
+ add_mean(val, & nobs, & sum_x, & neg_ct, & compensation_add )
320
345
321
346
if i > win - 1 :
322
347
prev_x = values[i - win]
323
- remove_mean(prev_x, & nobs, & sum_x, & neg_ct)
348
+ remove_mean(prev_x, & nobs, & sum_x, & neg_ct, & compensation_remove )
324
349
325
350
output[i] = calc_mean(minp, nobs, neg_ct, sum_x)
326
351
@@ -330,7 +355,7 @@ def roll_mean_fixed(ndarray[float64_t] values, ndarray[int64_t] start,
330
355
def roll_mean_variable (ndarray[float64_t] values , ndarray[int64_t] start ,
331
356
ndarray[int64_t] end , int64_t minp ):
332
357
cdef:
333
- float64_t val, sum_x = 0
358
+ float64_t val, compensation_add = 0 , compensation_remove = 0 , sum_x = 0
334
359
int64_t s, e
335
360
Py_ssize_t nobs = 0 , i, j, neg_ct = 0 , N = len (values)
336
361
ndarray[float64_t] output
@@ -350,26 +375,26 @@ def roll_mean_variable(ndarray[float64_t] values, ndarray[int64_t] start,
350
375
# setup
351
376
for j in range (s, e):
352
377
val = values[j]
353
- add_mean(val, & nobs, & sum_x, & neg_ct)
378
+ add_mean(val, & nobs, & sum_x, & neg_ct, & compensation_add )
354
379
355
380
else :
356
381
357
382
# calculate deletes
358
383
for j in range (start[i - 1 ], s):
359
384
val = values[j]
360
- remove_mean(val, & nobs, & sum_x, & neg_ct)
385
+ remove_mean(val, & nobs, & sum_x, & neg_ct, & compensation_remove )
361
386
362
387
# calculate adds
363
388
for j in range (end[i - 1 ], e):
364
389
val = values[j]
365
- add_mean(val, & nobs, & sum_x, & neg_ct)
390
+ add_mean(val, & nobs, & sum_x, & neg_ct, & compensation_add )
366
391
367
392
output[i] = calc_mean(minp, nobs, neg_ct, sum_x)
368
393
369
394
if not is_monotonic_bounds:
370
395
for j in range (s, e):
371
396
val = values[j]
372
- remove_mean(val, & nobs, & sum_x, & neg_ct)
397
+ remove_mean(val, & nobs, & sum_x, & neg_ct, & compensation_remove )
373
398
return output
374
399
375
400
# ----------------------------------------------------------------------
0 commit comments