25

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

示例数据:

表格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。谢谢你。

4

3 回答 3

67

这是 LINQ 的扩展方法。

public static double WeightedAverage<T>(this IEnumerable<T> records, Func<T, double> value, Func<T, double> weight)
{
    if(records == null)
        throw new ArgumentNullException(nameof(records), $"{nameof(records)} is null.");

    int count = 0;
    double valueSum = 0;
    double weightSum = 0;

    foreach (var record in records)
    {
        count++;
        double recordWeight = weight(record);

        valueSum += value(record) * recordWeight;
        weightSum += recordWeight;
    }

    if (count == 0)
        throw new ArgumentException($"{nameof(records)} is empty.");

    if (count == 1)
        return value(records.Single());

    if (weightSum != 0)
        return valueSum / weightSum;
    else
        throw new DivideByZeroException($"Division of {valueSum} by zero.");
}

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

更新

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

于 2010-08-30T22:38:53.573 回答
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特定的foreign_key.

IEnumerable<Record> records =
    (from record in Table2
    where record.ForeignKey == foreign_key
    where record.PCR != -1
    select record).ToList();
int wheighted_average = records.Sum(record => record.PCR * record.Length) /
    records.Sum(record => record.Length);

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

于 2010-04-26T15:51:21.903 回答
2

(回答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);
于 2010-04-26T18:20:23.587 回答