Validación y Análisis · Tema 25

Cálculo recursivo de media y varianza

Un millón de réplicas no caben en memoria ni deben: Welford actualiza media y varianza dato a dato, estable y en vivo.

01 · Punto de partida

Simular en streaming, no en planilla

Una simulación larga escupe miles de observaciones por segundo. Guardarlas todas para promediar al final gasta memoria, impide ver en vivo y rompe con el primer overflow. La salida profesional: actualizar (n, media, M2) con cada dato y tirar el dato.

  • ¿Cómo actualizar la media sin la suma total?
  • ¿Cómo llevar la varianza sin el historial?
  • ¿Por qué la fórmula ingenua explota en flotantes?
  • ¿Cómo combinar dos bloques ya resumidos?

02 · Definición

Estado mínimo y exacto

n

Conteo

Cuántos datos entraron. Sin n no hay promedio ni grados (n−1).

m

Media viva

El promedio de lo visto hasta ahora. Cada dato la tira un 1/n hacia él.

M2

Suma centrada

Σ(x−m)² acumulada sin guardar x. Dividida entre n−1 da s² (Tema 24).

Ingenuo frente a Welford.
AspectoIngenuo Σx, Σx²Welford
MemoriaO(1) pero sumas gigantesO(1): n, m, M2
EstabilidadCatástrofe por cancelaciónEstable (diferencias pequeñas)
En vivoSí, pero riesgosoSí: media + s + EE por paso

03 · Método

Algoritmo en 4 líneas

  1. 1
    Inicializar (0, 0, 0).

    n = 0, media = 0, M2 = 0. Sin datos no hay estadístico.

  2. 2
    Por cada x: actualizar.

    n += 1; δ = x − media; media += δ/n; M2 += δ·(x − media). Notar los dos «media» distintos.

  3. 3
    Leer s² = M2/(n−1).

    Con n = 1, s² indefinida (un punto no dispersa). Con n ≥ 2, insesgada.

  4. 4
    Combinar bloques (Chan).

    Dos resúmenes (n₁,m₁,M2₁) + (n₂,m₂,M2₂) se fusionan sin los datos: ideal para paralelo y réplicas.

(n, m, M2)→+ x→(n+1, m′, M2′)→s² + EE

04 · Estabilidad

Por qué lo ingenuo explota

Ingenuo

Σx² − (Σx)²/n

Resta dos gigantes casi iguales: con datos ~10⁶ la cancelación devora los decimales y hasta da varianza negativa.

Welford

Diferencias pequeñas

Opera con (x − media), números chicos y centrados. El error no crece con la magnitud de x.

Fusión de Chan
δ = m₂−m₁; m = (n₁m₁+n₂m₂)/N; M2 = M2₁+M2₂+δ²n₁n₂/N. Réplicas en paralelo sin juntar datos.
Ventana y calentamiento
Welford puro recuerda todo. Para estado estacionario (Temas 30–33) reiniciar tras el transitorio o usar ventana móvil.
s vs. EE en vivo
Por paso: s = √(M2/(n−1)), EE = s/√n. Parar cuando EE ≤ precisión (Tema 27).

05 · Representación en Python

Welford en 12 líneas, exacto

Python en tu navegador. Compara contra el cálculo por lotes: idéntico hasta el decimal 12.

xs = [11, 13, 12, 14, 12, 13]
n, media, M2 = 0, 0.0, 0.0
for x in xs:
    n += 1
    delta = x - media
    media += delta / n
    M2 += delta * (x - media)
s2 = M2 / (n - 1)
import math
print(f"recursivo: media={media:.4f} s²={s2:.4f} s={math.sqrt(s2):.4f}")
m2 = sum(xs)/len(xs)
print(f"lotes:     media={m2:.4f} s²={sum((v-m2)**2 for v in xs)/(len(xs)-1):.4f}")

Idénticos. La diferencia está en memoria O(1) y estabilidad, no en el resultado.

Clase reutilizable + fusión

class RunningStats:
    def __init__(self):
        self.n, self.m, self.M2 = 0, 0.0, 0.0
    def add(self, x):
        self.n += 1
        d = x - self.m
        self.m += d / self.n
        self.M2 += d * (x - self.m)
    @property
    def var(self):
        return self.M2 / (self.n - 1) if self.n > 1 else float("nan")

a, b = RunningStats(), RunningStats()
for x in [11, 13, 12]:
    a.add(x)
for x in [14, 12, 13]:
    b.add(x)
print(f"A:{a.m:.2f} B:{b.m:.2f} (fusionables por Chan sin los datos)")

06 · Exploración

Laboratorio: la media aprende en vivo

Flujo Normal(12, ruido). La curva lima es tu media recursiva paso a paso; la banda, ±2·EE en vivo (se angosta con √n). Mové el n (hasta dónde mirar) y el ruido. Los puntos son los datos que nunca guardás.

EXPERIMENTO 25

Welford en streaming

mₙ += (xₙ−m)/n · EE = s/√n en vivo

Los resultados numéricos aparecen debajo.
Media final0,00
s final0,00
EE final0,00
Dif. vs. lotes0,00

Con n=80, la media recursiva coincide con lotes y la banda es angosta.

Verde punteada: μ=12. Lima: media viva. La banda se cierra como 1/√n: el aprendizaje es rápido al inicio y lento después.

Preguntas para explorar

  1. Con n=80 y ruido 2: ¿media, s y EE finales? ¿Dif. contra lotes?
  2. Bajá a n=12: ¿ancho de la banda al final? ¿Qué enseña sobre parar temprano (Tema 27)?
  3. Subí el ruido a 4 con n=80: ¿la media converge igual? ¿Qué precio paga en EE?
Ver respuestas sugeridas
  1. Media ≈ 12 ± 0,2, s ≈ 2, dif ≈ 0,000000: idéntica a lotes. La banda ±2·EE ≈ ±0,45.
  2. Banda ≈ ±1,2: ancha e inútil para decidir. Parar con EE grande es dictaminar a ciegas.
  3. Converge igual (insesgada) pero EE ≈ 0,45 (doble): mismo centro, mitad de confianza. El ruido se paga en n.

07 · Comprensión

Confusiones frecuentes

«Recursivo es aproximado»

No: es algebraicamente idéntico a lotes (salvo redondeo, donde es mejor). La diferencia es memoria y estabilidad, no exactitud.

«Guardo todo por si acaso»

Guardar millones de flotantes «por si» impide el en vivo y revienta memoria. Guardar (n, m, M2) + muestras de auditoría alcanza.

«M2/(n) o /(n−1) da igual»

Para s² inferencial es /(n−1) (Tema 24). En streaming el error se arrastra millones de pasos: hacerlo bien desde el inicio.

«Welford olvida el pasado»

No olvida nada: lo resume exactamente. Para olvidar (ventanas, calentamiento) usar variantes con decaimiento, declaradas.

08 · Práctica guiada

Ejercicios con Python

Ejercicio 1: un paso a mano

Con m=12, n=4 llega x=16. ¿m′ y aporte a M2?

m, n, x = 12.0, 4, 16
mp = m + (x-m)/(n+1)
print(f"m'={mp:.2f} aporte={(x-m)*(x-mp):.2f}")
Ver solución razonada

m′ = 12,80, aporte = 12,80 a M2. El dato tira la media 0,8 hacia él y suma su «sorpresa» bilateral.

Ejercicio 2: ingenuo que explota

¿Qué le pasa a Σx² − (Σx)²/n con datos ~10⁶?

xs = [1_000_001, 1_000_002, 1_000_003]
s1, s2 = sum(xs), sum(x*x for x in xs)
print("ingenuo:", s2 - s1*s1/len(xs), "(redondeo peligroso)")
print("Welford: 2.0 exacto y estable")
Ver solución

El ingenuo resta ~3×10¹² − ~3×10¹² y el redondeo devora; puede dar hasta negativo. Welford opera con ±1: exacto.

Ejercicio 3: EE en vivo para parar

¿Cuándo parar si se exige EE ≤ 0,2 con s ≈ 2?

Ver una posible respuesta
import math
print("n ≈ (2/0.2)² =", int((2/0.2)**2), "(Tema 27: parar por precisión)")

n ≈ 100: el monitor en vivo con Welford detiene la simulación al alcanzar la precisión, sin desperdiciar réplicas.

09 · Síntesis

Ideas para recordar

  • Estado (n, m, M2): memoria O(1), exactitud de lotes, estabilidad superior.
  • m += δ/n, M2 += δ·(x−m′), s² = M2/(n−1), EE = s/√n en vivo.
  • Fusión de Chan para paralelo sin juntar datos.
  • Parar por EE (Tema 27), reiniciar tras calentamiento (Temas 30–31).
  • Nunca Σx² − (Σx)²/n en producción.

En el próximo tema usaremos ese EE vivo para los intervalos de confianza.