69

我需要计算通用列表的标准偏差。我将尝试包含我的代码。它是一个包含数据的通用列表。数据主要是浮点数和整数。这是我的代码,与它相关,但没有详细介绍:

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

这是需要计算标准偏差的函数:

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

        }
    }
}

忽略 LatchStdev() 函数内部的内容,因为我确定它不正确。这只是我计算st dev的糟糕尝试。我知道如何处理双打列表,但不知道通用数据列表。如果有人有这方面的经验,请帮助。

4

4 回答 4

183

上面的示例有点不正确,如果您的总体集为 1,则可能会出现除以零的错误。以下代码稍微简单一些,并给出“总体标准差”结果。(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)));
    }
}
于 2011-06-06T12:57:22.070 回答
79

这篇文章应该对你有所帮助。它创建了一个计算一系列double值的偏差的函数。您所要做的就是提供一系列适当的数据元素。

结果函数是:

private double CalculateStandardDeviation(IEnumerable<double> values)
{   
  double standardDeviation = 0;

  if (values.Any()) 
  {      
     // 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.      
     standardDeviation = Math.Sqrt((sum) / (values.Count()-1));   
  }  

  return standardDeviation;
}

这很容易适应任何泛型类型,只要我们为正在计算的值提供选择器。LINQ 非常适合这一点,该Select功能允许您从自定义类型的通用列表中投影一系列数值,以计算其标准偏差:

List<ValveData> list = ...
var result = list.Select( v => (double)v.SomeField )
                 .CalculateStdDev();
于 2010-06-29T14:39:16.117 回答
27

尽管接受的答案在数学上似乎是正确的,但从编程的角度来看它是错误的——它枚举了相同的序列 4 次。如果底层对象是列表或数组,这可能没问题,但如果输入是过滤/聚合/等 linq 表达式,或者数据直接来自数据库或网络流,这将导致性能低得多。

我强烈建议不要重新发明轮子并使用更好的开源数学库 Math.NET 之一。我们一直在公司中使用该库,并且对性能非常满意。

PM> 安装包 MathNet.Numerics

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

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

有关详细信息,请参阅http://numerics.mathdotnet.com/docs/DescriptiveStatistics.html

最后,对于那些想要获得尽可能快的结果并牺牲一些精度的人,请阅读“一次性”算法https://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods

于 2012-04-13T19:49:05.717 回答
0

我看到你在做什么,我使用类似的东西。在我看来,你走得还不够远。我倾向于将所有数据处理封装到一个类中,这样我就可以缓存计算出来的值,直到列表发生变化。例如:

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

您会注意到,使用这种方法,只有第一个求平均值的请求才会实际计算平均值。在那之后,只要我们不从列表中添加(或删除或修改,但那些未显示的)任何内容,我们基本上可以得到平均值。

此外,由于在标准差算法中使用了平均值,所以首先计算标准差将免费得到平均值,并且首先计算平均值将在标准差计算中给我们一点性能提升,假设我们记得检查标志。

此外!像平均函数这样的地方,无论如何你已经遍历了每个值,是缓存最小值和最大值之类的东西的好时机。当然,对这些信息的请求需要首先检查它们是否已被缓存,与仅使用列表查找最大值相比,这可能会导致相对减速,因为它会完成所有额外的工作来设置所有相关的缓存,而不仅仅是一个你的访问。

于 2012-10-29T17:08:15.950 回答