【问题标题】:LINQ - returning list of averages based on index in listLINQ - 根据列表中的索引返回平均值列表
【发布时间】:2011-08-11 20:20:54
【问题描述】:

所以我有一个有点奇怪的场景,我无法转过头来:

我有一个包含多个内部列表的外部列表。
每个内部列表包含多个 DataPoint 对象。
每个内部列表都包含相同数量的 DataPoint 对象。
数据点有一个名为 Value 的字段,它是一个双精度字段 - 例如:

  • 外部名单
    • 内部清单
      • 数据点(值=2)
      • 数据点(值=2)
    • 内部清单
      • 数据点(值=4)
      • 数据点(值=5)
    • 内部清单
      • 数据点(值=3)
      • 数据点(值=5)

所以我想做的是将内部列表“组合”成一个列表,其中每个 DataPoint 的值应该是旧值的平均值,基于它在列表中的索引 - 在这种情况下:

  • 列表
    • 数据点(3) - ((2+4+3)/3) = 3
    • 数据点(4) - ((2+5+5)/3) = 4

是否有任何简洁的 linq 表达式可以执行此类操作(我打赌有:))?


我最终使用了以下解决方案:

var averages = Enumerable.Range(0, outer.First().Count()).Select(i => new DataPoint(outer.Select(l => l[i]).Average(x=>x .值)));

它输出 IEnumerable,现在具有平均值 - 耶!

【问题讨论】:

  • 如果你有最新版本的 resharper,值得输入 for 循环,看看它是否建议基于 linq 的重构。好吧,我是在开玩笑,但它可能真的有效。
  • 哈哈,Resharper 很好,但我怀疑它有那么好。 :) 不过,我认为无论如何你都必须执行 for 循环。 LINQ 通常最适合与位置无关的操作。
  • 你可能想看看这个问题:stackoverflow.com/questions/6669121/…
  • 如果我正确理解了您的问题,您将不会在 linq 中沿枢轴线做任何事情。可能对这个问题感兴趣:Pivot data using LINQ.

标签: .net linq list average


【解决方案1】:

从一个扩展方法开始获取内部列表的一部分:

public static IEnumerable<double> Slice(
    this IEnumerable<List<DataPoint>> innerLists,
    int index) {
        foreach(var innerList in innerLists) {
            yield return innerList.DataPoints[index].Value;
        }
    }
}

然后:

var averages = Enumerable.Range(0, count)
                         .Select(index => outerList.InnerLists.Slice(index))
                         .Select(slice => slice.Average());

我假设这样的层次结构:

class DataPoint { public double Value { get; set; } }

class InnerList { public List<DataPoint> DataPoints { get; set; } }

class OuterList { public IEnumerable<InnerList> InnerLists { get; set; } }

但上述想法显然适用于您构建数据的方式。

【讨论】:

  • 哇,这很优雅。我从来没有考虑过这个。
  • 很酷的东西 - 显然比我最终使用的更具可读性。
【解决方案2】:

这里的关键是按索引对内部列表值进行分组。像这样的东西会起作用:

// Test data
var list = new List<List<double>>() { 
    new List<double> { 2, 2 },
    new List<double> { 4, 5 },
    new List<double> { 3, 5 }
};

// Created groups based on index
var lookup = from inner in list 
             from value in inner.Select((n,idx) => new { n,idx })
             group value by value.idx;

// Calculate averages    
var averagesBasedOnIndex = lookup.Select(g => g.Average(x => x.n)).ToList();

【讨论】:

  • 啊,很酷,所以有一个带有索引解析的 Select 版本 - 新人注意到 :)
【解决方案3】:

我在我的 iPad 上,所以我还没有编译,但以下应该可以工作:

Enumerable.Range(0,outer.First().Length).
   Select(i => outer.Select(l => l[i]).Average());

【讨论】:

  • 几乎...问题是您要区分要制表的内部列表,而不是哪个数据点。此外,average 将需要一个 lambda 函数,并且您将需要一个 selectmany:Enumerable.Range(0, items.First().InnerLists.First().DataPoints.Count())。 Select(i => items.SelectMany(x=>x.InnerLists).Average(x=>x.DataPoints[i].Value));
  • 实际上,除了平均缺少的 lambda 之外,Jan 几乎是准确的。我最终使用了以下内容: var averages = Enumerable.Range(0, outer.First().Count()).Select(i => new DataPoint(outer.Select(l => l[i]).Average( x=>x.Value)));
【解决方案4】:

这就是你所需要的:

var outer = new List<List<List<double>>>();
var l1 = outer.Select(
  x => new List<double> { 
         x.Average(y => y[0]), 
         x.Average(y => y[1]), 
         x.Average(y => y[2])
       });

使用像你这样的数据结构,它会是:

var list = items.Select(x => new List<DataPoint> (
            Enumerable.Range(0,3).Select(z => 
                new DataPoint {
                   Value = x.InnerLists.Average(y => y.DataPoints[z].Value)})
           ));

(感谢推荐Enumerable.Range的人)

【讨论】:

  • 问题是我不知道内部列表中数据点的数量,因为这些可能在不同的外部列表之间有所不同。我最终使用 Enumerable.Range 来处理数据点的动态数量。
猜你喜欢
  • 1970-01-01
  • 2016-01-07
  • 2021-12-17
  • 2018-02-10
  • 1970-01-01
  • 2016-08-23
  • 2019-12-14
  • 2022-11-23
  • 2021-09-29
相关资源
最近更新 更多