Come determinare la deviazione standard (stddev) di un insieme di valori?

Ho bisogno di sapere se un numero rispetto ad un insieme di numeri è al di fuori di 1 stddev dalla media, ecc.

Mentre l’algoritmo della sum dei quadrati funziona bene la maggior parte del tempo, può causare grossi problemi se si hanno a che fare con numeri molto grandi. Fondamentalmente potresti finire con una varianza negativa …

Inoltre, non calcolare mai, mai, mai un ^ 2 come pow (a, 2), a * a è quasi certamente più veloce.

Il modo migliore per calcolare una deviazione standard è di gran lunga il metodo di Welford . My C è molto arrugginito, ma potrebbe sembrare qualcosa di simile:

public static double StandardDeviation(List valueList) { double M = 0.0; double S = 0.0; int k = 1; foreach (double value in valueList) { double tmpM = M; M += (value - tmpM) / k; S += (value - tmpM) * (value - M); k++; } return Math.Sqrt(S / (k-2)); } 

Se si dispone dell’intera popolazione (al contrario di una popolazione campione ), utilizzare return Math.Sqrt(S / (k-1)); .

EDIT: Ho aggiornato il codice in base alle osservazioni di Jason …

EDIT: Ho anche aggiornato il codice in base alle osservazioni di Alex …

Una soluzione 10 volte più veloce di quella di Jaime, ma sappi che, come ha sottolineato Jaime:

“Mentre l’algoritmo della sum dei quadrati funziona bene la maggior parte delle volte, può causare grossi problemi se si hanno a che fare con numeri molto grandi . Fondamentalmente si può finire con una varianza negativa”

Se pensi di avere a che fare con numeri molto grandi o con una grande quantità di numeri, dovresti calcolare usando entrambi i metodi, se i risultati sono uguali, sai per certo che puoi usare il “mio” metodo per te.

  public static double StandardDeviation(double[] data) { double stdDev = 0; double sumAll = 0; double sumAllQ = 0; //Sum of x and sum of x² for (int i = 0; i < data.Length; i++) { double x = data[i]; sumAll += x; sumAllQ += x * x; } //Mean (not used here) //double mean = 0; //mean = sumAll / (double)data.Length; //Standard deviation stdDev = System.Math.Sqrt( (sumAllQ - (sumAll * sumAll) / data.Length) * (1.0d / (data.Length - 1)) ); return stdDev; } 

La libreria Math.NET fornisce questo per te della scatola.

PM> Install-Package MathNet.Numerics

 var populationStdDev = new List(1d, 2d, 3d, 4d, 5d).PopulationStandardDeviation(); var sampleStdDev = new List(2d, 3d, 4d).StandardDeviation(); 

Vedi http://numerics.mathdotnet.com/docs/DescriptiveStatistics.html per ulteriori informazioni.

La risposta accettata da Jaime è ottima, tranne che devi dividere per k-2 nell’ultima riga (devi dividere per “number_of_elements-1”). Meglio ancora, inizia da zero a 0:

 public static double StandardDeviation(List valueList) { double M = 0.0; double S = 0.0; int k = 0; foreach (double value in valueList) { k++; double tmpM = M; M += (value - tmpM) / k; S += (value - tmpM) * (value - M); } return Math.Sqrt(S / (k-1)); } 

Snippet di codice:

 public static double StandardDeviation(List valueList) { if (valueList.Count < 2) return 0.0; double sumOfSquares = 0.0; double average = valueList.Average(); //.NET 3.0 foreach (double value in valueList) { sumOfSquares += Math.Pow((value - average), 2); } return Math.Sqrt(sumOfSquares / (valueList.Count - 1)); } 

È ansible evitare di eseguire due passaggi sui dati accumulando la media e il quadrato medio

 cnt = 0 mean = 0 meansqr = 0 loop over array cnt++ mean += value meansqr += value*value mean /= cnt meansqr /= cnt 

e formando

 sigma = sqrt(meansqr - mean^2) 

Anche un fattore di cnt/(cnt-1) è spesso appropriato.

BTW– Il primo passaggio sui dati nelle risposte Demi e McWafflestix è nascosto nelle chiamate a Average . Questo tipo di cose è certamente banale in una piccola lista, ma se la lista supera la dimensione della cache, o anche il working set, questo diventa un affare.

Ho trovato che la risposta utile di Rob non corrispondeva esattamente a quello che stavo vedendo usando Excel. Per abbinare eccellere, ho passato la media per valueList nel calcolo di StandardDeviation.

Ecco i miei due centesimi … e chiaramente potresti calcolare la media mobile (ma) da valueList all’interno della funzione – ma mi è capitato di avere già prima di aver bisogno dello standardDeviation.

 public double StandardDeviation(List valueList, double ma) { double xMinusMovAvg = 0.0; double Sigma = 0.0; int k = valueList.Count; foreach (double value in valueList){ xMinusMovAvg = value - ma; Sigma = Sigma + (xMinusMovAvg * xMinusMovAvg); } return Math.Sqrt(Sigma / (k - 1)); } 

Con i metodi di estensione.

 using System; using System.Collections.Generic; namespace SampleApp { internal class Program { private static void Main() { List data = new List {1, 2, 3, 4, 5, 6}; double mean = data.Mean(); double variance = data.Variance(); double sd = data.StandardDeviation(); Console.WriteLine("Mean: {0}, Variance: {1}, SD: {2}", mean, variance, sd); Console.WriteLine("Press any key to continue..."); Console.ReadKey(); } } public static class MyListExtensions { public static double Mean(this List values) { return values.Count == 0 ? 0 : values.Mean(0, values.Count); } public static double Mean(this List values, int start, int end) { double s = 0; for (int i = start; i < end; i++) { s += values[i]; } return s / (end - start); } public static double Variance(this List values) { return values.Variance(values.Mean(), 0, values.Count); } public static double Variance(this List values, double mean) { return values.Variance(mean, 0, values.Count); } public static double Variance(this List values, double mean, int start, int end) { double variance = 0; for (int i = start; i < end; i++) { variance += Math.Pow((values[i] - mean), 2); } int n = end - start; if (start > 0) n -= 1; return variance / (n); } public static double StandardDeviation(this List values) { return values.Count == 0 ? 0 : values.StandardDeviation(0, values.Count); } public static double StandardDeviation(this List values, int start, int end) { double mean = values.Mean(start, end); double variance = values.Variance(mean, start, end); return Math.Sqrt(variance); } } } 
 ///  /// Calculates standard deviation, same as MATLAB std(X,0) function ///  ///  /// enumumerable data /// Standard deviation public static double GetStandardDeviation(this IEnumerable values) { //validation if (values == null) throw new ArgumentNullException(); int lenght = values.Count(); //saves from devision by 0 if (lenght == 0 || lenght == 1) return 0; double sum = 0.0, sum2 = 0.0; for (int i = 0; i < lenght; i++) { double item = values.ElementAt(i); sum += item; sum2 += item * item; } return Math.Sqrt((sum2 - sum * sum / lenght) / (lenght - 1)); } 

Il problema con tutte le altre risposte è che presuppongono che tu abbia i tuoi dati in un grande array. Se i tuoi dati arrivano al volo, questo sarebbe un approccio migliore. Questa class funziona indipendentemente da come o se si memorizzano i dati. Ti dà anche la possibilità di scegliere il metodo Waldorf o il metodo della sum dei quadrati. Entrambi i metodi funzionano con un singolo passaggio.

 public final class StatMeasure { private StatMeasure() {} public interface Stats1D { /** Add a value to the population */ void addValue(double value); /** Get the mean of all the added values */ double getMean(); /** Get the standard deviation from a sample of the population. */ double getStDevSample(); /** Gets the standard deviation for the entire population. */ double getStDevPopulation(); } private static class WaldorfPopulation implements Stats1D { private double mean = 0.0; private double sSum = 0.0; private int count = 0; @Override public void addValue(double value) { double tmpMean = mean; double delta = value - tmpMean; mean += delta / ++count; sSum += delta * (value - mean); } @Override public double getMean() { return mean; } @Override public double getStDevSample() { return Math.sqrt(sSum / (count - 1)); } @Override public double getStDevPopulation() { return Math.sqrt(sSum / (count)); } } private static class StandardPopulation implements Stats1D { private double sum = 0.0; private double sumOfSquares = 0.0; private int count = 0; @Override public void addValue(double value) { sum += value; sumOfSquares += value * value; count++; } @Override public double getMean() { return sum / count; } @Override public double getStDevSample() { return (float) Math.sqrt((sumOfSquares - ((sum * sum) / count)) / (count - 1)); } @Override public double getStDevPopulation() { return (float) Math.sqrt((sumOfSquares - ((sum * sum) / count)) / count); } } /** * Returns a way to measure a population of data using Waldorf's method. * This method is better if your population or values are so large that * the sum of x-squared may overflow. It's also probably faster if you * need to recalculate the mean and standard deviation continuously, * for example, if you are continually updating a graphic of the data as * it flows in. * * @return A Stats1D object that uses Waldorf's method. */ public static Stats1D getWaldorfStats() { return new WaldorfPopulation(); } /** * Return a way to measure the population of data using the sum-of-squares * method. This is probably faster than Waldorf's method, but runs the * risk of data overflow. * * @return A Stats1D object that uses the sum-of-squares method */ public static Stats1D getSumOfSquaresStats() { return new StandardPopulation(); } }