web-dev-qa-db-fra.com

Écart type de liste générique?

J'ai besoin de calculer l'écart type d'une liste générique. Je vais essayer d'inclure mon code. C'est une liste générique contenant des données. Les données sont principalement des flottants et des entiers. Voici mon code qui lui est relatif sans entrer dans les détails:

namespace ValveTesterInterface
{
    public class ValveDataResults
    {
        private List<ValveData> m_ValveResults;

        public ValveDataResults()
        {
            if (m_ValveResults == null)
            {
                m_ValveResults = new List<ValveData>();
            }
        }

        public void AddValveData(ValveData Valve)
        {
            m_ValveResults.Add(Valve);
        }

Voici la fonction où l'écart type doit être calculé:

        public float LatchStdev()
        {

            float sumOfSqrs = 0;
            float meanValue = 0;
            foreach (ValveData value in m_ValveResults)
            {
                meanValue += value.LatchTime;
            }
            meanValue = (meanValue / m_ValveResults.Count) * 0.02f;

            for (int i = 0; i <= m_ValveResults.Count; i++) 
            {   
                sumOfSqrs += Math.Pow((m_ValveResults - meanValue), 2);  
            }
            return Math.Sqrt(sumOfSqrs /(m_ValveResults.Count - 1));

        }
    }
}

Ignorez ce qui se trouve à l'intérieur de la fonction LatchStdev () car je suis sûr que ce n'est pas correct. C'est juste ma pauvre tentative de calculer le st dev. Je sais le faire d'une liste de doubles, mais pas d'une liste de liste de données génériques. Si quelqu'un a de l'expérience dans ce domaine, aidez-moi.

55
Tom Hangler

Cet article devrait vous aider. Il crée une fonction qui calcule l'écart d'une séquence de valeurs double. Il vous suffit de fournir une séquence d'éléments de données appropriés.

La fonction résultante est:

private double CalculateStdDev(IEnumerable<double> values)
{   
  double ret = 0;
  if (values.Count() > 0) 
  {      
     //Compute the Average      
     double avg = values.Average();
     //Perform the Sum of (value-avg)_2_2      
     double sum = values.Sum(d => Math.Pow(d - avg, 2));
     //Put it all together      
     ret = Math.Sqrt((sum) / (values.Count()-1));   
  }   
  return ret;
}

C'est assez facile à adapter pour n'importe quel type générique, tant que nous fournissons un sélecteur pour la valeur calculée. LINQ est idéal pour cela, la fonction Select vous permet de projeter à partir de votre liste générique de types personnalisés une séquence de valeurs numériques pour laquelle calculer l'écart-type:

List<ValveData> list = ...
var result = list.Select( v => (double)v.SomeField )
                 .CalculateStdDev();
63
LBushkin

L'exemple ci-dessus est légèrement incorrect et pourrait avoir une erreur de division par zéro si votre ensemble de population est 1. Le code suivant est un peu plus simple et donne le résultat "écart type de population". ( http://en.wikipedia.org/wiki/Standard_deviation )

using System;
using System.Linq;
using System.Collections.Generic;

public static class Extend
{
    public static double StandardDeviation(this IEnumerable<double> values)
    {
        double avg = values.Average();
        return Math.Sqrt(values.Average(v=>Math.Pow(v-avg,2)));
    }
}
146
Jonathan DeMarks

Même si la réponse acceptée semble mathématiquement correcte, elle est erronée du point de vue de la programmation - elle énumère la même séquence 4 fois. Cela peut être correct si l'objet sous-jacent est une liste ou un tableau, mais si l'entrée est une expression linq filtrée/agrégée/etc, ou si les données proviennent directement de la base de données ou du flux réseau, cela entraînerait des performances beaucoup plus faibles.

Je recommande fortement de ne pas réinventer la roue et d'utiliser l'une des meilleures bibliothèques mathématiques open source Math.NET. Nous utilisons cette bibliothèque dans notre entreprise et sommes très satisfaits de la performance.

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 http://numerics.mathdotnet.com/docs/DescriptiveStatistics.html pour plus d'informations.

Enfin, pour ceux qui veulent obtenir le résultat le plus rapide possible et sacrifier une certaine précision, lisez l'algorithme "en un seul passage" https://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods

18
Yurik

Je vois ce que tu fais et j'utilise quelque chose de similaire. Il me semble que vous n'allez pas assez loin. J'ai tendance à encapsuler tous les traitements de données dans une seule classe, de cette façon, je peux mettre en cache les valeurs calculées jusqu'à ce que la liste change. par exemple:

public class StatProcessor{
private list<double> _data; //this holds the current data
private _avg; //we cache average here
private _avgValid; //a flag to say weather we need to calculate the average or not
private _calcAvg(); //calculate the average of the list and cache in _avg, and set _avgValid
public double average{
     get{
     if(!_avgValid) //if we dont HAVE to calculate the average, skip it
        _calcAvg(); //if we do, go ahead, cache it, then set the flag.
     return _avg; //now _avg is garunteed to be good, so return it.
     }
}
...more stuff
Add(){
//add stuff to the list here, and reset the flag
}
}

Vous remarquerez qu'en utilisant cette méthode, seule la première demande de moyenne calcule réellement la moyenne. Après cela, tant que nous n'ajoutons pas (ou ne supprimons pas ou ne modifions pas du tout, mais ceux qui ne sont pas affichés) quoi que ce soit de la liste, nous pouvons obtenir la moyenne pour pratiquement rien.

De plus, étant donné que la moyenne est utilisée dans l'algorithme pour l'écart-type, le calcul de l'écart-type nous donnera d'abord la moyenne gratuitement, et le calcul de la moyenne d'abord nous donnera un petit coup de pouce dans les performances du calcul de l'écart-type, en supposant que nous nous souvenons de vérifiez le drapeau.

En outre! des endroits comme la fonction moyenne, où vous parcourez déjà chaque valeur de toute façon, est un bon moment pour mettre en cache des choses comme les valeurs minimales et maximales. Bien sûr, les demandes de ces informations doivent d'abord vérifier si elles ont été mises en cache, ce qui peut entraîner un ralentissement relatif par rapport à la recherche du maximum à l'aide de la liste, car il fait tout le travail supplémentaire de configuration de tous les caches concernés, pas seulement le l'un de vos accès.

0
Benjamin