Algoritmos para calcular a variância - Algorithms for calculating variance

Algoritmos para calcular a variância desempenham um papel importante nas estatísticas computacionais . Uma dificuldade chave no projeto de bons algoritmos para esse problema é que as fórmulas para a variância podem envolver somas de quadrados, o que pode levar à instabilidade numérica , bem como ao estouro aritmético ao lidar com valores grandes.

Algoritmo ingênuo

Uma fórmula para calcular a variância de uma população inteira de tamanho N é:

Usando a correção de Bessel para calcular uma estimativa imparcial da variância da população a partir de uma amostra finita de n observações, a fórmula é:

Portanto, um algoritmo ingênuo para calcular a variação estimada é dado pelo seguinte:

  • Seja n ← 0, Soma ← 0, SumSq ← 0
  • Para cada datum x :
    • nn + 1
    • Soma ← Soma + x
    • SumSq ← SumSq + x × x
  • Var = (SumSq - (Soma × Soma) / n) / (n - 1)

Este algoritmo pode ser facilmente adaptado para calcular a variância de uma população finita: simplesmente divida por N em vez de n  - 1 na última linha.

Como SumSq e (Sum × Sum) / n podem ser números muito semelhantes, o cancelamento pode fazer com que a precisão do resultado seja muito menor do que a precisão inerente da aritmética de ponto flutuante usada para realizar o cálculo. Portanto, este algoritmo não deve ser usado na prática, e vários algoritmos alternativos, numericamente estáveis, foram propostos. Isso é particularmente ruim se o desvio padrão for pequeno em relação à média.

Computando dados deslocados

A variância é invariável em relação às mudanças em um parâmetro de localização , uma propriedade que pode ser usada para evitar o cancelamento catastrófico nesta fórmula.

com qualquer constante, o que leva à nova fórmula

quanto mais próximo do valor médio, mais preciso será o resultado, mas apenas a escolha de um valor dentro da faixa de amostras garantirá a estabilidade desejada. Se os valores forem pequenos, então não há problemas com a soma dos seus quadrados, pelo contrário, se forem grandes significa necessariamente que a variância também é grande. Em qualquer caso, o segundo termo na fórmula é sempre menor do que o primeiro, portanto, nenhum cancelamento pode ocorrer.

Se apenas a primeira amostra for obtida, o algoritmo pode ser escrito na linguagem de programação 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 também facilita o cálculo incremental que pode ser expresso como

K = n = Ex = Ex2 = 0.0

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

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

def get_mean():
    global K, n, Ex
    return K + Ex / n

def get_variance():
    global n, Ex, Ex2
    return (Ex2 - (Ex * Ex) / n) / (n - 1)

Algoritmo de duas passagens

Uma abordagem alternativa, usando uma fórmula diferente para a variância, primeiro calcula a média da amostra,

e então calcula a soma dos quadrados das diferenças da média,

onde s é o desvio padrão. Isso é dado pelo seguinte có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 é numericamente estável se n for pequeno. No entanto, os resultados de ambos os algoritmos simples ("ingênuo" e "duas passagens") podem depender excessivamente da ordem dos dados e podem dar resultados ruins para conjuntos de dados muito grandes devido a repetidos erros de arredondamento no acúmulo de somas. Técnicas como soma compensada podem ser usadas para combater esse erro até certo ponto.

Algoritmo online de Welford

Muitas vezes é útil ser capaz de calcular a variação em uma única passagem , inspecionando cada valor apenas uma vez; por exemplo, quando os dados estão sendo coletados sem armazenamento suficiente para manter todos os valores, ou quando os custos de acesso à memória dominam os de computação. Para tal algoritmo online , uma relação de recorrência é necessária entre as quantidades a partir das quais as estatísticas necessárias podem ser calculadas de forma numericamente estável.

As fórmulas a seguir podem ser usadas para atualizar a média e a variância (estimada) da sequência, para um elemento adicional x n . Aqui, denota a média da amostra das primeiras n amostras , sua variância de amostra enviesada e sua variância de amostra enviesada .

Essas fórmulas sofrem de instabilidade numérica, pois repetidamente subtraem um pequeno número de um grande número que escala com n . Uma quantidade melhor para atualização é a soma dos quadrados das diferenças da média atual , aqui denotada :

Este algoritmo foi encontrado por Welford e foi completamente analisado. Também é comum denotar e .

Um exemplo de implementação Python para o algoritmo de Welford é fornecido abaixo.

# 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
    if count < 2:
        return float("nan")
    else:
        (mean, variance, sampleVariance) = (mean, M2 / count, M2 / (count - 1))
        return (mean, variance, sampleVariance)

Este algoritmo é muito menos sujeito a perda de precisão devido ao cancelamento catastrófico , mas pode não ser tão eficiente devido à operação de divisão dentro do loop. Para um algoritmo de duas passagens particularmente robusto para calcular a variância, pode-se primeiro calcular e subtrair uma estimativa da média e, em seguida, usar este algoritmo nos resíduos.

O algoritmo paralelo abaixo ilustra como mesclar vários conjuntos de estatísticas calculadas online.

Algoritmo incremental ponderado

O algoritmo pode ser estendido para lidar com pesos amostrais desiguais, substituindo o contador simples n pela soma dos pesos vistos até agora. West (1979) sugere este algoritmo incremental :

def weighted_incremental_variance(data_weight_pairs):
    w_sum = w_sum2 = mean = S = 0

    for x, w in data_weight_pairs:
        w_sum = w_sum + w
        w_sum2 = w_sum2 + w * w
        mean_old = mean
        mean = mean_old + (w / w_sum) * (x - mean_old)
        S = S + w * (x - mean_old) * (x - mean)

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

Algoritmo paralelo

Chan et al. observe que o algoritmo online de Welford detalhado acima é um caso especial de um algoritmo que funciona para combinar conjuntos arbitrários e :

.

Isso pode ser útil quando, por exemplo, várias unidades de processamento podem ser atribuídas a partes discretas da entrada.

O método de Chan para estimar a média é numericamente instável quando e ambos são grandes, porque o erro numérico em não é reduzido da maneira que é no caso. Nesses casos, prefira .

def parallel_variance(n_a, avg_a, M2_a, n_b, avg_b, M2_b):
    n = n_a + n_b
    delta = avg_b - avg_a
    M2 = M2_a + M2_b + delta ** 2 * n_a * n_b / n
    var_ab = M2 / (n - 1)
    return var_ab

Isso pode ser generalizado para permitir a paralelização com AVX , com GPUs e clusters de computador e para covariância.

Exemplo

Suponha que todas as operações de ponto flutuante usem aritmética de precisão dupla padrão IEEE 754 . Considere a amostra (4, 7, 13, 16) de uma população infinita. Com base nessa amostra, a média da população estimada é 10 e a estimativa não enviesada da variância da população é 30. Tanto o algoritmo ingênuo quanto o algoritmo de duas passagens calculam esses valores corretamente.

Em seguida, considere a amostra ( 10 8  + 4 , 10 8  + 7 , 10 8  + 13 , 10 8  + 16 ), que dá origem à mesma variância estimada da primeira amostra. O algoritmo de duas passagens calcula essa estimativa de variância corretamente, mas o algoritmo ingênuo retorna 29.333333333333332 em vez de 30.

Embora essa perda de precisão possa ser tolerável e vista como uma falha secundária do algoritmo ingênuo, aumentar ainda mais o deslocamento torna o erro catastrófico. Considere a amostra ( 10 9  + 4 , 10 9  + 7 , 10 9  + 13 , 10 9  + 16 ). Novamente, a variação da população estimada de 30 é calculada corretamente pelo algoritmo de duas passagens, mas o algoritmo ingênuo agora a calcula como -170.66666666666666. Este é um problema sério com o algoritmo ingênuo e é devido ao cancelamento catastrófico na subtração de dois números semelhantes no estágio final do algoritmo.

Estatísticas de ordem superior

Terriberry estende as fórmulas de Chan para calcular o terceiro e o quarto momentos centrais , necessários, por exemplo, ao estimar assimetria e curtose :

Aqui estão novamente as somas das potências das diferenças da média , dando

Para o caso incremental (ou seja, ), isso simplifica para:

Ao preservar o valor , apenas uma operação de divisão é necessária e as estatísticas de ordem superior podem, portanto, ser calculadas com pouco custo incremental.

Um exemplo do algoritmo online para curtose implementado conforme descrito é:

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

    # Note, you may also calculate variance using M2, and skewness using M3
    # Caution: If all the inputs are the same, M2 will be 0, resulting in a division by 0.
    kurtosis = (n * M4) / (M2 * M2) - 3
    return kurtosis

Pébaÿ estende ainda mais esses resultados para momentos centrais de ordem arbitrária , para os casos incrementais e aos pares, e subsequentemente Pébaÿ et al. para momentos ponderados e compostos. Também podem ser encontradas fórmulas semelhantes para covariância .

Choi e Sweetman oferecem dois métodos alternativos para calcular a assimetria e curtose, cada um dos quais pode economizar requisitos substanciais de memória do computador e tempo de CPU em certos aplicativos. A primeira abordagem é computar os momentos estatísticos, separando os dados em caixas e, em seguida, computando os momentos da geometria do histograma resultante, que se torna efetivamente um algoritmo de uma passagem para momentos mais elevados. Um benefício é que os cálculos estatísticos do momento podem ser realizados com precisão arbitrária, de modo que os cálculos podem ser ajustados para a precisão, por exemplo, do formato de armazenamento de dados ou do hardware de medição original. Um histograma relativo de uma variável aleatória pode ser construído da maneira convencional: o intervalo de valores potenciais é dividido em caixas e o número de ocorrências dentro de cada caixa é contado e plotado de modo que a área de cada retângulo seja igual à parte dos valores de amostra dentro dessa caixa:

onde e representam a frequência e a frequência relativa no bin e é a área total do histograma. Após esta normalização, os momentos brutos e os momentos centrais de podem ser calculados a partir do histograma relativo:

onde o sobrescrito indica que os momentos são calculados a partir do histograma. Para largura de compartimento constante, essas duas expressões podem ser simplificadas usando :

A segunda abordagem de Choi e Sweetman é uma metodologia analítica para combinar momentos estatísticos de segmentos individuais de uma história de tempo de modo que os momentos gerais resultantes sejam aqueles da história de tempo completa. Esta metodologia pode ser usada para cálculo paralelo de momentos estatísticos com subsequente combinação desses momentos, ou para combinação de momentos estatísticos computados em tempos sequenciais.

Se conjuntos de momentos estatísticos são conhecidos: para , então cada um pode ser expresso em termos de momentos brutos equivalentes :

onde geralmente é considerado a duração do histórico de tempo, ou o número de pontos se for constante.

A vantagem de expressar os momentos estatísticos em termos de é que os conjuntos podem ser combinados por adição e não há limite superior para o valor de .

onde o subscrito representa o histórico de tempo concatenado ou combinado . Esses valores combinados de podem então ser inversamente transformados em momentos brutos que representam a história do tempo concatenada completa

Relacionamentos conhecidos entre os momentos brutos ( ) e os momentos centrais ( ) são então usados ​​para computar os momentos centrais da história do tempo concatenada. Finalmente, os momentos estatísticos da história concatenada são calculados a partir dos momentos centrais:

Covariância

Algoritmos muito semelhantes podem ser usados ​​para calcular a covariância .

Algoritmo ingênuo

O algoritmo ingênuo é:

Para o algoritmo acima, pode-se usar o seguinte código 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

Com estimativa da média

Quanto à variância, a covariância de duas variáveis ​​aleatórias também é invariante ao deslocamento, portanto, dados quaisquer dois valores constantes e podem ser escritos:

e, novamente, a escolha de um valor dentro da faixa de valores estabilizará a fórmula contra o cancelamento catastrófico, bem como a tornará mais robusta contra grandes somas. Tomando o primeiro valor de cada conjunto de dados, o algoritmo pode ser escrito como:

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

Duas passagens

O algoritmo de duas passagens primeiro calcula as médias da amostra e, em seguida, a covariância:

O algoritmo de duas passagens pode ser escrito 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

Uma versão compensada um pouco mais precisa executa o algoritmo ingênuo completo nos resíduos. As somas finais e devem ser zero, mas a segunda passagem compensa qualquer pequeno erro.

Conectados

Existe um algoritmo de passagem única estável, semelhante ao algoritmo online para calcular a variância, que calcula o co-momento :

A aparente assimetria nessa última equação se deve ao fato de que , ambos os termos de atualização são iguais a . Uma precisão ainda maior pode ser alcançada calculando primeiro os meios e, em seguida, usando o algoritmo de uma passagem estável nos resíduos.

Assim, a covariância pode ser calculada 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)

Uma pequena modificação também pode ser feita para calcular a covariância 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)

Da mesma forma, existe uma fórmula para combinar as covariâncias de dois conjuntos que podem ser usados ​​para paralelizar o cálculo:

Versão ponderada em lote

Também existe uma versão do algoritmo online ponderado que atualiza em lote: vamos denotar os pesos e escrever

A covariância pode então ser calculada como

Veja também

Referências

  1. ^ a b Einarsson, Bo (2005). Precisão e confiabilidade em computação científica . SIAM. p. 47. ISBN 978-0-89871-584-2.
  2. ^ a b c Chan, Tony F .; Golub, Gene H .; LeVeque, Randall J. (1983). "Algoritmos para calcular a variância da amostra: Análise e recomendações" (PDF) . The American Statistician . 37 (3): 242–247. doi : 10.1080 / 00031305.1983.10483115 . JSTOR  2683386 .
  3. ^ a b c Schubert, Erich; Gertz, Michael (9 de julho de 2018). Cálculo paralelo numericamente estável de (co-) variância . ACM. p. 10. doi : 10.1145 / 3221269.3223036 . ISBN 9781450365055. S2CID  49665540 .
  4. ^ Higham, Nicholas (2002). Precisão e estabilidade de algoritmos numéricos (2 ed) (Problema 1.10) . SIAM.
  5. ^ Welford, BP (1962). "Nota sobre um método de cálculo de somas corrigidas de quadrados e produtos". Tecnometria . 4 (3): 419–420. doi : 10.2307 / 1266577 . JSTOR  1266577 .
  6. ^ Donald E. Knuth (1998). The Art of Computer Programming , volume 2: Seminumerical Algorithms , 3rd edn., P. 232. Boston: Addison-Wesley.
  7. ^ Ling, Robert F. (1974). "Comparação de vários algoritmos para calcular médias e variações de amostra". Journal of the American Statistical Association . 69 (348): 859–866. doi : 10.2307 / 2286154 . JSTOR  2286154 .
  8. ^ http://www.johndcook.com/standard_deviation.html
  9. ^ West, DHD (1979). "Atualizando Estimativas de Média e Variância: Um Método Melhorado". Comunicações da ACM . 22 (9): 532–535. doi : 10.1145 / 359146.359153 . S2CID  30671293 .
  10. ^ Chan, Tony F .; Golub, Gene H .; LeVeque, Randall J. (1979), "Updating Formulas and a Pairwise Algorithm for Computing Sample Variances." (PDF) , Relatório Técnico STAN-CS-79-773 , Departamento de Ciência da Computação, Universidade de Stanford.
  11. ^ Terriberry, Timothy B. (2007), Computing Higher-Order Moments Online , arquivado do original em 23 de abril de 2014 , recuperado em 5 de maio de 2008
  12. ^ Pébaÿ, Philippe (2008), "Formulas for Robust, One-Pass Parallel Computation of Covariances and Arbitrary-Order Statistical Moments" (PDF) , 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 , S2CID  124570169
  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 , S2CID  17534100

links externos