我有一个内存数据集,我正在尝试使用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'd”。
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();
}
答案 0 :(得分:2)
我找不到令人满意的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
是任意的,它们会对序列中较高的项目产生偏差,或者更低。
答案 1 :(得分:0)
我想我明白你在寻找什么。据我所知,您希望在数据集中只返回一定数量的实体。正如我对您原始帖子的评论所问,您是否尝试使用Take运算符?您正在寻找的是这样的。
// .Skip is optional, but you can use it with it.
// Just ensure that instead of .FirstOrDefault(), you use Take(quantity)
var sample = dataSet.Skip(amt).Take(dataSet.Count() / desiredSampleSize);