C'è un difetto nella risposta di Jason R, che è discussa nel vol. Di "Art of Computer Programming" di Knuth. 2. Il problema si presenta se si ha una deviazione standard che è una piccola frazione della media: il calcolo di E (x ^ 2) - (E (x) ^ 2) soffre di una grave sensibilità agli errori di arrotondamento in virgola mobile.
Puoi anche provarlo tu stesso in uno 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
Ottengo -128.0 come risposta, che chiaramente non è valida dal punto di vista computazionale, poiché la matematica prevede che il risultato dovrebbe essere non negativo.
Knuth cita un approccio (non ricordo il nome dell'inventore) per il calcolo della media corrente e della deviazione standard che va in questo modo:
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 quindi dopo ogni passaggio, il valore di m
è la media e la deviazione standard può essere calcolata come sqrt(S/n)
o in sqrt(S/n-1)
base alla definizione preferita di deviazione standard.
L'equazione che scrivo sopra è leggermente diversa da quella di Knuth, ma è equivalente dal punto di vista computazionale.
Quando avrò ancora qualche minuto, codificherò la formula sopra in Python e mostrerò che otterrai una risposta non negativa (che si spera sia vicina al valore corretto).
aggiornamento: eccolo qui.
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)
risultato:
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}
Noterai che c'è ancora un errore di arrotondamento, ma non è male, mentre naive_stats
solo vomita.
modifica: Ho appena notato il commento di Belisario che cita Wikipedia che menziona l'algoritmo Knuth.