code wiki / _hdl_build / nx_timeseries.nx

nx_timeseries.nx source

↩ module page · 79 lines · 2915 B

1// nx_timeseries.nx -- LIB: TIME-SERIES analytics over an ordered i64 column (rounds out the analytics 2// primitive set: aggregation + profiling + correlation + NOW time). Windowed rolling mean, least-squares 3// LINEAR TREND (slope + direction class), and overall growth. Integer/fixed-point (bit-reproducible). 4// Composes nx_dataframe. license_tier: ORIGINAL 5import "nx_syscalls.nx" 6import "_hdl_build/nx_dataframe.nx" 7 8const TS_FLAT: i64 = 0 9const TS_RISING: i64 = 1 10const TS_FALLING: i64 = 2 11 12// rolling (trailing) mean of window w into out[0..n): out[i] = mean(col[max(0,i-w+1)..i]). out must hold n. 13func ts_rolling_mean(col: *i64, n: i64, w: i64, out: *i64) -> i64 { 14 if w < 1 { return 0 - 1 } 15 var i: i64 = 0 16 while i < n { 17 var lo: i64 = i - w + 1 18 if lo < 0 { lo = 0 } 19 var s: i64 = 0 20 var j: i64 = lo 21 while j <= i { s = s + col[j]; j = j + 1 } 22 let cnt: i64 = i - lo + 1 23 out[i] = s / cnt 24 i = i + 1 25 } 26 return 0 27} 28 29// least-squares linear trend of col vs index (0..n-1). Writes slope*1000 to out_slope_milli and the integer 30// intercept to out_intercept. slope = (n*Sxy - Sx*Sy)/(n*Sxx - Sx^2), x=index. Returns 0 ok, -1 degenerate. 31func ts_linear_trend(col: *i64, n: i64, out_slope_milli: *i64, out_intercept: *i64) -> i64 { 32 if n < 2 { out_slope_milli[0] = 0; out_intercept[0] = 0; return 0 - 1 } 33 var sx: i64 = 0 34 var sy: i64 = 0 35 var sxy: i64 = 0 36 var sxx: i64 = 0 37 var i: i64 = 0 38 while i < n { 39 sx = sx + i 40 sy = sy + col[i] 41 sxy = sxy + i * col[i] 42 sxx = sxx + i * i 43 i = i + 1 44 } 45 let denom: i64 = n * sxx - sx * sx 46 if denom == 0 { out_slope_milli[0] = 0; out_intercept[0] = 0; return 0 - 1 } 47 let num: i64 = n * sxy - sx * sy 48 out_slope_milli[0] = (num * 1000) / denom 49 // intercept = (sy - slope*sx)/n ; use slope_milli to keep precision then /1000 50 let sm: i64 = out_slope_milli[0] 51 out_intercept[0] = (sy * 1000 - sm * sx) / (n * 1000) 52 return 0 53} 54 55// classify a slope_milli into direction. `flat_band_milli` = |slope*1000| below which we call it flat. 56func ts_trend_class(slope_milli: i64, flat_band_milli: i64) -> i64 { 57 var a: i64 = slope_milli 58 if a < 0 { a = 0 - a } 59 if a <= flat_band_milli { return TS_FLAT } 60 if slope_milli > 0 { return TS_RISING } 61 return TS_FALLING 62} 63 64func ts_class_str(cls: i64) -> *u8 { 65 if cls == TS_RISING { return "rising" as *u8 } 66 if cls == TS_FALLING { return "falling" as *u8 } 67 return "flat" as *u8 68} 69 70// overall growth in permil from the first value to the last: (last-first)*1000/|first|. first==0 -> sentinel. 71func ts_growth_permil(col: *i64, n: i64) -> i64 { 72 if n < 2 { return 0 } 73 let first: i64 = col[0] 74 let last: i64 = col[n - 1] 75 if first == 0 { return 0 - 1 } 76 var f: i64 = first 77 if f < 0 { f = 0 - f } 78 return ((last - first) * 1000) / f 79}