1

我有一个表达:

Records.OrderBy(o => o.TIME).Where((o, i) => i % interval == 0).ToList();

这在获取大量数据记录并将其缩减为较小的列表方面做得很好。(interval 是要跳过的记录数)。问题是,我想平均一些字段,而不仅仅是跳过它们。我不知道如何在不产生巨大循环的情况下做到这一点。值得注意的是,每条数据记录大约有 90 个字段。想法?

编辑:我希望能够准确地跳过每第 n 条记录,平均 2 个特定字段(纬度和经度(存储为十进制)),并且很可能保持其他 88 个字段不变。

编辑:我想从

    timelat longmany other fields
    1   2   3   field1
    2   3   4   field1
    3   4   5   field1
    4   5   6   field1
    5   6   7   field1
    6   7   8   field1
    7   8   9   field1
    8   9   10  field1
    9   10  11  field1
    10  11  12  field1
    11  12  13  field1
    12  13  14  field1

到:

    timelat     long    other fields            
    3   3   4   field1
    6   6   7   field1
    9   9   10  field1
    12  12  13  field1
4

3 回答 3

3

如果我理解正确,您希望将大量项目分组到较小数量的同等大小的“桶”中,其中对于每个桶,一些字段被聚合(例如平均)并且一些被跳过(即从最后一个项目中获取桶)。

考虑是否可以这样做:

Records
.ToBuckets(interval)
.Select(bucket => new Record {
     Time = bucket.Last().Time,
     Count = bucket.Count,
     Lat = bucket.Average(x => x.Lat), 
     Long = bucket.Average(x => x.Long),
     Other = bucket.First().Other
}
.ToList()

如果这是你想要的,你需要做的就是创建 ToBuckets 方法,这是一个更简单(和通用!)的问题:

public static IEnumerable<IList<T>> ToBuckets<T>(this IEnumerable<T> source, int size)
{
    var bucket = new List<T>(size);
    foreach (var item in source)
    {
        bucket.Add(item);
        if (bucket.Count == size) {
           yield return bucket;
           bucket = new List<T>(size); // or you can use the same one if you're careful
    }

    if (bucket.Count > 0) yield return bucket;
}

(以上是作为支持示例的扩展方法给出的,但这当然也可以是常规方法)。

于 2012-07-14T16:51:16.940 回答
2

如果您想在平均值中包含给定记录,您将不得不触及该记录。 无论您是明确执行此操作还是Linq是否在幕后执行此操作,都必须循环遍历所有记录。

给定的 Linq 表达式只能返回一件事。

您当前拥有的 Linq 表达式将返回过滤后的列表。

您将需要第二个 Linq 表达式(或您自己的循环)来平均所有记录,例如

var avg = Records.Average(r => r.FieldToAverage);

我不确定你的意思

值得注意的是,每条数据记录大约有 90 个字段

您是否必须以某种方式对给定记录中的字段进行平均?如果是这样,它们是什么数据类型?是否有一些现有的方法来枚举所有这些字段?如果没有,您将需要显式访问每个字段,或使用反射来枚举(相关)字段。

于 2012-07-14T16:13:30.110 回答
2

您应该能够将其粘贴在 Where 子句中。它会有点难看,但像这样:

[编辑:从您的编辑中,我现在了解到您想要一些不同的东西。此代码已相应编辑]。

decimal latSum = 0;
decimal longSum = 0;
int count = 0;

var recordList = Records
    .OrderBy(o => o.TIME)
    .Where((o, i) => {
        if (i % interval == 0)
        {
            // Modify the record in place (hope that's OK)
            o.Lat = (o.Lat + latSum) / (count + 1);
            o.Long = (o.Long + longSum) / (count + 1);
            latSum = longSum = count = 0;
            return true;
        }

        latSum += o.Lat;
        longSum += o.Long;
        count++;
        return false;
    })
    .ToList();
于 2012-07-14T16:32:57.907 回答