【问题标题】:How to calculate STD dev and Average with linq and entity framework at the same time如何同时使用linq和实体框架计算STD dev和Average
【发布时间】:2020-04-01 22:12:13
【问题描述】:

你好我想问你有没有办法用LINQ同时计算平均值和标准偏差。

var players = new List<Player>
{
    new Player { Name = "Alex", Team = "A", Score = 10 },
    new Player { Name = "Anna", Team = "A", Score = 20 },
    new Player { Name = "Luke", Team = "L", Score = 60 },
    new Player { Name = "Lucy", Team = "L", Score = 40 },
};

var teamAverageScores =
    from player in players
    group player by player.Team into playerGroup
    select new
    {
        Team = playerGroup.Key,
        AverageScore = playerGroup.Average(x => x.Score),
    };

我想在这个例子中计算分数的 STDev 并返回两个不同的列表,一个是平均值,一个是标准差。

AverageScore 的预期结果:

{ Team = "A", AverageScore = 15.0 } 
{ Team = "L", AverageScore = 50.0 } 

对于标准开发:

{ Team = "A", STDScore = 5.0 } 
{ Team = "L", STDScore = 10.0 }

【问题讨论】:

  • 你能展示你想要的结果吗?
  • 我想要一个平均列表 { Team = "A", AverageScore = 15.0 } // { Team = "L", AverageScore = 50.0 } 和标准开发列表 { Team = " A", STDScore = 2.0 } // { Team = "L", STDScore = 3.0 } 这只是一个例子,不是精确值
  • 您确定 STDDev 的值吗?我已经为你找到了一个解决方案,但在我的计算中,根据你的输入,我得到了 5 和 10 作为 STDDev。
  • 是的,这就是我提到的,它仅用于示例,所以它不是正确的输出,但如果你有解决方案,我很高兴你可以分享它

标签: c# entity-framework linq


【解决方案1】:

基于this,你需要使用这样的扩展方法:

public static class ExtensionsClass
{
    public static double CalculateStdDev(this IEnumerable<double> values)
    {
        double ret = 0;
        int count = values.Count();
        if (count > 1)
        {
            //Compute the Average
            double avg = values.Average();

            //Perform the Sum of (value-avg)^2
            double sum = values.Sum(d => (d - avg) * (d - avg));

            //Put it all together
            ret = Math.Sqrt(sum / count);
        }
        return ret;
    }
}

那么你可以这样做:

var teamAverageScores =
     from player in players
     group player by player.Team into playerGroup
     select new
     {
         Team = playerGroup.Key,
         AverageScore = playerGroup.Average(c => c.Score),
         STDev = playerGroup.Select(c => c.Score).CalculateStdDev(),
     };

【讨论】:

  • IEnumerable&lt;T&gt; 的扩展方法不应多次枚举源,因为有可能(并且非常频繁)基础数据不存储在内存中,而是从数据库(或从文件系统或来自 Web API)。一个简单的解决方法是将IEnumerable&lt;int&gt; 替换为ICollection&lt;int&gt;,因为这种类型的容器肯定存储在内存中。
  • System.NotSupportedException: 'LINQ to Entities 无法识别方法'Double StdDev(System.Collections.Generic.IEnumerable`1[System.Double])' 方法,并且该方法无法翻译成商店表达。这就是我在运行时得到的结果
  • @ZaatourWadi 在Select 之后和Calculate 之前使用ToList()
  • 您能否更新问题以包含该问题?该信息非常重要,因为 Salah 已经回答了这个问题,认为它是一个简单的 LINQ 查询(如果我错了@SalahAkbari,请纠正我)并且这样的查询在实体框架中不起作用。
  • @ColinM 是的,这就是为什么我建议使用ToList() 以便在内存中进行进一步的操作。但是我不能保证这种方法的性能。
【解决方案2】:

一个简单的解决方案是使用 NuGet 包 LinqStatistics 计算标准差分数,并使用 LINQ 中的 Enumerable.Average() 计算平均分数。如果您不喜欢使用外部库,那么@Salah Akbari's 解决方案将更合适。我认为提出这个库是个好主意,因为我过去使用它没有任何问题。

using LinqStatistics;

// Other code

// Class that stores results from query
// Might be cleaner than using an anonymous class
public class Result
{
    public string Team { get; set; }
    public double Average { get; set; }
    public double StdDev { get; set; }
}

var teamAverageScores = players
    .GroupBy(player => player.Team)
    .Select(grp => new Result
    {
         Team = grp.Key,
         Average = grp.Average(player => player.Score),
         StdDev = grp.StandardDeviation(player => player.Score)
    });

试试dotnetfiddle.net

注意:上面完全使用了LINQ 方法语法,而不是查询语法。您可以在MSDN 了解这些不同语法类型之间的差异。使用任何一种都取决于偏好。

【讨论】:

  • 感谢您的回复,但我避免使用外部库
  • OP 正在使用实体框架。它们的来源实际上不是List&lt;Player&gt;,所以很可能StandardDeviation 运算符不会被EF 机器识别。
  • @TheodorZoulias 是的。我没有设置 EF Core 环境,所以我很难测试,但你可能是对的。
【解决方案3】:

经过多次尝试,我找到了解决方案

var teamAverageScores =
        from player in players
        group player by player.Team into playerGroup
        select new
        {
            Team = playerGroup.Key,
            AverageScore = playerGroup.Average(c => c.Score),
            STDev =  (playerGroup.Count() == 1) ? 0 : Math.Sqrt(playerGroup.Sum(b => Math.Pow(playerGroup.Score - playerGroup.Average(c => c.Score), 2)) / (g.Count() - 1)),,
        };

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-29
    • 1970-01-01
    相关资源
    最近更新 更多