running_stats example
Welford's online algorithm — streaming mean and variance over any sequence of real-valued samples. Useful anywhere you need live statistics without storing the full dataset: monitoring, data pipelines, scientific computing, control loops.
Follow along to scaffold, implement, build, and use it yourself.
TL;DR — see it work first
. <(curl -fsSL https://just-buildit.github.io/just-makeit/install.sh)
just-makeit example running_stats
# running_stats: PASSED
Prerequisites
. <(curl -fsSL https://just-buildit.github.io/just-makeit/install.sh)
Pass a custom path to keep the venv somewhere persistent:
. <(curl -fsSL https://just-buildit.github.io/just-makeit/install.sh) -- ~/my-venv
Or with pip if just-makeit is already installed:
pip install just-makeit && just-makeit install-deps
source /tmp/jm-venv/bin/activate
1. Scaffold
just-makeit new my_stats \
--object running_stats \
--state "n:int32_t:0" \
--state "mean:double:0.0" \
--state "m2:double:0.0"
Three state variables — all zero by default, so RunningStats() needs no arguments:
| Name | Type | Role |
|---|---|---|
n |
int32_t |
Sample count |
mean |
double |
Running mean (Welford) |
m2 |
double |
Sum of squared deviations (Welford) |
Variance = m2 / (n - 1) once n > 1.
2. Implement
Open native/inc/running_stats/running_stats_core.h and replace the stub.
The algorithm mutates state, so the signature changes from const to mutable.
The real part of the input is the sample value; the return packs mean into
the real part and sample variance into the imaginary part:
// before
static inline float complex running_stats_step(const running_stats_state_t *state,
float complex x) {
(void)state; /* TODO: implement using state variables */
return x;
}
// after — Welford's online algorithm
// Input: real part = new sample (imaginary part ignored)
// Output: real = current mean, imag = sample variance (0 until n > 1)
static inline float complex running_stats_step(running_stats_state_t *state, float complex x) {
double sample = (double)crealf(x);
state->n++;
double delta = sample - state->mean;
state->mean += delta / (double)state->n;
double delta2 = sample - state->mean;
state->m2 += delta * delta2;
double var = (state->n > 0) ? state->m2 / (double)state->n : 0.0;
return (float)state->mean + (float)var * I;
}
3. Build and test
make
make test
4. Try it from Python
pip install -e .
import numpy as np
from my_stats import RunningStats
# All defaults are 0 — no arguments needed
s = RunningStats()
# Classic Welford test dataset: mean=5, variance=4
data = np.array([2, 4, 4, 4, 5, 5, 7, 9], dtype=np.complex64)
for x in data:
y = s.step(x)
print(f"n: {s.get_n()}") # 8
print(f"mean: {s.get_mean():.4f}") # 5.0000
print(f"variance: {y.imag:.4f}") # 4.0000 (packed into imag of last step)
# reset and try a single-pass block via steps()
s.reset()
y_all = s.steps(data)
print(f"final mean from steps(): {y_all[-1].real:.4f}") # 5.0000
print(f"final var from steps(): {y_all[-1].imag:.4f}") # 4.0000
5. Try it from C
After make, the combined shared library is at build/libmy_stats.so.
// demo.c
#include "running_stats/running_stats_core.h"
#include <complex.h>
#include <stdio.h>
int main(void) {
running_stats_state_t *s = running_stats_create(0, 0.0, 0.0);
float data[] = {2, 4, 4, 4, 5, 5, 7, 9};
float complex y;
for (int i = 0; i < 8; i++)
y = running_stats_step(s, data[i] + 0.0f * I);
printf("n: %d\n", running_stats_get_n(s)); /* 8 */
printf("mean: %.4f\n", running_stats_get_mean(s)); /* 5.0000 */
printf("variance: %.4f\n", (double)cimagf(y)); /* 4.0000 */
running_stats_reset(s);
printf("after reset: n=%d mean=%.1f\n", running_stats_get_n(s),
running_stats_get_mean(s)); /* n=0 mean=0.0 */
running_stats_destroy(s);
return 0;
}
gcc -O2 -std=c99 -Inative/inc demo.c \
-Lbuild -lmy_stats -Wl,-rpath,build \
-lm -o demo && ./demo
6. Add more state
Track the min and max alongside the running statistics:
just-makeit add --state "min_val:double:0.0" --state "max_val:double:0.0"
make test
add regenerates only the state-sensitive files — your running_stats_step
implementation is untouched.