LINQ,Lambda Expression跳过n条记录,同时对跳过的记录求平均值

时间:2012-07-14 16:08:05

标签: c# linq lambda

我有一个表达式:

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

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

编辑:我希望能够完全跳过每个第n条记录,并平均2个特定字段(lat和long(存储为十进制)),并且很可能不会触及其他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

3 个答案:

答案 0 :(得分:2)

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

给定的Linq表达式只能返回一个东西。

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

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

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

我不确定你的意思

  

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

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

答案 1 :(得分: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();

答案 2 :(得分:2)

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

考虑一下你是否可以这样做:

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(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;
}

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