从集合中返回中间n(值不是索引)

问题描述 投票:7回答:6

我有一个List<int>,我需要删除异常值,所以想要使用一种方法,我只采取中间n。我想要中间值,而不是索引。

例如,给定以下列表如果我想要中间80%我会期望11和100将被删除。

11,22,22,33,44,44,55,55,55,100.

在LINQ中有一个简单/内置的方法吗?

c# linq algorithm
6个回答
11
投票

我有一个List<int>,我需要删除异常值,所以想要使用一种方法,我只采取中间n。我想要中间值,而不是索引。

正确删除异常值完全取决于准确描述数据分布的统计模型 - 您没有为我们提供。

假设它是正态(高斯)分布,这就是你想要做的。

首先计算平均值。这很简单;它只是总和除以项目数。

其次,计算标准偏差。标准差是衡量数据“分散”在平均值附近的指标。通过以下方式计算:

  • 从均值中得出每个点的差异
  • 平方差
  • 取平方的均值 - 这是方差
  • 取方差的平方根 - 这是标准差

在正态分布中,80%的项目在平均值的1.2标准偏差内。因此,例如,假设平均值为50,标准差为20.您可以预期80%的样本将介于50 - 1.2 * 20和50 + 1.2 * 20之间。然后您可以从列表中过滤掉项目超出该范围。

但请注意,这并不是删除“异常值”。这是为了消除平均值超过1.2标准差的元素,以便在均值周围获得80%的间隔。在正态分布中,人们期望定期看到“异常值”。 99.73%的项目在平均值的三个标准偏差范围内,这意味着如果您有一千个观测值,那么在平均值之外看到两个或三个观测值超过三个标准偏差是完全正常的!事实上,在给出一千个观测结果时,在距离平均值超过三个标准偏差的任何地方,可能并不表示异常值。

我认为你需要非常仔细地定义你的异常值,并描述你为什么试图消除它们。看起来像异常值的东西可能根本不是异常值,它们是你应该注意的真实数据。

另请注意,如果正态分布不正确,则此分析均不正确!你可能会遇到很大的麻烦,消除看起来像异常值的东西,事实上你实际上已经弄错了整个统计模型。如果模型比正态分布更“尾重”,那么异常值是常见的,而不是实际的异常值。小心!如果您的分布不正常,那么在我们建议如何识别异常值并消除它们之前,您需要告诉我们分布情况。


4
投票

您可以使用Enumerable.OrderBy方法对列表进行排序,然后使用Enumerable.SkipEnumerable.Take函数,例如:

var result = nums.OrderBy(x => x).Skip(1).Take(8);

nums是你的整数列表。

如果你只想要“中间Skip值”,找出用作Taken参数的值应该看起来像这样:

nums.OrderBy(x => x).Skip((nums.Count - n) / 2).Take(n);

但是,当(nums.Count - n) / 2的结果不是整数时,您希望代码如何表现?


2
投票

假设你没有做任何加权平均有趣的业务:

List<int> ints = new List<int>() { 11,22,22,33,44,44,55,55,55,100 };

int min = ints.Min();
double range = (ints.Max() - min);

var results = ints.Select(o => new { IntegralValue = o, Weight = (o - ints.Min()) / range} );

results.Where(o => o.Weight >= .1 && o.Weight < .9);

然后,您可以根据需要过滤重量。根据需要放下顶部/底部n%。

在你的情况下:

results.Where(o => o.Weight >= .1 && o.Weight < .9)

编辑:作为扩展方法,因为我喜欢扩展方法:

public static class Lulz
{
    public static List<int> MiddlePercentage(this List<int> ints, double Percentage)
    {
        int min = ints.Min();
        double range = (ints.Max() - min);

        var results = ints.Select(o => new { IntegralValue = o, Weight = (o - ints.Min()) / range} );

        double tolerance = (1 - Percentage) / 2;
        return results.Where(o => o.Weight >= tolerance && o.Weight < 1 - tolerance).Select(o => o.IntegralValue).ToList();
    }
}

用法:

List<int> ints = new List<int>() { 11,22,22,33,44,44,55,55,55,100 };
var results = ints.MiddlePercentage(.8);

2
投票

通常,如果要从一组值中排除统计异常值,则需要计算集合的算术平均值和标准差,然后删除比平均值更远的值(以标准偏差度量)。正态分布 - 您的经典钟形曲线 - 具有以下属性:

  • 大约68%的数据与平均值的差异在+/- 1之内。
  • 大约95%的数据与平均值的差异在+/- 2之内。
  • 大约99.7%的数据将在平均值的+/- 3标准差内。

您可以在http://www.codeproject.com/KB/linq/LinqStatistics.aspx获得用于计算标准差(和其他统计函数)的Linq扩展方法


0
投票

我不会质疑计算异常值的有效性,因为我有类似的需要做这种选择。中间n的具体问题的答案是:

List<int> ints = new List<int>() { 11,22,22,33,44,44,55,55,55,100 };
var result = ints.Skip(1).Take(ints.Count() - 2);

这会跳过第一个项目,并在最后一个项目之前停止。这是.NET Fiddle演示此查询的链接。

https://dotnetfiddle.net/p1z7em


0
投票

我有一个列表,我需要删除异常值,所以想要使用一种方法,我只采取中间n。我想要中间值,而不是索引。

如果我理解正确,我们希望保留任何落在11-100范围中间80%的值,或者

min + (max - min - (max - min) * 0.8) / 2 < x < max - (max - min - (max - min) * 0.8) / 2

假设有序列表,我们可以SkipWhile值低于lowerBound,然后TakeWhile数字比upperBound更爱

public void Calculalte()
{
    var numbers = new[] { 11, 22, 22, 33, 44, 44, 55, 55, 55, 100 };

    var percentage = 0.8;

    var result = RemoveOutliers(numbers, percentage);
}

private IEnumerable<int> RemoveOutliers(int[] numbers, double percentage)
{
    int min = numbers.First();
    int max = numbers.Last();
    double range = (max - min);
    double lowerBound = min + (range - range * percentage) / 2;
    double upperBound = max - (range - range * percentage) / 2;
    return numbers.SkipWhile(n => n < lowerBound).TakeWhile(n => n < upperBound);   
}
© www.soinside.com 2019 - 2024. All rights reserved.