The average filter estimates a fixed but unknown quantity from a sequence of noisy measurements of it, by continuously updating the mean of everything measured so far. A typical use is sensor initialization, such as finding the zero point of an electronic weighing scale: the true reading with nothing on the scale is some constant, but every individual measurement is corrupted by noise, and averaging many of them cancels that noise out. Recomputing the full average from scratch after every new reading would waste both time and memory, so the filter is usually written in a recursive form that folds each new reading into the running mean directly.
From the Batch Mean to a Recursive Update
Start from the ordinary average of the first \(k\) measurements \(x_1,x_2,\dots,x_k\):
\[ \overline{x}_k = \frac{x_1+x_2+\dots+x_k}{k}. \]
Multiplying both sides by \(k\) isolates the running sum:
\[ k\overline{x}_k = x_1+x_2+\dots+x_k. \]
Dividing both sides by \(k-1\) instead, and splitting off the last term of the sum, relates this to the average of the first \(k-1\) measurements:
\[ \begin{array}{rl} \dfrac{k}{k-1}\overline{x}_k &= \dfrac{x_1+x_2+\dots+x_k}{k-1}\\[6pt] &= \dfrac{x_1+x_2+\dots+x_{k-1}}{k-1} + \dfrac{x_k}{k-1}\\[6pt] &= \overline{x}_{k-1} + \dfrac{x_k}{k-1}. \end{array} \]
Multiplying through by \(\frac{k-1}{k}\) gives the recursive update rule: the new average is a weighted blend of the old average and the new sample,
\[ \overline{x}_k = \frac{k-1}{k}\overline{x}_{k-1} + \frac{1}{k}x_k. \]
Only the previous average \(\overline{x}_{k-1}\) and the sample count \(k\) need to be kept between updates; none of the individual past measurements have to be stored.
Relation to the Exponential Moving Average
Writing \(\alpha := \frac{k-1}{k} = 1-\frac{1}{k}\), so that \(\frac{1}{k}=1-\alpha\), turns the update into the familiar form of a first-order low-pass filter,
\[ \overline{x}_k = \alpha\,\overline{x}_{k-1} + (1-\alpha)x_k. \]
An exponential moving average uses exactly this recurrence, but with a fixed coefficient \(\alpha\) chosen once and reused at every step, rather than the increasing sequence \(\alpha_k=\frac{k-1}{k}\). The two behave very differently. Assume the measurements are independent with a common true mean \(\mu\) and variance \(\sigma^2\). For the growing-window average, \(\overline{x}_k=\frac{1}{k}\sum_{i=1}^k x_i\) is an average of \(k\) independent terms, so
\[ \operatorname{Var}(\overline{x}_k) = \frac{\sigma^2}{k} \xrightarrow{k\to\infty} 0, \]
matching the Law of Large Numbers: the estimate converges to \(\mu\) and its uncertainty shrinks without bound as more readings accumulate. The fixed-\(\alpha\) filter never reaches this state. Once its own transient has died out, unrolling the recurrence expresses the current output as \(y_k=(1-\alpha)\sum_{i=0}^{\infty}\alpha^i x_{k-i}\), and since the \(x_{k-i}\) are independent,
\[ \operatorname{Var}(y_k) = (1-\alpha)^2\sigma^2\sum_{i=0}^{\infty}\alpha^{2i} = \frac{(1-\alpha)^2}{1-\alpha^2}\sigma^2 = \frac{1-\alpha}{1+\alpha}\sigma^2, \]
a constant that never goes to zero no matter how long the filter runs, though it can be made arbitrarily small by choosing \(\alpha\) close to \(1\) at the cost of reacting more slowly to a genuine change in the input.
Why the Growing-Window Average Eventually Stops Adapting
The growing-window weight on a new sample is \(\frac{1}{k}\), which shrinks toward zero as \(k\) grows. This is exactly why its variance vanishes and why it is well suited to estimating a quantity that is genuinely constant, such as a sensor's zero offset: every additional reading is given proportionally less influence, and the estimate settles down rather than continuing to jitter. The same property is a liability once the underlying quantity is allowed to drift over time, because a single new, more relevant sample eventually carries almost no weight against the accumulated history. A filter meant to track a changing signal instead needs every incoming sample to keep a roughly constant influence, which is what a moving average over a fixed window or a fixed-\(\alpha\) exponential filter provide, each trading away the growing-window filter's eventual zero variance for the ability to keep following the input.
Software Implementation
Translating the recursion directly into code, the loop counter doubles as the sample count \(k\), and the current value of \(\overline{x}_{k-1}\) is simply overwritten in place at every step:
let x = read_value() // x holds the average of 1 sample after the first reading
for (let k = 2; ; k++) {
let alpha = (k - 1) / k
x = alpha * x + (1 - alpha) * read_value()
write_value(x)
} The loop starts at \(k=2\) rather than \(k=1\): the very first reading is already the entire average of one sample, \(\overline{x}_1=x_1\), so it is assigned directly and the recursive update only has work to do from the second reading onward. Starting the loop at \(k=1\) would recompute \(\alpha=\frac{k-1}{k}=0\) and silently discard the value just read, wasting a measurement without changing the result.
Convergence in Practice
Measuring a constant 5V reading with a standard deviation of 3V of noise over 60 samples shows the estimate settling toward the true value as \(k\) grows and its own weight \(\frac{1}{k}\) shrinks. The grey dots are the raw noisy readings, the red curve is the running average, and the dashed blue line marks the true value the filter converges toward.
Key Results
- The recursive average \(\overline{x}_k=\frac{k-1}{k}\overline{x}_{k-1}+\frac{1}{k}x_k\) is algebraically identical to the batch mean of all samples seen so far, but needs only the previous average and a sample count in memory.
- Writing \(\alpha=\frac{k-1}{k}\) casts it as a low-pass filter with a time-varying coefficient, distinct from a fixed-\(\alpha\) exponential moving average.
- Its variance is \(\sigma^2/k\), vanishing as \(k\to\infty\) by the Law of Large Numbers, whereas a fixed-\(\alpha\) filter keeps a constant residual variance \(\frac{1-\alpha}{1+\alpha}\sigma^2\).
- The shrinking weight \(\frac{1}{k}\) that makes the filter converge on a constant quantity is also what makes it unable to track a quantity that changes over time.
- The loop should start at \(k=2\), since \(k=1\) trivially equals the first sample and a naive loop starting at \(k=1\) recomputes and discards it.