2010-06-29 19 views
45

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 avec des données. Les données sont principalement des floats et des ints. Voici mon code qui est par rapport à sans entrer dans de plus de détails:Déviation standard de la liste générique?

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ée:

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

     } 
    } 
} 

Ignore ce qui est à l'intérieur du LatchStdev() Fonction parce que je suis sûr que ce n'est pas correct. C'est juste ma pauvre tentative de calculer le st dév. Je sais comment le faire d'une liste de doubles, cependant pas d'une liste de liste générique de données. Si quelqu'un a de l'expérience dans ce domaine, aidez s'il vous plaît.

Répondre

48

This article devrait vous aider. Il crée une fonction qui calcule l'écart d'une séquence de double valeurs. Tout ce que vous avez à faire est 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; 
} 

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

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

mon C# n'a pas de moyenne. Ça ne se montre pas. C'est l'un de mes problèmes. Aussi, je ne peux pas passer une liste générique à travers ma fonction en tant que paramètres. Le moyen doit être implémenté dans le stdevmethod comme mon code ci-dessus. Mon écart-type est éteint. –

+0

Aussi les gars. C# n'a pas la moyenne (Math.average). Donc, je calcule la moyenne moi-même comme mon code ci-dessus. C'est l'écart-type avec lequel j'ai le plus de problèmes. Merci –

+1

@Tom Hangler, assurez-vous d'ajouter 'using System.Linq;' en haut de votre fichier pour inclure la bibliothèque de fonctions LINQ. Ils comprennent à la fois 'Average()' et 'Select()' – LBushkin

125

L'exemple est légèrement au-dessus incorrecte et pourrait avoir un diviser par zéro si votre population est égale à 1. Le code suivant est un peu plus simple et donne le résultat de «déviation standard de la 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))); 
    } 
} 
+8

+1 pour la simplicité –

+1

Celui-ci devrait être la réponse, il calcule la déviation standard par opposition à la réponse par LBushkin qui calcule réellement l'écart type d'échantillon – Wouter

+0

Kudos pour la simplicité aussi bien. Bien fait. – PseudoToad

17

Même si la réponse acceptée semble mathématiquement correcte, il est faux du point de vue de la programmation - il é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, les performances seront nettement inférieures.

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 avons utilisé cette lib dans notre entreprise et sommes très heureux de la performance.

PM> Installer-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 meilleur résultat possible et sacrifier une certaine précision, lisez « un seul passage » algorithme https://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods

0

Je vois ce que vous faites, et j'utilise quelque chose de similaire. Il me semble que vous n'allez pas assez loin. J'ai tendance à encapsuler tout le traitement des données dans une seule classe, de cette façon je peux mettre en cache les valeurs qui sont 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 que cette méthode, seule la première demande de moyenne calcule en fait la moyenne. Après cela, tant que nous n'ajoutons (ou supprimons, ou modifions pas du tout, mais ceux qui sont montrés) quelque chose de la liste, nous pouvons obtenir la moyenne pour pratiquement rien. De plus, comme la moyenne est utilisée dans l'algorithme pour l'écart-type, le calcul de l'écart-type nous donnera la moyenne gratuitement, et le calcul de la moyenne nous donnera un petit coup de pouce dans le calcul de l'écart-type, en supposant que nous nous souvenions de vérifier le drapeau.

En outre! des endroits comme la fonction moyenne, où vous parcourez déjà toutes les valeurs, est un bon moment pour mettre en cache des choses comme les valeurs minimum et maximum. Bien sûr, les demandes pour cette information doivent d'abord vérifier si elles ont été mises en cache, et cela peut causer un ralentissement relatif comparé à la simple utilisation de la liste, car elle fait tout le travail supplémentaire pour mettre en place toutes les caches concernées. un votre accès.