This solution implements a suggestion made by @Innuo in a comment to the question:
You can maintain a uniformly sampled random subset of size 100 or 1000 from all the data seen thus far. This set and the associated "fences" can be updated in $O(1)$ time.
Once we know how to maintain this subset, we may select any method we like to estimate the mean of a population from such a sample. This is a universal method, making no assumptions whatsoever, that will work with any input stream to within an accuracy that can be predicted using standard statistical sampling formulas. (The accuracy is inversely proportional to the square root of the sample size.)
This algorithm accepts as input a stream of data $x(t),$ $t=1, 2, \ldots,$ a sample size $m$, and outputs a stream of samples $s(t)$ each of which represent the population $X(t) = (x(1),x(2), \ldots, x(t))$. Specifically, for $1\le i \le t$, $s(i)$ is a simple random sample of size $m$ from $X(t)$ (without replacement).
For this to happen, it suffices that every $m$-element subset of $\{1,2,\ldots,t\}$ have equal chances of being the indexes of $x$ in $s(t)$. This implies the chance that $x(i),$ $1\le i\lt t,$ is in $s(t)$ equals $m/t$ provided $t \ge m$.
At the beginning we just collect the stream until $m$ elements have been stored. At that point there is only one possible sample, so the probability condition is trivially satisfied.
The algorithm takes over when $t=m+1$. Inductively suppose that $s(t)$ is a simple random sample of $X(t)$ for $t\gt m$. Provisionally set $s(t+1) = s(t)$. Let $U(t+1)$ be a uniform random variable (independent of any previous variables used to construct $s(t)$). If $U(t+1) \le m/(t+1)$ then replace a randomly chosen element of $s$ by $x(t+1)$. That's the entire procedure!
Clearly $x(t+1)$ has probability $m/(t+1)$ of being in $s(t+1)$. Moreover, by the induction hypothesis, $x(i)$ had probability $m/t$ of being in $s(t)$ when $i\le t$. With probability $m/(t+1) \times 1/m$ = $1/(t+1)$ it will have been removed from $s(t+1)$, whence its probability of remaining equals
$$\frac{m}{t}\left(1 - \frac{1}{t+1}\right) = \frac{m}{t+1},$$
exactly as needed. By induction, then, all the inclusion probabilities of the $x(i)$ in the $s(t)$ are correct and it's clear there is no special correlation among those inclusions. That proves the algorithm is correct.
The algorithm efficiency is $O(1)$ because at each stage at most two random numbers are computed and at most one element of an array of $m$ values is replaced. The storage requirement is $O(m)$.
The data structure for this algorithm consists of the sample $s$ together with the index $t$ of the population $X(t)$ that it samples. Initially we take $s = X(m)$ and proceed with the algorithm for $t=m+1, m+2, \ldots.$ Here is an R
implementation to update $(s,t)$ with a value $x$ to produce $(s,t+1)$. (The argument n
plays the role of $t$ and sample.size
is $m$. The index $t$ will be maintained by the caller.)
update <- function(s, x, n, sample.size) {
if (length(s) < sample.size) {
s <- c(s, x)
} else if (runif(1) <= sample.size / n) {
i <- sample.int(length(s), 1)
s[i] <- x
}
return (s)
}
To illustrate and test this, I will use the usual (non-robust) estimator of the mean and compare the mean as estimated from $s(t)$ to the actual mean of $X(t)$ (the cumulative set of data seen at each step). I chose a somewhat difficult input stream that changes quite smoothly but periodically undergoes dramatic jumps. The sample size of $m=50$ is fairly small, allowing us to see sampling fluctuations in these plots.
n <- 10^3
x <- sapply(1:(7*n), function(t) cos(pi*t/n) + 2*floor((1+t)/n))
n.sample <- 50
s <- x[1:(n.sample-1)]
online <- sapply(n.sample:length(x), function(i) {
s <<- update(s, x[i], i, n.sample)
summary(s)})
actual <- sapply(n.sample:length(x), function(i) summary(x[1:i]))
At this point online
is the sequence of mean estimates produced by maintaining this running sample of $50$ values while actual
is the sequence of mean estimates produced from all the data available at each moment. The plot shows the data (in gray), actual
(in black), and two independent applications of this sampling procedure (in colors). The agreement is within expected sampling error:
plot(x, pch=".", col="Gray")
lines(1:dim(actual)[2], actual["Mean", ])
lines(1:dim(online)[2], online["Mean", ], col="Red")
For robust estimators of the mean, please search our site for outlier and related terms. Among the possibilities worth considering are Winsorized means and M-estimators.