web-dev-qa-db-fra.com

Comment déterminer l'écart-type (stddev) d'un ensemble de valeurs?

J'ai besoin de savoir si un nombre par rapport à un ensemble de nombres est en dehors de 1 stddev de la moyenne, etc.

45
dead and bloated

Bien que l'algorithme de somme des carrés fonctionne bien la plupart du temps, il peut causer de gros problèmes si vous avez affaire à de très grands nombres. Vous pouvez essentiellement vous retrouver avec une variance négative ...

De plus, ne calculez jamais, jamais, jamais a ^ 2 comme pow (a, 2), a * a est presque certainement plus rapide.

De loin, la meilleure façon de calculer un écart-type est méthode de Welford . Mon C est très rouillé, mais il pourrait ressembler à:

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 vous avez la population entier (par opposition à un échantillon population), utilisez alors return Math.Sqrt(S / (k-1));.

EDIT: J'ai mis à jour le code selon les remarques de Jason ...

EDIT: J'ai également mis à jour le code selon les remarques d'Alex ...

100
Jaime

10 fois plus rapide solution que celle de Jaime, mais attention cela, comme l'a souligné Jaime:

"Bien que l'algorithme de la somme des carrés fonctionne très bien la plupart du temps, il peut causer de gros problèmes si vous avez affaire à des nombres très grands. En gros, vous pouvez vous retrouver avec une variance négative"

Si vous pensez que vous avez affaire à de très grands nombres ou à une très grande quantité de nombres, vous devez calculer en utilisant les deux méthodes, si les résultats sont égaux, vous savez avec certitude que vous pouvez utiliser la méthode "my" pour votre cas.

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

La réponse acceptée par Jaime est excellente, sauf que vous devez diviser par k-2 dans la dernière ligne (vous devez diviser par "number_of_elements-1"). Mieux encore, commencez k à 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));
}
5
AlexB

La bibliothèque Math.NET vous fournit cela de la boîte.

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

Voir PopulationStandardDeviation pour plus d'informations.

4
Chris Marisic

Extrait de code:

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

Vous pouvez éviter de faire deux passes sur les données en accumulant la moyenne et le carré moyen

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

et formant

sigma = sqrt(meansqr - mean^2)

Un facteur de cnt/(cnt-1) est également souvent approprié.

BTW - Le premier passage sur les données dans les réponses Demi et McWafflestix est caché dans les appels à Average. Ce genre de chose est certainement trivial sur une petite liste, mais si la liste dépasse la taille du cache, ou même de l'ensemble de travail, cela devient un accord d'enchère.

2
dmckee

J'ai trouvé que la réponse utile de Rob ne correspondait pas tout à fait à ce que je voyais en utilisant Excel. Pour correspondre à Excel, j'ai transmis la moyenne de ValueList au calcul StandardDeviation.

Voici mes deux cents ... et vous pouvez clairement calculer la moyenne mobile (ma) à partir de valueList à l'intérieur de la fonction - mais il m'arrive d'avoir déjà avant d'avoir besoin de la 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));
}       
1
hongkonggil

Avec des méthodes d'extension.

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);
        }
    }
}
1
Rikin Patel
/// <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));
}
0
oleksii

Il s'agit de l'écart-type de la population

private double calculateStdDev(List<double> values)
{
    double average = values.Average();
    return Math.Sqrt((values.Select(val => (val - average) * (val - average)).Sum()) / values.Count);
}

Pour Exemple d'écart-type, remplacez simplement [values.Count] par [values.Count -1] dans le code ci-dessus.

Assurez-vous de ne pas avoir qu'un seul point de données dans votre ensemble.

0
Amey Vartak

Nous pourrons peut-être utiliser le module de statistiques en Python. Il a des commandes stedev () et pstdev () pour calculer respectivement l'écart type de l'échantillon et de la population.

détails ici: https://www.geeksforgeeks.org/python-statistics-stdev/

importer des statistiques en tant que st print (st.ptdev (dataframe ['column name'])))

0
Dhaval

Le problème avec toutes les autres réponses est qu'elles supposent que vous avez vos données dans un grand tableau. Si vos données arrivent à la volée, ce serait une meilleure approche. Cette classe fonctionne quelle que soit la façon dont vous stockez vos données ou si vous les stockez. Il vous donne également le choix entre la méthode Waldorf ou la méthode de la somme des carrés. Les deux méthodes fonctionnent en un seul passage.

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(); }
}
0
MiguelMunoz