我有一个内存数据集,我正在尝试使用 LINQ 获取均匀分布的样本。据我所知,没有任何东西可以开箱即用,所以我正在尝试提出某种组合或扩展来执行采样。
我希望的是我可以像这样使用的东西:
var sample = dataset.Sample(100);
var smallSample = smallDataset.Sample(100);
Assert.IsTrue(dataset.Count() > 100);
Assert.IsTrue(sample.Count() == 100);
Assert.IsTrue(smallDataset.Count() < 100);
Assert.IsTrue(smallSample .Count() == smallDataset.Count());
我开始的构图,但只在某些时候有效:
var sample = dataset
.Select((v,i) => new Tuple<string, int>(v,i))
.Where(t => t.Item2 / (double)(dataset.Count() / SampleSize) % 1 != 0)
.Select(t => t.Item1);
当数据集和样本大小共享一个共同的因子并且样本大小大于数据集大小的 50% 时,此方法有效。或者类似的东西。
任何帮助都会很棒!
更新:所以我有以下非-LINQ 逻辑可以工作,但我想弄清楚这是否可以以某种方式“LINQ”。
var sample = new List<T>();
double sampleRatio = dataset.Count() / sampleSize;
for (var i = 0; i < dataset.Count(); i++)
{
if ((sample.Count() * sampleRatio) <= i)
sample.Add(dataset.Skip(i).FirstOrDefault();
}
最佳答案
我找不到令人满意的 LINQ 解决方案,主要是因为迭代 LINQ 语句不知道它们所处理的序列的长度——这没关系:它完全适合 LINQ 的延迟执行和流式处理方法。当然,可以将长度存储在变量中并在 Where
语句中使用它,但这不符合 LINQ 的函数式(无状态)范式,因此我总是尽量避免这种情况。
Aggregate
语句可以是无状态和长度感知的,但我倾向于使用 Aggregate
找到解决方案,而不是做作且难以阅读。它只不过是一个隐蔽的状态循环; for
和 foreach
需要更多行,但更容易理解。
我可以为您提供一个扩展方法来满足您的需求:
public static IEnumerable<T> TakeProrated<T>(this IEnumerable<T> sequence, int numberOfItems)
{
var local = sequence.ToList();
var n = Math.Min(local.Count, numberOfItems);
var dist = (decimal)local.Count / n;
for (int i = 0; i < n; i++)
{
var index = (int)(Math.Ceiling(i * dist));
yield return local[index];
}
}
想法是首先计算项目之间所需的距离。然后返回请求的元素数量,每次大致跳过这个距离,有时多,有时少,但分布均匀。使用 Math.Ceiling
或 Math.Floor
是任意的,它们要么偏向序列中较高的项目,要么偏向较低的项目。
关于c# - 从大型数据集中获取样本集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42634709/