您当前的位置: 首页 > 

寒冰屋

暂无认证

  • 0浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

使用LINQ计算基本统计

寒冰屋 发布时间:2020-12-01 14:40:47 ,浏览量:0

  • GitHub上的最新资源
  • 现在带有NuGet软件包
介绍

在进行另一个项目时,我发现自己需要计算各种基础类型的各种数据集的基本统计信息。LINQ有Count,Min,Max和Average,但没有其他统计汇总。像我在这种情况下经常做的那样,我从Google开始,认为其他人一定已经为此写了一些方便的扩展方法。有大量的统计和数值处理程序包,但是我想要的是一种简单且轻便的基本统计数据实现:方差(样本和总体),标准差(样本和总体),协方差,皮尔逊(卡方),范围,中位数,最小二乘,均方根,直方图和众数。

背景

我已经针对Enumerable.Average的各种重载对API进行了建模,因此您可以在这些方法接受的相同类型的集合上使用这些方法。希望这将使用法熟悉和易于使用。

这意味着常见数字数据类型及其Nullable计数器部分的集合会产生重载,而便捷的选择器也将产生重载。

public static decimal? StandardDeviation(this IEnumerable source);
public static decimal StandardDeviation(this IEnumerable source);
public static double? StandardDeviation(this IEnumerable source);
public static double StandardDeviation(this IEnumerable source);
public static float? StandardDeviation(this IEnumerable source);
public static float StandardDeviation(this IEnumerable source);
public static double? StandardDeviation(this IEnumerable source);
public static double StandardDeviation(this IEnumerable source);
public static double? StandardDeviation(this IEnumerable source);
public static double StandardDeviation(this IEnumerable source);
public static decimal? StandardDeviation
    (this IEnumerable source, Func selector);
public static decimal StandardDeviation
    (this IEnumerable source, Func selector);
public static double? StandardDeviation
    (this IEnumerable source, Func selector);
public static double StandardDeviation
    (this IEnumerable source, Func selector);
public static float? StandardDeviation
    (this IEnumerable source, Func selector);
public static float StandardDeviation
    (this IEnumerable source, Func selector);
public static double? StandardDeviation
    (this IEnumerable source, Func selector);
public static double StandardDeviation
    (this IEnumerable source, Func selector);
public static double? StandardDeviation
    (this IEnumerable source, Func selector);
public static double StandardDeviation
    (this IEnumerable source, Func selector);

所有采用Nullable类型集合的重载仅在计算结果中包括实际值。例如:

public static double? StandardDeviation(this IEnumerable source)
{
    IEnumerable values = source.AllValues();
    if (values.Any())
        return values.StandardDeviation();

    return null;
}

其AllValues方法是:

public static IEnumerable AllValues(this IEnumerable source) where T : struct
{
    Debug.Assert(source != null);
    return source.Where(x => x.HasValue).Select(x => (T)x);
}
关于模式的注意事项

由于值的分布可能没有模式,因此所有Mode方法都返回Nullable类型。例如,在连续的{ 1, 2, 3, 4 }中,没有一个值出现多次。在这种情况下,返回值为null。

在存在多种模式的情况下,Mode返回最大模式(即,出现次数最多的值)。如果最大模式并列,它将返回最大模式集合中的最小值。

还有两种方法可以计算一系列的所有模式。它们以模态降序返回所有模式IEnumerable中的一个。

统计计算

Wikipedia的链接,描述和数学图像。

方差

方差是对一个变量的所有分数的变化量的度量(不仅仅是给出范围的极端值)。

样本方差通常由小写的西格玛平方表示:σ 2。

 

public static double Variance(this IEnumerable source) 
{ 
    int n = 0;
    double mean = 0;
    double M2 = 0;

    foreach (double x in source)
    {
        n = n + 1;
        double delta = x - mean;
        mean = mean + delta / n;
        M2 += delta * (x - mean);
    }
    return M2 / (n - 1);
}
标准偏差

统计总体,数据集或概率分布的标准偏差是其方差的平方根。

标准偏差通常由小写的σ表示。

 

public static double StandardDeviation(this IEnumerable source) 
{ 
    return Math.Sqrt(source.Variance());
}
中位数

中位数是将样本的上半部分,总体或概率分布与下半部分分开的数字。

public static double Median(this IEnumerable source) 
{ 
    var sortedList = from number in source 
        orderby number 
        select number; 
        
    int count = sortedList.Count(); 
    int itemIndex = count / 2; 
    if (count % 2 == 0) // Even number of items. 
        return (sortedList.ElementAt(itemIndex) + 
                sortedList.ElementAt(itemIndex - 1)) / 2; 
        
    // Odd number of items. 
    return sortedList.ElementAt(itemIndex); 
}
模式

模式是在数据集或概率分布中最频繁出现的值。

public static T? Mode(this IEnumerable source) where T : struct
{
    var sortedList = from number in source
                     orderby number
                     select number;

    int count = 0;
    int max = 0;
    T current = default(T);
    T? mode = new T?();

    foreach (T next in sortedList)
    {
        if (current.Equals(next) == false)
        {
            current = next;
            count = 1;
        }
        else
        {
            count++;
        }

        if (count > max)
        {
            max = count;
            mode = current;
        }
    }

    if (max > 1)
        return mode;

    return null;
}
直方图

直方图是数据的连续分布的表示。在给定连续数据集的情况下,直方图会统计其数据点出现在一组连续值范围(即bin)中的次数。没有单一的方法来确定bin的数量,因为这取决于所执行的数据和分析。有一些标准机制可用于根据数据点的数量来计算bin大小。其中的三个包含在一组BinCount扩展方法中。也有不同的方法来确定每个容器的范围。这些用BinningMode举表示。在所有情况下,除一个以外,bin范围都包含>=最小值范围和 x += Math.Pow(d, 2)); return Math.Sqrt(s / source.Count()); } 使用代码

包含的单元测试应提供许多有关如何使用这些方法的示例,但最简单的是,它们的行为类似于其他可枚举的扩展方法。以下程序...

static void Main(string[] args)
{
      IEnumerable data = new int[] { 1, 2, 5, 6, 6, 8, 9, 9, 9 };

      Console.WriteLine("Count = {0}", data.Count());
      Console.WriteLine("Average = {0}", data.Average());
      Console.WriteLine("Median = {0}", data.Median());
      Console.WriteLine("Mode = {0}", data.Mode());
      Console.WriteLine("Sample Variance = {0}", data.Variance());
      Console.WriteLine("Sample Standard Deviation = {0}", data.StandardDeviation());
      Console.WriteLine("Population Variance = {0}", data.VarianceP());
      Console.WriteLine("Population Standard Deviation = {0}", 
                    data.StandardDeviationP());
      Console.WriteLine("Range = {0}", data.Range());
}

...产生:

Count = 9
Average = 6.11111111111111
Median = 6
Mode = 9
Sample Variance = 9.11111111111111
Sample Standard Deviation = 3.01846171271247
Population Variance = 8.09876543209877
Population Standard Deviation = 2.8458329944146
Range = 8
兴趣点

我没有花很多时间优化计算,因此如果您要评估非常大的数据集,请务必小心。

希望下次需要进行一些简单的统计计算时,可以找到此代码。

关于T4模板的注释

我从来没有发现代码生成模板有多少用处,但是在开发此库时,它们极大地简化了一个用例:即算术运算不能直接在C#泛型中表示。因为运算符被实现为static方法,并且没有机制要求Type具有特定的static方法,所以编译器无法在此代码段中通用地解析“-”:

public static T Range(this IEnumerable source)
{
    // error CS0019: Operator '-' cannot be applied to operands of type 'T' and 'T'
    return source.Max() - source.Min(); 
}

如果您查看框架类中由Average和Sum设置的模式(我在这里试图模拟),他们对int,long,float,double和decimal进行枚举操作。为了避免过多的“复制、粘贴、修改代码和注释中的操作数类型”,T4模板非常方便。

基本上,对于可以对一组固有类型进行操作的每个操作,模板:

  1. 声明支持的类型的列表
  2. 遍历列表并为支持给定固有类型的所有重载生成代码注释、方法签名和主体
 
  public static partial class EnumerableStats
    {
    	
    	/// 
    	/// Computes the Range of a sequence of nullable  values.
    	/// 
        /// The sequence of elements.
        /// The Range.
        public static ? Range(this IEnumerable source)
        {
            IEnumerable values = source.AllValues();
            if (values.Any())
                return values.Range();

            return null;
        }

    	/// 
    	/// Computes the Range of a sequence of  values.
    	/// 
        /// The sequence of elements.
        /// The Range.
        public static  Range(this IEnumerable source)
        {
            return source.Max() - source.Min();
        }

	...
	etc etc
	...

   }

 

这样很好,因为它可以确保所有类型都支持相同的重载集合,并具有相同的实现和代码注释。

关注
打赏
1665926880
查看更多评论

最近更新

热门博客

立即登录/注册

微信扫码登录

0.0457s