使用LINQ返回不同值的平均值

时间:2017-03-31 07:15:21

标签: c# linq

我对LINQ没有多少经验,而且我很难使用LINQ根据不同列中的不同值提取ListView列的平均值,模式和中位数。

例如,请考虑以下数据:

ReportID  TAT  Col3  Col4  etc...
--------  ---  ----  ----  -------
80424     9    1     7     ...
80424     9    2     3     ...
80424     9    2     2     ...
80423     5    1     5     ...
80425     4    1     5     ...
80423     7    1     4     ...
82541     7    1     5     ...

要提取TAT(周转时间)超过5天的不同报告的计数,我使用以下内容:

int countDistinctReports = (from ListViewItem itm in lvList.Items
                            where itm.SubItems[1].Text) > 5
                            select itm.SubItems[0].Text
                           ).Distinct().Count();

countDistinctReports的值为3。

为了提取这些报告的最小TAT,我使用:

string minVal = (from ListViewItem itm in lvList.Items
             where itm.SubItems[1].Text) > 5
             select itm.SubItems[1].Text
            ).Min();

minVal的值为7。

但是,我不知道如何编写LINQ表达式来计算TAT大于5的不同报告的平均值,模式和中位数TAT。在上面的例子中,平均值应为(9 + 7 + 7) )/ 3 = 7.67。模式应为7.

有人可以帮忙吗?非常感谢。

3 个答案:

答案 0 :(得分:2)

这是问题的复制和解决,仅使用linq 1-liners:

using System;
using System.Linq;

namespace StackOverflow_StatisticsLinq
{
    class Program
    {
        static void Main(string[] args)
        {
            var reports = new int[][]
            {
                  new int[] { 80424, 9, 1, 7 }
                , new int[] { 80424, 9, 2, 3 }
                , new int[] { 80424, 9, 2, 2 }
                , new int[] { 80423, 5, 1, 5 }
                , new int[] { 80425, 4, 1, 5 }
                , new int[] { 80423, 7, 1, 4 }
                , new int[] { 80541, 7, 1, 5 }
            };

            var reportsTat5Plus = (from int[] r in reports
                                   where r[1] > 5
                                   orderby r[1] descending
                                  select r).GroupBy(r => r[0]).Select(r => r.First()) // ensure ids are distinct
                                  ;
            var average = reportsTat5Plus.Select(r => r[1])
                                         .Average();
            var mode = reportsTat5Plus.GroupBy(v => v)
                                      .OrderByDescending(g => g.Count())
                                      .First()
                                      .Select(r => r[1])
                                      .First()
                                      ;
            var median = reportsTat5Plus.Count() % 2 == 1 ? reportsTat5Plus.Skip(reportsTat5Plus.Count() / 2 + 1).Select(r => r[1]).First()
                                                          : reportsTat5Plus.Skip(reportsTat5Plus.Count() / 2 - 1).Take(2).Select(r => r[1]).Average()
                                                          ;

            Console.WriteLine($"Average: {average}");
            Console.WriteLine($"mode: {mode}");
            Console.WriteLine($"median: {median}");
            Console.ReadKey();
        }
    }
}

答案 1 :(得分:1)

对于平均值,还有一个名为Average的LINQ方法。但是,您必须将字符串转换为数字才能有效地使用数字算法。

var minVal = (from ListViewItem itm in lvList.Items
             where itm.SubItems[1].Text) > 5
             select Convert.ToInt32(itm.SubItems[1].Text)
            ).Average();

然而,中位数或模式没有这样的方法。你必须创建自己的。

E.g。中位数:

string subList = (from ListViewItem itm in lvList.Items
             where itm.SubItems[1].Text) > 5
             select Convert.ToIn32(itm.SubItems[1].Text)
            ).OrderBy(x => x).ToList();
var median = subList[subList.Count / 2];

这将首先创建一个列表,其中包含值大于5的所有商品,而不是中心商品。

答案 2 :(得分:0)

您应该使用GroupBy方法。

var groups = myList.GroupBy(e=>e.Key);//here key is your TAT

然后,如果你想计算群组的数量:

var count = groups.Count();

如果您想获得所有组中的最低要求:

var mins = groups.Select(g=>g.Min());

如果您想获取并保留关键信息:

var keyedMins = groups.Select(g=>new{K=g.Key,Min=g.Min()};

平均值和其他值相同:)