Algoritmos para calcular la varianza

De Wikipedia, la enciclopedia libre

Los algoritmos para calcular la varianza juegan un papel importante en la estadística informática. Una dificultad clave en el diseño de buenos algoritmos para este problema es que las fórmulas de la varianza pueden incluir sumas de cuadrados, lo que puede llevar a problemas de inestabilidad numérica así como a rebosamiento cuando se trata de valores grandes.

Algoritmo "ingenuo"[editar]

Una fórmula para calcular la varianza de una población completa de tamaño N es:

Usando la corrección de Bessel para calcular una estimación no sesgada de la varianza poblacional de una muestra finita de observaciones n, la fórmula es:

Por lo tanto, un algoritmo "ingenuo" para calcular la varianza estimada viene dado por el procedimiento:

  • Let n ← 0, Sum ← 0, SumSq ← 0
  • For each datum x:
    • nn + 1
    • Sum ← Sum + x
    • SumSq ← SumSq + x × x
  • Var = (SumSq − (Sum × Sum) / n) / (n − 1)

Este algoritmo se puede adaptar fácilmente para calcular la varianza de una población finita: simplemente, basta con dividr entre N en lugar de entre n - 1 en la última línea.

Como SumSq y (Sum×Sum)/n pueden ser números muy similares, cancelación de los restos puede llevar a que la precisión del resultado sea mucho menor que la precisión inherente de coma flotante utilizada para realizar el cálculo. Por lo tanto, se ha propuesto este algoritmo no debe utilizarse en la práctica.[1][2]​ y varios algoritmos alternativos, numéricamente estables.[3]​ Esto es particularmente malo si la desviación estándar es pequeña en relación con la media. Sin embargo, el algoritmo puede mejorarse adoptando el método de la media falsa.

Cálculo de datos desplazados[editar]

Podemos usar una propiedad de la varianza para evitar la cancelación catastrófica en esta fórmula, a saber, la varianza es invariante con respecto a los cambios respecto a un parámetro de localización

Siendo una constante cualquiera, se obtiene la nueva fórmula:

Cuanto más cerca esté del valor medio, más preciso será el resultado, pero simplemente eligiendo un valor dentro del rango de muestras se garantizará la estabilidad deseada. Si los valores son pequeños, entonces no hay problemas con la suma de sus cuadrados, por el contrario, si son grandes, necesariamente significa que la varianza también es grande. En cualquier caso, el segundo término en la fórmula es siempre más pequeño que el primero, por lo que no puede producirse ninguna cancelación.[2]

Si se toma solo la primera muestra como , el algoritmo se puede escribir en el lenguaje de programación Python como

def shifted_data_variance(data):
   if len(data) < 2:
      return 0.0
   K = data[0]
   n = Ex = Ex2 = 0.0
   for x in data:
      n = n + 1
      Ex += x - K
      Ex2 += (x - K) * (x - K)
   variance = (Ex2 - (Ex * Ex)/n)/(n - 1)
   # use n instead of (n-1) if want to compute the exact variance of the given data
   # use (n-1) if data are samples of a larger population
   return variance

esta fórmula facilita también el cálculo incremental, que puede expresarse como

K = n = Ex = Ex2 = 0.0

def add_variable (x):
     si (n ==0):
       K = x
     n = n + 1
     Ex + = x - K
     Ex2 + = (x - K) * (x - K)

def remove_variable (x):
     n = n - 1
     Ex - = (x - K)
     Ex2 - = (x - K) * (x - K)

def get_meanvalue ():
     volver K + Ex / n

def get_variance ():
     retorno (Ex2 - (Ex * Ex) / n) / (n-1)

Algoritmo de dos pasos[editar]

Una aproximación alternativa, usando una fórmula diferente para la varianza, computa primero la media de la muestra,

y después computa la suma de los cuadrados de las diferencias con la media,

donde s es la desviación estándar. Este planteamiento viene dado por el siguiente pseudocódigo:

def two_pass_variance(data):
    n = sum1 = sum2 = 0

    for x in data:
        n += 1
        sum1 += x

    mean = sum1 / n

    for x in data:
        sum2 += (x - mean)*(x - mean)

    variance = sum2 / (n - 1)
    return variance

Este algoritmo es numéricamente estable si n es pequeño.[1][4]​ Sin embargo, los resultados de estos dos algoritmos simples ("ingenuo" y "de dos pasos") pueden depender excesivamente del orden de los datos y pueden dar resultados deficientes para conjuntos de datos muy grandes, debido a repetidos errores de redondeo en la acumulación de las sumas. Técnicas como la suma compensada se pueden usar para combatir este error hasta cierto punto.

Algoritmo en línea de Welford[editar]

A menudo es útil poder calcular la variación en una sola pasada, inspeccionando cada valor solo una vez; por ejemplo, cuando los datos se recopilan sin el almacenamiento suficiente para mantener todos los valores, o cuando los costos de acceso a la memoria dominan a los de cálculo. Para este algoritmo en línea, se utiliza una relación de recurrencia entre cantidades a partir de las cuales las estadísticas requeridas se pueden calcular de forma numéricamente estable.

Las siguientes fórmulas se pueden utilizar para actualizar la media y la varianza (estimada) de la secuencia, para un elemento adicional xn. Aquí, xn denota la media muestral de las primeras n muestras (x1, ..., xn), s2
n
su varianza muestral, y σ2
n
su varianza poblacional.

Estas fórmulas sufren de inestabilidad numérica, ya que restan repetidamente un número pequeño de un número grande que se escala con n. Una mejor cantidad para actualizar es la suma de cuadrados de las diferencias de la media actual, , que aquí denota :

Este algoritmo fue ideado por Welford,[5][6]​ y se ha analizado exhaustivamente.[2][7]​ También es común denotar y .[8]

A continuación se muestra un ejemplo de implementación en Python para el algoritmo de Welford.

# for a new value newValue, compute the new count, new mean, the new M2.
# mean accumulates the mean of the entire dataset
# M2 aggregates the squared distance from the mean
# count aggregates the number of samples seen so far
def update(existingAggregate, newValue):
    (count, mean, M2) = existingAggregate
    count += 1 
    delta = newValue - mean
    mean += delta / count
    delta2 = newValue - mean
    M2 += delta * delta2

    return (count, mean, M2)

# retrieve the mean, variance and sample variance from an aggregate
def finalize(existingAggregate):
    (count, mean, M2) = existingAggregate
    (mean, variance, sampleVariance) = (mean, M2/count, M2/(count - 1)) 
    if count < 2:
        return float('nan')
    else:
        return (mean, variance, sampleVariance)

Este algoritmo es mucho menos propenso a la pérdida de precisión debido a la cancelación catastrófica, pero podría no ser tan eficiente debido a la operación de división incluida dentro del bucle. Para disponer de un algoritmo de dos pasos particularmente robusto con el fin de calcular la varianza, primero se puede calcular y restar una estimación de la media y luego usar este algoritmo con los residuos.

El algoritmo paralelo mostrado más adelante ilustra cómo combinar varios conjuntos de estadísticas calculadas en línea.

Algoritmo incremental ponderado[editar]

El algoritmo puede extenderse para manejar pesos de muestra desiguales, reemplazando el simple contador n con la suma de pesos vista hasta ahora. West (1979)[9]​ sugiere este algoritmo incremental:

def weighted_incremental_variance(dataWeightPairs):
    wSum = wSum2 = mean = S = 0

    for x, w in dataWeightPairs:  # Alternatively "for x, w in zip(data, weights):"
        wSum = wSum + w
        wSum2 = wSum2 + w*w
        meanOld = mean
        mean = meanOld + (w / wSum) * (x - meanOld)
        S = S + w * (x - meanOld) * (x - mean)

    population_variance = S / wSum
    # Bessel's correction for weighted samples
    # Frequency weights
    sample_frequency_variance = S / (wSum - 1)
    # Reliability weights
    sample_reliability_variance = S / (wSum - wSum2/wSum)

Algoritmo "paralelo"[editar]

Chan et al.[10]​ observaron que el algoritmo en línea de Welford detallado anteriormente es un caso especial de un algoritmo que funciona para cualquier partición de la muestra en los conjuntos , :

.

Esto puede ser útil cuando, por ejemplo, se pueden asignar múltiples unidades de procesamiento a partes discretas de la entrada.

El método de Chan para estimar la media es numéricamente inestable cuando y ambos son grandes, porque el error numérico en no se reduce de la forma en que se encuentra en el caso . En tales casos, es preferible .

def parallel_variance(avg_a, count_a, var_a, avg_b, count_b, var_b):
    delta = avg_b - avg_a
    m_a = var_a * (count_a - 1)
    m_b = var_b * (count_b - 1)
    M2 = m_a + m_b + delta ** 2 * count_a * count_b / (count_a + count_b)
    return M2 / (count_a + count_b - 1)

Esto se puede generalizar para permitir la paralelización con Extensiones Vectoriales Avanzadas (EVA), con unidades de procesamiento gráfico y agregados de computadoras, y para la covarianza.[3]

Ejemplo[editar]

Supóngase que todas las operaciones de punto flotante utilizan aritmética IEEE 754 de doble precisión estándar. Considérese la muestra (4, 7, 13, 16) de una población infinita. En base a esta muestra, la media poblacional estimada es 10, y la estimación no sesgada de la varianza poblacional es 30. Tanto el algoritmo "ingenuo" como el algoritmo de dos pasos calculan estos valores correctamente.

A continuación, considérese la muestra (108 + 4, 108 + 7, 108 + 13, 108 + 16), que da lugar a la misma varianza estimada que la primera muestra. El algoritmo de dos pasos calcula esta estimación de varianza correctamente, pero el algoritmo "ingenuo" devuelve 29.33333333333333332 en lugar de 30.

Si bien esta pérdida de precisión puede ser tolerable y vista como un defecto menor del algoritmo "ingenuo", un aumento adicional del desplazamiento hace que el error sea catastrófico. Considérese la muestra (109 + 4, 109 + 7, 109 + 13, 109 + 16). De nuevo, la varianza estimada de la población de 30 se calcula correctamente mediante el algoritmo de dos pasos, pero el algoritmo "ingenuo" ahora lo calcula como −170.66666666666666. Este es un problema serio con el algoritmo "ingenuo" y se debe a la cancelación catastrófica en la resta de dos números similares en la etapa final del algoritmo.

Estadísticas de orden superior[editar]

Terriberry[11]​ amplía las fórmulas de Chan para calcular el tercer y cuarto momento central, necesarios, por ejemplo, al estimar la asimetría estadística y la curtosis:

Aquí, los son nuevamente las sumas de las potencias de las diferencias conrespecto a la media , dando

Para el caso incremental (es decir, ), esto se simplifica a:

Al preservar el valor , solo se necesita una operación de división y, por lo tanto, las estadísticas de orden superior se pueden calcular con un reducido costo incremental.

Un ejemplo del algoritmo en línea para la curtosis implementado como se describe es:

def online_kurtosis(data):
    n = mean = M2 = M3 = M4 = 0

    for x in data:
        n1 = n
        n = n + 1
        delta = x - mean
        delta_n = delta / n
        delta_n2 = delta_n * delta_n
        term1 = delta * delta_n * n1
        mean = mean + delta_n
        M4 = M4 + term1 * delta_n2 * (n*n - 3*n + 3) + 6 * delta_n2 * M2 - 4 * delta_n * M3
        M3 = M3 + term1 * delta_n * (n - 2) - 3 * delta_n * M2
        M2 = M2 + term1

    kurtosis = (n*M4) / (M2*M2) - 3
    return kurtosis

Pébaÿ[12]​ extiende aún más estos resultados a momentos centrales de orden arbitrario, para los casos incrementales y por pares, y posteriormente Pébaÿ et al.[13]​ lo hicieron para momentos ponderados y compuestos. También se pueden encontrar fórmulas similares para la covarianza.

Choi y Sweetman[14]​ ofrecieron dos métodos alternativos para calcular la asimetría y la curtosis, cada uno de los cuales puede ahorrar importantes requisitos de memoria de computadora y tiempo de CPU en ciertas aplicaciones. El primer enfoque es calcular los momentos estadísticos separando los datos en contenedores y luego calculando los momentos a partir de la geometría del histograma resultante, que efectivamente se convierte en un algoritmo de un paso para momentos más altos. Un beneficio es que los cálculos estadísticos del momento se pueden realizar con una precisión arbitraria, de manera que se pueden ajustar a la precisión de, por ejemplo, el formato de almacenamiento de datos o el hardware de medición original. Un histograma relativo de una variable aleatoria se puede construir de manera convencional: el rango de valores potenciales es dividido en contenedores y la cantidad de ocurrencias dentro de cada contenedor se recuenta y se traza de manera tal que el área de cada rectángulo es igual a la porción de los valores de muestra dentro de ese contenedor:

donde y representan la frecuencia y la frecuencia relativa en el intervalo y es el área total del histograma. Después de esta normalización, los momentos en bruto y los momentos centrales de se pueden calcular a partir del histograma relativo:

donde el superíndice indica que los momentos se calculan a partir del histograma. Para un ancho constante del contenedor estas dos expresiones se pueden simplificar utilizando :

El segundo enfoque de Choi y Sweetman[14]​ es una metodología analítica para combinar momentos estadísticos de segmentos individuales de un historial en el tiempo, de modo que los momentos globales resultantes sean los del historial del tiempo completo. Esta metodología podría usarse para el cálculo paralelo de momentos estadísticos con la combinación posterior de esos momentos, o para la combinación de momentos estadísticos computados en tiempos secuenciales.

Si se conocen conjuntos de momentos estadísticos: para , entonces cada puede expresarse en términos de los momentos en bruto equivalentes:

donde generalmente se toma como la duración del historial de tiempo de , o el número de puntos si es constante.

La ventaja de expresar los momentos estadísticos en términos de es que los conjuntos de se pueden combinar por adición, y no hay límite superior en el valor de .

donde el subíndice representa el historial en el tiempo concatenado o combinado. Estos valores combinados de se pueden transformar inversamente en momentos sin procesar que representan el historial en el tiempo concatenado completo

Relaciones conocidas entre los momentos sin procesar () y los momentos centrales () se utilizan después para calcular los momentos centrales de la historia temporal concatenada. Finalmente, los momentos estadísticos de la historia concatenada se computan a partir de los momentos centrales:

Covarianza[editar]

Se pueden usar algoritmos muy similares para calcular el covarianza.

Algoritmo "ingenuo"[editar]

El algoritmo "ingenuo" es:

Para el algoritmo anterior, se podría usar el siguiente código de Python:

def naive_covariance(data1, data2):
    n = len(data1)
    sum12 = 0
    sum1 = sum(data1)
    sum2 = sum(data2)

    for i1, i2 in zip(data1, data2):
        sum12 += i1*i2

    covariance = (sum12 - sum1*sum2 / n) / n
    return covariance

Con estimación de la media[editar]

En cuanto a la varianza, la covarianza de dos variables aleatorias también es invariante al cambio, por lo que dados dos valores constantes y pueden escribirse:

y nuevamente, elegir un valor dentro del rango de valores estabilizará la fórmula contra la cancelación catastrófica y lo hará más robusto contra grandes sumas. Tomando el primer valor de cada conjunto de datos, el algoritmo se puede escribir como:

def shifted_data_covariance(dataX, dataY):
   n = len(dataX)
   if (n < 2):
     return 0
   kx = dataX[0]
   ky = dataY[0]
   Ex = Ey = Exy = 0
   for ix, iy in zip(dataX, dataY):
      Ex += ix - kx
      Ey += iy - ky
      Exy += (ix - kx) * (iy - ky)
   return (Exy - Ex * Ey / n) / n

Algoritmo de dos pasos[editar]

El algoritmo de dos pasos primero calcula las medias muestrales y luego la covarianza:

Se puede escribir como:

def two_pass_covariance(data1, data2):
    n = len(data1)

    mean1 = sum(data1) / n
    mean2 = sum(data2) / n

    covariance = 0

    for i1, i2 in zip(data1, data2):
        a = i1 - mean1
        b = i2 - mean2
        covariance += a*b / n
    return covariance

Una versión compensada ligeramente más precisa realiza el algoritmo "ingenuo" completamente en los residuos. Las sumas finales y deberían ser cero, pero la segunda pasada compensa cualquier pequeño error.

Algoritmo en línea[editar]

Existe un algoritmo estable de una pasada, similar al algoritmo en línea para calcular la varianza, que calcula el momento :

La asimetría aparente en esa última ecuación se debe al hecho de que , por lo que ambos términos de actualización son iguales a . Se puede lograr una mayor precisión si primero se calculan las medias y luego se usa el algoritmo estable de una pasada en los residuos.

Así se puede calcular la covarianza como

def online_covariance(data1, data2):
    meanx = meany = C = n = 0
    for x, y in zip(data1, data2):
        n += 1
        dx = x - meanx
        meanx += dx / n
        meany += (y - meany) / n
        C += dx * (y - meany)

    population_covar = C / n
    # Bessel's correction for sample variance
    sample_covar = C / (n - 1)

También se puede hacer una pequeña modificación para calcular la covarianza ponderada:

def online_weighted_covariance(data1, data2, data3):
    meanx = meany = 0
    wsum = wsum2 = 0
    C = 0
    for x, y, w in zip(data1, data2, data3):
        wsum += w
        wsum2 += w*w
        dx = x - meanx
        meanx += (w / wsum) * dx
        meany += (w / wsum) * (y - meany)
        C += w * dx * (y - meany)

    population_covar = C / wsum
    # Bessel's correction for sample variance
    # Frequency weights
    sample_frequency_covar = C / (wsum - 1)
    # Reliability weights
    sample_reliability_covar = C / (wsum - wsum2 / wsum)

Del mismo modo, existe una fórmula para combinar las covarianzas de dos conjuntos que se pueden usar para paralelizar el cálculo:[3]

Versión ponderada por lotes[editar]

También existe una versión del algoritmo en línea ponderado que se actualiza por lotes: haciendo que denote los pesos, se puede escribir

La covarianza se puede calcular como

Véase también[editar]

Referencias[editar]

  1. a b Einarsson, Bo (1 de agosto de 2005). Accuracy and Reliability in Scientific Computing. SIAM. p. 47. ISBN 978-0-89871-584-2. Consultado el 17 de febrero de 2013. 
  2. a b c Chan, Tony F.; Golub, Gene H.; LeVeque, Randall J. (1983). «Algorithms for computing the sample variance: Analysis and recommendations». The American Statistician 37 (3): 242-247. JSTOR 2683386. 
  3. a b c Schubert, Erich; Gertz, Michael (9 de julio de 2018). Numerically stable parallel computation of (co-)variance. ACM. p. 10. ISBN 9781450365055. doi:10.1145/3221269.3223036. 
  4. Higham, Nicholas (2002). Accuracy and Stability of Numerical Algorithms (2 ed) (Problem 1.10). SIAM. 
  5. Welford, B. P. (1962). «Note on a method for calculating corrected sums of squares and products». Technometrics 4 (3): 419-420. JSTOR 1266577. doi:10.2307/1266577. 
  6. Donald Knuth (1998). The Art of Computer Programming, volume 2: Seminumerical Algorithms, 3rd edn., p. 232. Boston: Addison-Wesley.
  7. Ling, Robert F. (1974). «Comparison of Several Algorithms for Computing Sample Means and Variances». Journal of the American Statistical Association 69 (348): 859-866. doi:10.2307/2286154. 
  8. http://www.johndcook.com/standard_deviation.html
  9. West, D. H. D. (1979). «Updating Mean and Variance Estimates: An Improved Method». Communications of the ACM 22 (9): 532-535. doi:10.1145/359146.359153. 
  10. Chan, Tony F.; Golub, Gene H.; LeVeque, Randall J. (1979), «Updating Formulae and a Pairwise Algorithm for Computing Sample Variances.», Technical Report STAN-CS-79-773, Department of Computer Science, Stanford University ..
  11. Terriberry, Timothy B. (2007), Computing Higher-Order Moments Online, archivado desde el original el 23 de abril de 2014, consultado el 9 de abril de 2019 .
  12. Pébaÿ, Philippe (2008), «Formulas for Robust, One-Pass Parallel Computation of Covariances and Arbitrary-Order Statistical Moments», Technical Report SAND2008-6212, Sandia National Laboratories .
  13. Pébaÿ, Philippe; Terriberry, Timothy; Kolla, Hemanth; Bennett, Janine (2016), «Numerically Stable, Scalable Formulas for Parallel and Online Computation of Higher-Order Multivariate Central Moments with Arbitrary Weights», Computational Statistics (Springer) 31 (4): 1305-1325, doi:10.1007/s00180-015-0637-z .
  14. a b Choi, Myoungkeun; Sweetman, Bert (2010), «Efficient Calculation of Statistical Moments for Structural Health Monitoring», Journal of Structural Health Monitoring 9 (1): 13-24, doi:10.1177/1475921709341014 .

Enlaces externos[editar]