用LINQ计算加权平均值

时间:2010-04-26 15:23:39

标签: c# linq weighted-average

我的目标是根据另一个表主键从一个表中获得加权平均值。

示例数据:

表1

Key     WEIGHTED_AVERAGE

0200    0

表2

ForeignKey    Length    Value
0200          105       52
0200          105       60
0200          105       54
0200          105       -1
0200          47        55

我需要根据段的长度得到加权平均值,我需要忽略值-1。我知道如何在SQL中执行此操作,但我的目标是在LINQ中执行此操作。它在SQL中看起来像这样:

SELECT Sum(t2.Value*t2.Length)/Sum(t2.Length) AS WEIGHTED_AVERAGE
FROM Table1 t1, Table2 t2
WHERE t2.Value <> -1
AND t2.ForeignKey = t1.Key;

我仍然是LINQ的新手,并且很难弄清楚我将如何翻译它。结果加权平均值应该大约为55.3。谢谢。

3 个答案:

答案 0 :(得分:47)

我这么做,我为LINQ创建了一个扩展方法。

public static double WeightedAverage<T>(this IEnumerable<T> records, Func<T, double> value, Func<T, double> weight)
{
    double weightedValueSum = records.Sum(x => value(x) * weight(x));
    double weightSum = records.Sum(x => weight(x));

    if (weightSum != 0)
        return weightedValueSum / weightSum;
    else
        throw new DivideByZeroException("Your message here");
}

获得数据子集后,调用如下所示。

double weightedAverage = records.WeightedAverage(x => x.Value, x => x.Length);

这非常方便,因为我可以根据同一记录中的另一个字段获得任何数据组的加权平均值。

<强>更新

我现在检查除以零并抛出更详细的异常而不是返回0.允许用户捕获异常并根据需要进行处理。

答案 1 :(得分:4)

如果您确定Table2中的每个外键在Table1中都有相应的记录,那么您可以避免仅通过组合来加入。

在这种情况下,LINQ查询是这样的:

IEnumerable<int> wheighted_averages =
    from record in Table2
    where record.PCR != -1
    group record by record.ForeignKey into bucket
    select bucket.Sum(record => record.PCR * record.Length) / 
        bucket.Sum(record => record.Length);

<强>更新

这是您获取特定wheighted_average的{​​{1}}的方法。

foreign_key

获取记录时调用的ToList方法是避免在两个单独的Sum操作中聚合记录时执行两次查询。

答案 2 :(得分:1)

(回答jsmith对上述答案的评论)

如果您不希望循环访问某些集合,可以尝试以下操作:

var filteredList = Table2.Where(x => x.PCR != -1)
 .Join(Table1, x => x.ForeignKey, y => y.Key, (x, y) => new { x.PCR, x.Length });

int weightedAvg = filteredList.Sum(x => x.PCR * x.Length) 
    / filteredList.Sum(x => x.Length);