我有一个List<int>
,我需要删除异常值,所以我想使用一种方法,我只取中间的n。我想要的中间值,而不是索引。
例如,给定下面的列表,如果我想要中间的80%,我希望11和100会被删除。
11日,22日,22日,33岁,44岁,44岁,55岁,55岁,55100。
在LINQ中是否有一个简单/内置的方法来做到这一点?
我有一个
List<int>
,我需要删除异常值,所以我想使用一种方法,我只取中间的n。我想要的是中间的值,而不是索引。
正确地去除异常值完全取决于准确描述数据分布的统计模型,而您没有提供给我们。
假设它是正态(高斯)分布,这是你想要做的。
首先计算平均值。这很简单;它只是总和除以项目的数量。
其次,计算标准差。标准偏差是衡量数据在平均值周围的"分布"程度。- 取每个点与平均值的差值
- 求差的平方
- 取平方的平均值,这是方差
- 取方差的平方根——这是标准差
在正态分布中,80%的项目在均值的1.2个标准差范围内。比如,假设均值是50,标准差是20。你会期望80%的样本落在50 - 1.2 * 20和50 + 1.2 * 20之间。然后,您可以从列表中过滤出超出该范围的项。
但请注意,这是而不是删除"异常值"。这是去除距离平均值超过1.2个标准差的元素,以便在平均值周围获得80%的区间。在正态分布中,人们期望定期看到"异常值"。99.73%的项目在平均值的三个标准差范围内,这意味着如果你有一千个观察值,那么在平均值之外看到两个或三个超过三个标准差的观察值是完全正常的!事实上,如果在给定1000个观测值的情况下,距离平均值超过3个标准差的5个观测值以内,则可能不表示异常值。我认为你需要非常仔细地定义outlier是什么意思,并描述为什么你要试图消除它们。看起来像异常值的东西可能根本不是异常值,它们是你应该注意的真实数据。
另外,请注意,如果正态分布不正确,那么这些分析都是不正确的!消除看似异常值的数据可能会遇到很大的麻烦而实际上整个统计模型都是错的。如果模型比正态分布更"重尾",那么异常值是常见的,并且实际上不是异常值。小心!如果你的分布不是正态分布,那么你需要告诉我们分布是什么,然后我们才能建议如何识别异常值并消除它们。
您可以使用Enumerable.OrderBy
方法对列表进行排序,然后使用Enumerable.Skip
和Enumerable.Take
函数,例如:
var result = nums.OrderBy(x => x).Skip(1).Take(8);
其中nums
是你的整数列表。
如果你只想要"n
的中间值",那么找出Skip
和Take
的参数值应该是这样的:
nums.OrderBy(x => x).Skip((nums.Count - n) / 2).Take(n);
但是,当(nums.Count - n) / 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);
通常情况下,如果您想要从一组值中排除统计异常值,您需要计算该集合的算术平均值和标准偏差,然后删除距离平均值较远的值(以标准偏差度量)。正态分布;经典的钟形曲线—显示以下属性:
- 约68%的数据将位于平均值±1个标准差范围内。
- 约95%的数据位于平均值±2个标准差范围内。
- 约99.7%的数据位于平均值±3个标准差范围内。
您可以在http://www.codeproject.com/KB/linq/LinqStatistics.aspx
我不打算质疑计算异常值的有效性,因为我也有类似的需要进行这种选择。取中间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);
跳过第一个项目,并在最后一个项目之前停止,只给您中间的n个项目。下面是一个. net Fiddle的链接,它演示了这个查询。
https://dotnetfiddle.net/p1z7em我有一个列表,我需要删除异常值,所以我想使用一种方法,我只取中间的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);
}