public static class Extensions
{
public static double StdDev(this IEnumerable<double> values)
{
double ret = 0;
int count = values.Count();
if (count > 1)
{
//Compute the Average
double avg = values.Average();
//Perform the Sum of (value-avg)^2
double sum = values.Sum(d => (d - avg) * (d - avg));
//Put it all together
ret = Math.Sqrt(sum / count);
}
return ret;
}
}
public static class Extensions
{
public static double StdDev<T>(this IEnumerable<T> list, Func<T, double> values)
{
// ref: https://stackoverflow.com/questions/2253874/linq-equivalent-for-standard-deviation
// ref: http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/
var mean = 0.0;
var sum = 0.0;
var stdDev = 0.0;
var n = 0;
foreach (var value in list.Select(values))
{
n++;
var delta = value - mean;
mean += delta / n;
sum += delta * (value - mean);
}
if (1 < n)
stdDev = Math.Sqrt(sum / (n - 1));
return stdDev;
}
}
public static double StdDev(this IEnumerable<double> values)
{
var count = values?.Count() ?? 0;
if (count <= 1) return 0;
var avg = values.Average();
var sum = values.Sum(d => Math.Pow(d - avg, 2));
return Math.Sqrt(sum / count);
}
编辑2020-08-27: 我接受了@大卫Clarke的评论,进行了一些性能测试,结果如下:
public static (double stdDev, double avg) StdDevFast(this List<double> values)
{
var count = values?.Count ?? 0;
if (count <= 1) return (0, 0);
var avg = GetAverage(values);
var sum = GetSumOfSquareDiff(values, avg);
return (Math.Sqrt(sum / count), avg);
}
private static double GetAverage(List<double> values)
{
double sum = 0.0;
for (int i = 0; i < values.Count; i++)
sum += values[i];
return sum / values.Count;
}
private static double GetSumOfSquareDiff(List<double> values, double avg)
{
double sum = 0.0;
for (int i = 0; i < values.Count; i++)
{
var diff = values[i] - avg;
sum += diff * diff;
}
return sum;
}
我用一百万个随机的二人组来测试这个 原始实现运行时间约为48 ms 性能优化实现2- 3 ms 所以这是一个显著的改进。 一些有趣的细节: 摆脱数学。Pow带来了33 ms的提升! 列表而不是IEnumerable 6 ms 手动平均计算4 ms For-loops代替ForEach-loops 2 ms 数组而不是列表带来的只是~2%的改进,所以我跳过了这一点 使用single而不是double不会带来任何好处 进一步降低代码并使用后藤(是的,GOTO...自从90年代的汇编程序以来就没有用过这个了……)代替for循环不划算,谢天谢地! 我也测试了并行计算,这是有意义的列表> 200.000项目似乎硬件和软件需要初始化了很多,这是小名单适得其反。 所有测试连续执行两次,以消除预热时间。
public static double StdDev(this IEnumerable<int> values, bool as_sample = false)
{
var count = values.Count();
if (count > 0) // check for divide by zero
// Get the mean.
double mean = values.Sum() / count;
// Get the sum of the squares of the differences
// between the values and the mean.
var squares_query =
from int value in values
select (value - mean) * (value - mean);
double sum_of_squares = squares_query.Sum();
return Math.Sqrt(sum_of_squares / (count - (as_sample ? 1 : 0)))
}
8条答案
按热度按时间bkhjykvo1#
你可以做你自己的扩展计算它
如果你有一个样本而不是整个人口,那么你应该使用
ret = Math.Sqrt(sum / (count - 1));
。从Adding Standard Deviation to LINQ by Chris Bennett转换为扩展。
puruo6ea2#
Dynami的答案是有效的,但要通过多次数据传递才能获得结果。这是一种计算样品标准差的单程方法:
这是 * 样本标准差 *,因为它除以
n - 1
。对于正态标准差,您需要除以n
。这使用了Welford's method,与
Average(x^2)-Average(x)^2
方法相比,Welford's method具有更高的数值精度。bvn4nwqk3#
这将把David Clarke's answer转换成一个扩展,它遵循与其他聚合LINQ函数(如Average)相同的形式。
用法如下:
var stdev = data.StdDev(o => o.number)
hl0ma9xz4#
5n0oy7gb5#
直截了当地说(C# > 6.0),Dynamis的答案是:
编辑2020-08-27:
我接受了@大卫Clarke的评论,进行了一些性能测试,结果如下:
我用一百万个随机的二人组来测试这个
原始实现运行时间约为48 ms
性能优化实现2- 3 ms
所以这是一个显著的改进。
一些有趣的细节:
摆脱数学。Pow带来了33 ms的提升!
列表而不是IEnumerable 6 ms
手动平均计算4 ms
For-loops代替ForEach-loops 2 ms
数组而不是列表带来的只是~2%的改进,所以我跳过了这一点
使用single而不是double不会带来任何好处
进一步降低代码并使用后藤(是的,GOTO...自从90年代的汇编程序以来就没有用过这个了……)代替for循环不划算,谢天谢地!
我也测试了并行计算,这是有意义的列表> 200.000项目似乎硬件和软件需要初始化了很多,这是小名单适得其反。
所有测试连续执行两次,以消除预热时间。
bxfogqkk6#
简单的4行,我使用了一个双精度列表,但可以使用
IEnumerable<int> values
pepwfjgg7#
z9smfwbn8#
在 * 一般 * 情况下,我们希望在 * 一次通过 * 中计算
StdDev
:如果values
是 file 或RDBMS cursor,可以在计算average和sum之间进行 * 更改 *,会怎么样?我们会得到 * 不一致的结果 *。下面的代码只使用了一次:对于 sample
StdDev
也是同样的想法: