2009-05-22 10 views
42

Ho bisogno di sapere se un numero rispetto ad un insieme di numeri è al di fuori del 1 stdDev dalla media, ecc ..Come si determina la deviazione standard (stddev) di un insieme di valori?

+3

Questo sembra mancare il tag compiti a casa .... – overslacked

+12

prego prego soddisfa prego non date per scontato l'OP è una domanda a scopo di compiti a casa, piuttosto che per un progetto "reale" o di auto-miglioramento . Chiediglielo. –

+3

in realtà non sto chiedendo i compiti a casa, ma se aiuta le persone che stanno facendo i compiti a trovare la risposta, per favore aggiungi il tag –

risposta

94

Mentre la somma dei quadrati algoritmo funziona bene la maggior parte del tempo, può causare grossi guai se si tratta di numeri molto grandi. Che, fondamentalmente, può finire con una variazione negativa ...

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

Di gran lunga il miglior modo di calcolare una deviazione standard è Welford's method. Il mio C è molto arrugginito, ma potrebbe essere simile:

public static double StandardDeviation(List<double> 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 di tutta la popolazione(in contrapposizione ad un campione di popolazione), quindi 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 ...

+2

+1: ho letto il commento di Knuth su questo, ma non ho mai saputo che si chiamava metodo di Welford. Per tua informazione puoi eliminare il caso k == 1, funziona e basta. –

+2

OH: e stai dimenticando il divide per N o divide per N-1 alla fine. –

+7

Ora guarda cosa hai fatto. Mi hai dato qualcosa di nuovo da imparare. Tu bestia. – dmckee

2

È possibile evitare di fare due passaggi sui dati accumulando la media e media-square

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) 

Un fattore di cnt/(cnt-1) è spesso opportuno pure.

BTW-- Il primo passaggio sui dati nelle risposte Demi e McWafflestix sono nascosti 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.

+1

La tua formula è sbagliata. Dovrebbe essere sigma = sqrt (meansqr - mean^2) Leggete attentamente questa pagina http://en.wikipedia.org/wiki/Standard_deviation per vedere il vostro errore. – leif

+0

@leif: Sì. E avrei dovuto notare anche il problema dimensionale. – dmckee

+0

-1: matematicamente corretto, ma numericamente questo è male. –

2

Codice frammento:

public static double StandardDeviation(List<double> 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)); 
} 
+1

Dividere per Conteggio - 1 o Conteggio dipende dal fatto che stiamo parlando di un'intera popolazione o di un campione, sì? Sembra che OP stia parlando di una popolazione conosciuta ma non del tutto chiara. –

+0

Questo è corretto - questo è per la varianza campionaria. Apprezzo il momento saliente – Demi

+0

Il tuo codice si arresta in modo anomalo per il caso legittimo di un elenco con un valore. – SPWorley

0
/// <summary> 
/// Calculates standard deviation, same as MATLAB std(X,0) function 
/// <seealso cref="http://www.mathworks.co.uk/help/techdoc/ref/std.html"/> 
/// </summary> 
/// <param name="values">enumumerable data</param> 
/// <returns>Standard deviation</returns> 
public static double GetStandardDeviation(this IEnumerable<double> 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)); 
} 
1

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 è possibile 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<double> 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)); 
}  
3

La risposta accettata da Jaime è grande, tranne che è necessario dividere per k-2 nell'ultima riga (è necessario dividere per "number_of_elements-1"). Meglio ancora, avviare k a 0:

public static double StandardDeviation(List<double> 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)); 
} 
1

Con i metodi di estensione.

using System; 
using System.Collections.Generic; 

namespace SampleApp 
{ 
    internal class Program 
    { 
     private static void Main() 
     { 
      List<double> data = new List<double> {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<double> values) 
     { 
      return values.Count == 0 ? 0 : values.Mean(0, values.Count); 
     } 

     public static double Mean(this List<double> 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<double> values) 
     { 
      return values.Variance(values.Mean(), 0, values.Count); 
     } 

     public static double Variance(this List<double> values, double mean) 
     { 
      return values.Variance(mean, 0, values.Count); 
     } 

     public static double Variance(this List<double> 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<double> values) 
     { 
      return values.Count == 0 ? 0 : values.StandardDeviation(0, values.Count); 
     } 

     public static double StandardDeviation(this List<double> values, int start, int end) 
     { 
      double mean = values.Mean(start, end); 
      double variance = values.Variance(mean, start, end); 

      return Math.Sqrt(variance); 
     } 
    } 
} 
6

10 volte più veloce soluzione che Jaime di, ma essere consapevoli che, come Jaime rilevare:

"Mentre la somma dei dell'algoritmo piazze funziona bene la maggior parte del tempo, possono causare grossi guai se si tratta di molto grandi numeri. È fondamentalmente può finire con una variazione negativa"

Se pensate di avere a che fare con numeri molto grandi o una grande quantità di numeri, dovreste calcolare usando entrambi i metodi, se i risultati sono uguali, sapete per certo che potete usare il "mio" metodo per voi .

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; 
    } 
1

La libreria Math.NET prevede questo per voi a della scatola.

PM> Installa-Package MathNet.Numerics

var populationStdDev = new List<double>(1d, 2d, 3d, 4d, 5d).PopulationStandardDeviation(); 

var sampleStdDev = new List<double>(2d, 3d, 4d).StandardDeviation(); 

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

0

Il problema con tutte le altre risposte è che presuppongono che i tuoi dati sono in un grande array. Se i tuoi dati arrivano al volo, questo sarebbe un approccio migliore. Questa classe funziona indipendentemente da come o se si memorizzano i dati. Ti dà anche la possibilità di scegliere il metodo Waldorf o il metodo della somma 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(); } 
} 
Problemi correlati