2009-05-22 20 views
42

Necesito saber si un número en comparación con un conjunto de números está fuera de 1 STDDEV de la media, etc ..¿Cómo determino la desviación estándar (stddev) de un conjunto de valores?

+3

Esto parece que falta la etiqueta de la tarea .... – overslacked

+12

por favor por favor por favor por favor no asuma el PO está haciendo una pregunta para los propósitos de tarea, en lugar de para un proyecto "real" o de superación . Pregúntales. –

+3

en realidad no estoy preguntando por razones de tarea, pero si ayuda a las personas que hacen la tarea para encontrar la respuesta, por favor agregue la etiqueta –

Respuesta

94

Mientras que la suma de los cuadrados algoritmo funciona bien la mayor parte del tiempo, puede causar grandes problemas si se trata de un número muy grande. Básicamente puede terminar con una varianza negativa ...

Además, nunca, nunca, nunca, calcule a^2 como pow (a, 2), a * a es casi seguro más rápido.

Con mucho, la mejor manera de calcular una desviación estándar es Welford's method. Mi C es muy oxidada, pero podría ser algo como:

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

Si usted tiene la población entera (en contraposición a una muestra de población), a continuación, utilizar return Math.Sqrt(S/(k-1));.

EDIT: He actualizado el código de acuerdo a las declaraciones de Jason ...

EDIT: También he actualizado el código de acuerdo con las declaraciones de Alex ...

+2

+1: He leído el comentario de Knuth sobre esto, pero nunca he sabido que se llamara el método de Welford. FYI puedes eliminar el caso k == 1, simplemente funciona. –

+2

OH: y te estás olvidando de la división por N o divide por N-1 al final. –

+7

Ahora mira lo que has hecho. Me has dado algo nuevo para aprender. Tu bestia. – dmckee

2

Puede evitar hacer dos pasadas por los datos mediante la acumulación de la media y

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

y formando

sigma = sqrt(meansqr - mean^2) 

un factor de cnt/(cnt-1) es a menudo apropiado también.

BTW-- La primera pasada sobre los datos en Demi y McWafflestix las respuestas están ocultas en las llamadas al Average. Ese tipo de cosas es ciertamente trivial en una lista pequeña, pero si la lista excede el tamaño de la memoria caché, o incluso el conjunto de trabajo, se convierte en una oferta.

+1

Su fórmula es incorrecta. Debe ser sigma = sqrt (meansqr - mean^2) Lea atentamente esta página http://en.wikipedia.org/wiki/Standard_deviation para ver su error. – leif

+0

@leif: Sí. Y debería haber notado el problema dimensional, también. – dmckee

+0

-1: matemáticamente correcto, pero numéricamente esto es malo. –

2

Fragmento de código:

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

Dividir por Recuento - 1 o Recuento depende de si estamos hablando de toda la población o muestra, ¿sí? Parece que OP está hablando de una población conocida pero no del todo clara. –

+0

Eso es correcto, esto es para varianza muestral. Aprecio lo más destacado. – Demi

+0

Tu código falla para el caso legítimo de una lista con un valor. – 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

Descubrí que la respuesta útil de Rob no coincidía exactamente con lo que veía usando Excel. Para hacer coincidir Excel, pasé el promedio para valueList en el cálculo de StandardDeviation.

Aquí están mis dos centavos ... y claramente se puede calcular la media móvil (ma) de valueList dentro de la función, pero ya he necesitado antes la desviación estándar.

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 respuesta aceptada por Jaime es grande, excepto que se debe dividir por k-2 en la última línea (hay que dividir por "number_of_elements-1"). Mejor aún, empezar a 0 K:

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 Los métodos de extensión.

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 veces más rápido solución de Jaime, pero ser conscientes que, como Jaime señaló:

"Si bien la suma de algoritmo de cuadrados funciona bien la mayor parte del tiempo, puede causar un gran problema si se trata de números muy grandes. Usted básicamente puede terminar con una varianza negativa "

Si cree que se trata de números muy grandes o una gran cantidad de números, debe calcular usando ambos métodos, si los resultados son iguales, puede estar seguro de que puede usar "mi" método para su caso .

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 biblioteca de Math.NET le proporciona esto para usted de la caja.

PM> Install-Package MathNet.Numerics

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

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

Ver http://numerics.mathdotnet.com/docs/DescriptiveStatistics.html para más información.

0

El problema con todas las demás respuestas es que asumen que tiene sus datos en una gran matriz. Si sus datos están llegando sobre la marcha, esto sería un mejor enfoque. Esta clase funciona independientemente de cómo y si almacena sus datos. También le da la opción del método Waldorf o el método de suma de cuadrados. Ambos métodos funcionan usando un solo pase.

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(); } 
} 
Cuestiones relacionadas