Il y a une faille dans la réponse de Jason R, qui est discutée dans "Art of Computer Programming" vol. 2. Le problème survient si vous avez un écart-type qui est une petite fraction de la moyenne: le calcul de E (x ^ 2) - (E (x) ^ 2) souffre d'une sensibilité sévère aux erreurs d'arrondi à virgule flottante.
Vous pouvez même essayer cela vous-même dans un 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
J'obtiens -128.0 comme réponse, ce qui n'est clairement pas valable pour le calcul, car les mathématiques prédisent que le résultat devrait être non négatif.
Knuth cite une approche (je ne me souviens pas du nom de l'inventeur) pour calculer la moyenne courante et l'écart type qui ressemble à ceci:
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);
puis après chaque étape, la valeur de m
est la moyenne, et l'écart-type peut être calculé comme sqrt(S/n)
ou sqrt(S/n-1)
selon votre définition préférée de l'écart-type.
L'équation que j'écris ci-dessus est légèrement différente de celle de Knuth, mais elle est équivalente sur le plan des calculs.
Lorsque j'ai encore quelques minutes, je coderai la formule ci-dessus en Python et montrerai que vous obtiendrez une réponse non négative (qui, espérons-le, est proche de la valeur correcte).
mise à jour: le voici.
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)
résultat:
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}
Vous remarquerez qu'il y a encore une erreur d'arrondi, mais ce n'est pas mauvais, alors naive_stats
que ça vomit.
edit: Je viens de remarquer le commentaire de Belisarius citant Wikipedia qui mentionne l'algorithme Knuth.