【问题标题】:Calculate Percentile using LINQ使用 LINQ 计算百分位数
【发布时间】:2015-02-25 06:43:01
【问题描述】:

全部,

查看了 StackOverflow 和更广泛的互联网后,我仍在努力有效地使用 LINQ 计算百分位数。

percentile 是统计中使用的度量,表示一组观察值中给定百分比的观察值低于该值。下面的示例尝试将值列表转换为一个数组,其中每个(唯一)值表示为关联的百分位数。 列表的 min() 和 max() 必然是返回数组百分位数的 0% 和 100%。

使用LINQPad,下面的代码生成所需的输出一个VP[]:

这可以解释为: - 在 0% 时,最小值为 1 - 100% 时最大值为 3 - 在最小值和最大值之间的 50% 处,值为 2

void Main()
{
    var list = new List<double> {1,2,3};
    double denominator = list.Count - 1;   
    var answer = list.Select(x => new VP
        {
            Value = x,
            Percentile = list.Count(y => x > y) / denominator
        })
        //.GroupBy(grp => grp.Value) --> commented out until attempted duplicate solution 
        .ToArray();
    answer.Dump();
}

public struct VP
{
    public double Value;
    public double Percentile;
}

但是,当“列表”包含重复条目(例如 1,2,**2,**3)时,这会返回不正确的 VP[]:

我尝试按列表中的唯一值分组(通过包括“.GroupBy(grp => grp.Value)”)未能产生预期的结果(值 =2,百分位数 = 0.666):

欢迎所有建议。包括考虑到“list.Count(y => x > y)”的重复迭代,这是否是一种有效的方法。

一如既往,谢谢 香农

【问题讨论】:

  • 我不清楚你到底想计算什么,也许我的数学有点生疏……你能告诉我你所说的比例/百分位数是什么意思吗?与分母有关。谢谢
  • 为什么要用list.Count()来计算分母?目前还不清楚您要完成什么。
  • @TheCatWhisperer 对于原始列表中的每个元素(即 1,2,3)我需要值和百分位数(例如 value = 2 标记分布的 50% 点。我正在组装一个概率各种密度函数。可以快速引用 VP[] 来确定 50% 的元素小于或等于“2”。
  • 百分位数是如何分布的?通常,学生,等等?
  • 如果您重新表述您的问题并包含列表代表什么以及价值和比例是什么的定义,这将非常有帮助。

标签: c# linq percentile


【解决方案1】:

我不确定我是否理解这个问题的要求。当我运行接受的答案的代码时,我得到了这个结果:

但如果我将输入更改为:

var dataSet = new List<double> { 1, 1, 1, 1, 2, 3, 3, 3, 2 };

...然后我得到这个结果:

使用“列表的 min() 和 max() 必须是返回的数组百分位数的 0% 和 100%”这一行。在我看来,OP 要求值从 0 到 1,但更新后的结果超出了 1。

第一个值应该是 0% 在我看来也是错误的,因为我不确定这对数据的上下文意味着什么。

阅读链接的 Wikipedia 页面后,似乎 OP 实际上是在尝试进行反向计算来计算百分位值。事实上,文章说 0 的百分位数是未定义的。这是有道理的,因为百分位数 0 将是空值集 - 空集的最大值是多少?

OP 似乎正在根据这些值计算百分位数。因此,从这个意义上说,并且知道 0 是未定义的,似乎最适合计算的值是等于或低于集合中每个不同值的值的百分比。

现在,如果我使用 Microsoft 的响应式框架团队的交互式扩展 (NuGet "Ix-Main"),那么我可以运行以下代码:

var dataSet = new List<double> { 1, 1, 1, 1, 2, 3, 3, 3, 2 };

var result =
    dataSet
        .GroupBy(x => x)
        .Scan(
            new VP()
            {
                Value = double.MinValue, Proportion = 0.0
            },
            (a, x) =>
                new VP()
                {
                    Value = x.Key,
                    Proportion = a.Proportion + (double)x.Count() / dataSet.Count
                });

我得到这个结果:

这告诉我大约 44% 的值是 1;大约 67% 的值是 1 或 2;并且 100% 的值是 1、2 或 3。

在我看来,这是满足需求的最合乎逻辑的计算。

【讨论】:

    【解决方案2】:

    我就是这样做的。我更改了一些变量名称以使上下文更清晰。

    var dataSet = new List<double> { 1, 2, 3, 2 };
    double denominator = dataSet.Count - 1;
    var uniqueValues = dataSet.Distinct();
    var vp = dataSet.Select(value => new VP
    {
        Value = value,
        Proportion = dataSet.Count(datum => value > datum) / denominator
    });
    
    var answer = uniqueValues.Select(u => new VP{
        Value = u,
        Proportion = vp.Where(v => v.Value == u).Select(x => x.Proportion).Sum()
    });
    

    【讨论】:

    • Ed,谢谢你,这正是我所追求的。对我在定义问题时缺乏明确性表示歉意。我会改进的。
    • 它似乎效率很低。 Count(datum =&gt; value &gt; datum) 将一遍又一遍地遍历整个集合。
    【解决方案3】:
    void Main()
    {
        var list = new List<double> {1,2,3};
        double denominator = list.Count - 1;   
        var answer = list.OrderBy(x => x).Select(x => new VP
            {
                Value = x,
                Proportion = list.IndexOf(x) / denominator
            })
            .ToArray();
        answer.Dump();
    }
    
    public struct VP
    {
        public double Value;
        public double Proportion;
    }
    

    【讨论】:

    • 感谢您的回复。你的方法更有效是有道理的。但是,如果存在重复条目(例如 1,2,2,3),问题仍然存在,其中返回的 VP[] 中的值“2”重复,每个百分位数为 33%,而不是唯一的“2”百分位数 67%
    • 当列表中只有 3 个项目时,我看不出 2 怎么可能出现两次。也许这是 linqpad 中的错误?
    猜你喜欢
    • 2017-09-04
    • 2011-12-29
    • 2013-06-20
    • 2016-07-28
    • 2017-08-29
    • 2012-10-28
    • 2021-02-26
    • 1970-01-01
    • 2021-09-22
    相关资源
    最近更新 更多