Há uma falha na resposta de Jason R, que é discutida no "Art of Computer Programming", de Knuth, vol. 2. O problema surge se você tiver um desvio padrão que é uma pequena fração da média: o cálculo de E (x ^ 2) - (E (x) ^ 2) sofre de severa sensibilidade a erros de arredondamento de ponto flutuante.
Você pode até tentar isso sozinho em um script Python:
ofs = 1e9
A = [ofs+x for x in [1,-1,2,3,0,4.02,5]]
A2 = [x*x for x in A]
(sum(A2)/len(A))-(sum(A)/len(A))**2
Recebo -128.0 como resposta, que claramente não é computacionalmente válida, pois a matemática prevê que o resultado deve ser não-negativo.
Knuth cita uma abordagem (não me lembro do nome do inventor) para calcular a média e o desvio padrão em execução, que são mais ou menos assim:
initialize:
m = 0;
S = 0;
n = 0;
for each incoming sample x:
prev_mean = m;
n = n + 1;
m = m + (x-m)/n;
S = S + (x-m)*(x-prev_mean);
e depois de cada etapa, o valor de m
é a média e o desvio padrão pode ser calculado como sqrt(S/n)
ou sqrt(S/n-1)
dependendo da sua definição favorita de desvio padrão.
A equação que escrevo acima é um pouco diferente da de Knuth, mas é computacionalmente equivalente.
Quando tiver mais alguns minutos, codificarei a fórmula acima em Python e mostrarei que você obterá uma resposta não negativa (que, esperamos, esteja próxima do valor correto).
atualização: aqui está.
test1.py:
import math
def stats(x):
n = 0
S = 0.0
m = 0.0
for x_i in x:
n = n + 1
m_prev = m
m = m + (x_i - m) / n
S = S + (x_i - m) * (x_i - m_prev)
return {'mean': m, 'variance': S/n}
def naive_stats(x):
S1 = sum(x)
n = len(x)
S2 = sum([x_i**2 for x_i in x])
return {'mean': S1/n, 'variance': (S2/n - (S1/n)**2) }
x1 = [1,-1,2,3,0,4.02,5]
x2 = [x+1e9 for x in x1]
print "naive_stats:"
print naive_stats(x1)
print naive_stats(x2)
print "stats:"
print stats(x1)
print stats(x2)
resultado:
naive_stats:
{'variance': 4.0114775510204073, 'mean': 2.0028571428571427}
{'variance': -128.0, 'mean': 1000000002.0028572}
stats:
{'variance': 4.0114775510204073, 'mean': 2.0028571428571431}
{'variance': 4.0114775868357446, 'mean': 1000000002.0028571}
Você notará que ainda há algum erro de arredondamento, mas não é ruim, enquanto naive_stats
apenas vomita.
edit: Acabei de notar o comentário de Belisarius citando a Wikipedia, que menciona o algoritmo de Knuth.