2
2
3
3
import cython
4
4
5
+ from libc.math cimport round
5
6
from libcpp.deque cimport deque
6
7
7
8
import numpy as np
@@ -458,51 +459,92 @@ cdef inline float64_t calc_skew(int64_t minp, int64_t nobs,
458
459
459
460
cdef inline void add_skew(float64_t val, int64_t * nobs,
460
461
float64_t * x, float64_t * xx,
461
- float64_t * xxx) nogil:
462
+ float64_t * xxx,
463
+ float64_t * compensation_x,
464
+ float64_t * compensation_xx,
465
+ float64_t * compensation_xxx) nogil:
462
466
""" add a value from the skew calc """
467
+ cdef:
468
+ float64_t y, t
463
469
464
470
# Not NaN
465
471
if notnan(val):
466
472
nobs[0 ] = nobs[0 ] + 1
467
473
468
- # seriously don't ask me why this is faster
469
- x[0 ] = x[0 ] + val
470
- xx[0 ] = xx[0 ] + val * val
471
- xxx[0 ] = xxx[0 ] + val * val * val
474
+ y = val - compensation_x[0 ]
475
+ t = x[0 ] + y
476
+ compensation_x[0 ] = t - x[0 ] - y
477
+ x[0 ] = t
478
+ y = val * val - compensation_xx[0 ]
479
+ t = xx[0 ] + y
480
+ compensation_xx[0 ] = t - xx[0 ] - y
481
+ xx[0 ] = t
482
+ y = val * val * val - compensation_xxx[0 ]
483
+ t = xxx[0 ] + y
484
+ compensation_xxx[0 ] = t - xxx[0 ] - y
485
+ xxx[0 ] = t
472
486
473
487
474
488
cdef inline void remove_skew(float64_t val, int64_t * nobs,
475
489
float64_t * x, float64_t * xx,
476
- float64_t * xxx) nogil:
490
+ float64_t * xxx,
491
+ float64_t * compensation_x,
492
+ float64_t * compensation_xx,
493
+ float64_t * compensation_xxx) nogil:
477
494
""" remove a value from the skew calc """
495
+ cdef:
496
+ float64_t y, t
478
497
479
498
# Not NaN
480
499
if notnan(val):
481
500
nobs[0 ] = nobs[0 ] - 1
482
501
483
- # seriously don't ask me why this is faster
484
- x[0 ] = x[0 ] - val
485
- xx[0 ] = xx[0 ] - val * val
486
- xxx[0 ] = xxx[0 ] - val * val * val
502
+ y = - val - compensation_x[0 ]
503
+ t = x[0 ] + y
504
+ compensation_x[0 ] = t - x[0 ] - y
505
+ x[0 ] = t
506
+ y = - val * val - compensation_xx[0 ]
507
+ t = xx[0 ] + y
508
+ compensation_xx[0 ] = t - xx[0 ] - y
509
+ xx[0 ] = t
510
+ y = - val * val * val - compensation_xxx[0 ]
511
+ t = xxx[0 ] + y
512
+ compensation_xxx[0 ] = t - xxx[0 ] - y
513
+ xxx[0 ] = t
487
514
488
515
489
516
def roll_skew (ndarray[float64_t] values , ndarray[int64_t] start ,
490
517
ndarray[int64_t] end , int64_t minp ):
491
518
cdef:
492
- float64_t val, prev
519
+ float64_t val, prev, min_val, mean_val, sum_val = 0
520
+ float64_t compensation_xxx_add = 0 , compensation_xxx_remove = 0
521
+ float64_t compensation_xx_add = 0 , compensation_xx_remove = 0
522
+ float64_t compensation_x_add = 0 , compensation_x_remove = 0
493
523
float64_t x = 0 , xx = 0 , xxx = 0
494
- int64_t nobs = 0 , i, j, N = len (values)
524
+ int64_t nobs = 0 , i, j, N = len (values), nobs_mean = 0
495
525
int64_t s, e
496
- ndarray[float64_t] output
526
+ ndarray[float64_t] output, mean_array
497
527
bint is_monotonic_increasing_bounds
498
528
499
529
minp = max (minp, 3 )
500
530
is_monotonic_increasing_bounds = is_monotonic_increasing_start_end_bounds(
501
531
start, end
502
532
)
503
533
output = np.empty(N, dtype = float )
534
+ min_val = np.nanmin(values)
504
535
505
536
with nogil:
537
+ for i in range (0 , N):
538
+ val = values[i]
539
+ if notnan(val):
540
+ nobs_mean += 1
541
+ sum_val += val
542
+ mean_val = sum_val / nobs_mean
543
+ # Other cases would lead to imprecision for smallest values
544
+ if min_val - mean_val > - 1e5 :
545
+ mean_val = round (mean_val)
546
+ for i in range (0 , N):
547
+ values[i] = values[i] - mean_val
506
548
507
549
for i in range (0 , N):
508
550
@@ -515,22 +557,24 @@ def roll_skew(ndarray[float64_t] values, ndarray[int64_t] start,
515
557
516
558
for j in range (s, e):
517
559
val = values[j]
518
- add_skew(val, & nobs, & x, & xx, & xxx)
560
+ add_skew(val, & nobs, & x, & xx, & xxx, & compensation_x_add,
561
+ & compensation_xx_add, & compensation_xxx_add)
519
562
520
563
else :
521
564
522
565
# After the first window, observations can both be added
523
566
# and removed
567
+ # calculate deletes
568
+ for j in range (start[i - 1 ], s):
569
+ val = values[j]
570
+ remove_skew(val, & nobs, & x, & xx, & xxx, & compensation_x_remove,
571
+ & compensation_xx_remove, & compensation_xxx_remove)
524
572
525
573
# calculate adds
526
574
for j in range (end[i - 1 ], e):
527
575
val = values[j]
528
- add_skew(val, & nobs, & x, & xx, & xxx)
529
-
530
- # calculate deletes
531
- for j in range (start[i - 1 ], s):
532
- val = values[j]
533
- remove_skew(val, & nobs, & x, & xx, & xxx)
576
+ add_skew(val, & nobs, & x, & xx, & xxx, & compensation_x_add,
577
+ & compensation_xx_add, & compensation_xxx_add)
534
578
535
579
output[i] = calc_skew(minp, nobs, x, xx, xxx)
536
580
@@ -585,42 +629,80 @@ cdef inline float64_t calc_kurt(int64_t minp, int64_t nobs,
585
629
586
630
cdef inline void add_kurt(float64_t val, int64_t * nobs,
587
631
float64_t * x, float64_t * xx,
588
- float64_t * xxx, float64_t * xxxx) nogil:
632
+ float64_t * xxx, float64_t * xxxx,
633
+ float64_t * compensation_x,
634
+ float64_t * compensation_xx,
635
+ float64_t * compensation_xxx,
636
+ float64_t * compensation_xxxx) nogil:
589
637
""" add a value from the kurotic calc """
638
+ cdef:
639
+ float64_t y, t
590
640
591
641
# Not NaN
592
642
if notnan(val):
593
643
nobs[0 ] = nobs[0 ] + 1
594
644
595
- # seriously don't ask me why this is faster
596
- x[0 ] = x[0 ] + val
597
- xx[0 ] = xx[0 ] + val * val
598
- xxx[0 ] = xxx[0 ] + val * val * val
599
- xxxx[0 ] = xxxx[0 ] + val * val * val * val
645
+ y = val - compensation_x[0 ]
646
+ t = x[0 ] + y
647
+ compensation_x[0 ] = t - x[0 ] - y
648
+ x[0 ] = t
649
+ y = val * val - compensation_xx[0 ]
650
+ t = xx[0 ] + y
651
+ compensation_xx[0 ] = t - xx[0 ] - y
652
+ xx[0 ] = t
653
+ y = val * val * val - compensation_xxx[0 ]
654
+ t = xxx[0 ] + y
655
+ compensation_xxx[0 ] = t - xxx[0 ] - y
656
+ xxx[0 ] = t
657
+ y = val * val * val * val - compensation_xxxx[0 ]
658
+ t = xxxx[0 ] + y
659
+ compensation_xxxx[0 ] = t - xxxx[0 ] - y
660
+ xxxx[0 ] = t
600
661
601
662
602
663
cdef inline void remove_kurt(float64_t val, int64_t * nobs,
603
664
float64_t * x, float64_t * xx,
604
- float64_t * xxx, float64_t * xxxx) nogil:
665
+ float64_t * xxx, float64_t * xxxx,
666
+ float64_t * compensation_x,
667
+ float64_t * compensation_xx,
668
+ float64_t * compensation_xxx,
669
+ float64_t * compensation_xxxx) nogil:
605
670
""" remove a value from the kurotic calc """
671
+ cdef:
672
+ float64_t y, t
606
673
607
674
# Not NaN
608
675
if notnan(val):
609
676
nobs[0 ] = nobs[0 ] - 1
610
677
611
- # seriously don't ask me why this is faster
612
- x[0 ] = x[0 ] - val
613
- xx[0 ] = xx[0 ] - val * val
614
- xxx[0 ] = xxx[0 ] - val * val * val
615
- xxxx[0 ] = xxxx[0 ] - val * val * val * val
678
+ y = - val - compensation_x[0 ]
679
+ t = x[0 ] + y
680
+ compensation_x[0 ] = t - x[0 ] - y
681
+ x[0 ] = t
682
+ y = - val * val - compensation_xx[0 ]
683
+ t = xx[0 ] + y
684
+ compensation_xx[0 ] = t - xx[0 ] - y
685
+ xx[0 ] = t
686
+ y = - val * val * val - compensation_xxx[0 ]
687
+ t = xxx[0 ] + y
688
+ compensation_xxx[0 ] = t - xxx[0 ] - y
689
+ xxx[0 ] = t
690
+ y = - val * val * val * val - compensation_xxxx[0 ]
691
+ t = xxxx[0 ] + y
692
+ compensation_xxxx[0 ] = t - xxxx[0 ] - y
693
+ xxxx[0 ] = t
616
694
617
695
618
696
def roll_kurt (ndarray[float64_t] values , ndarray[int64_t] start ,
619
697
ndarray[int64_t] end , int64_t minp ):
620
698
cdef:
621
- float64_t val, prev
699
+ float64_t val, prev, mean_val, min_val, sum_val = 0
700
+ float64_t compensation_xxxx_add = 0 , compensation_xxxx_remove = 0
701
+ float64_t compensation_xxx_remove = 0 , compensation_xxx_add = 0
702
+ float64_t compensation_xx_remove = 0 , compensation_xx_add = 0
703
+ float64_t compensation_x_remove = 0 , compensation_x_add = 0
622
704
float64_t x = 0 , xx = 0 , xxx = 0 , xxxx = 0
623
- int64_t nobs = 0 , i, j, s, e, N = len (values)
705
+ int64_t nobs = 0 , i, j, s, e, N = len (values), nobs_mean = 0
624
706
ndarray[float64_t] output
625
707
bint is_monotonic_increasing_bounds
626
708
@@ -629,8 +711,20 @@ def roll_kurt(ndarray[float64_t] values, ndarray[int64_t] start,
629
711
start, end
630
712
)
631
713
output = np.empty(N, dtype = float )
714
+ min_val = np.nanmin(values)
632
715
633
716
with nogil:
717
+ for i in range (0 , N):
718
+ val = values[i]
719
+ if notnan(val):
720
+ nobs_mean += 1
721
+ sum_val += val
722
+ mean_val = sum_val / nobs_mean
723
+ # Other cases would lead to imprecision for smallest values
724
+ if min_val - mean_val > - 1e4 :
725
+ mean_val = round (mean_val)
726
+ for i in range (0 , N):
727
+ values[i] = values[i] - mean_val
634
728
635
729
for i in range (0 , N):
636
730
@@ -642,20 +736,25 @@ def roll_kurt(ndarray[float64_t] values, ndarray[int64_t] start,
642
736
if i == 0 or not is_monotonic_increasing_bounds:
643
737
644
738
for j in range (s, e):
645
- add_kurt(values[j], & nobs, & x, & xx, & xxx, & xxxx)
739
+ add_kurt(values[j], & nobs, & x, & xx, & xxx, & xxxx,
740
+ & compensation_x_add, & compensation_xx_add,
741
+ & compensation_xxx_add, & compensation_xxxx_add)
646
742
647
743
else :
648
744
649
745
# After the first window, observations can both be added
650
746
# and removed
747
+ # calculate deletes
748
+ for j in range (start[i - 1 ], s):
749
+ remove_kurt(values[j], & nobs, & x, & xx, & xxx, & xxxx,
750
+ & compensation_x_remove, & compensation_xx_remove,
751
+ & compensation_xxx_remove, & compensation_xxxx_remove)
651
752
652
753
# calculate adds
653
754
for j in range (end[i - 1 ], e):
654
- add_kurt(values[j], & nobs, & x, & xx, & xxx, & xxxx)
655
-
656
- # calculate deletes
657
- for j in range (start[i - 1 ], s):
658
- remove_kurt(values[j], & nobs, & x, & xx, & xxx, & xxxx)
755
+ add_kurt(values[j], & nobs, & x, & xx, & xxx, & xxxx,
756
+ & compensation_x_add, & compensation_xx_add,
757
+ & compensation_xxx_add, & compensation_xxxx_add)
659
758
660
759
output[i] = calc_kurt(minp, nobs, x, xx, xxx, xxxx)
661
760
0 commit comments