代码之家  ›  专栏  ›  技术社区  ›  jsmith

用LINQ计算加权平均

  •  18
  • jsmith  · 技术社区  · 16 年前

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

    示例数据:

    Key     WEIGHTED_AVERAGE
    
    0200    0
    

    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 回复  |  直到 9 年前
        1
  •  66
  •   jsmith    5 年前

    这里有一个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,并抛出更详细的异常,而不是返回0。允许用户捕获异常并根据需要进行处理。

        2
  •  4
  •   Fede    16 年前

    如果您确定表2中的每个外键在表1中都有一个对应的记录,那么您可以避免仅仅通过创建一个group by来进行连接。

    在这种情况下,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);
    

        3
  •  2
  •   Jimmy W    16 年前

    (回答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);