Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/293.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用LINQ计算加权平均_C#_Linq_Weighted Average - Fatal编程技术网

C# 用LINQ计算加权平均

C# 用LINQ计算加权平均,c#,linq,weighted-average,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

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;
选择Sum(t2.值*t2.长度)/Sum(t2.长度)作为加权平均值
来自表1 t1、表2 t2
其中t2.值为-1
t2.ForeignKey=t1.Key;

我对LINQ还是一个新手,很难弄清楚如何翻译这个。结果加权平均值应该达到大约55.3。谢谢。

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

在这种情况下,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);
获取记录时调用的ToList方法是为了避免在两个单独的求和操作中聚合记录时执行两次查询。

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

这里是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.");
}
public静态双权重平均值(此IEnumerable记录、Func值、Func权重)
{
if(记录==null)
抛出新ArgumentNullException(nameof(records),$“{nameof(records)}为null。”);
整数计数=0;
双值和=0;
双加权和=0;
foreach(记录中的var记录)
{
计数++;
双记录重量=重量(记录);
valueSum+=值(记录)*记录权重;
权重总和+=记录权重;
}
如果(计数=0)
抛出新ArgumentException($“{nameof(records)}为空。”);
如果(计数=1)
返回值(records.Single());
如果(加权和!=0)
返回值总和/权重总和;
其他的
抛出新的DivideByZeroException($“将{valueSum}除以零”);
}
这变得非常方便,因为我可以基于同一记录中的另一个字段获得任意数据组的加权平均值

更新


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

这将为每个不同的ForeignKey返回一个值。如果只需要特定外键的加权平均值,并且只需要外键,则可以避免使用GroupBy,只需使用所需外键筛选记录,然后执行聚合操作。我将编辑我的答案以向您展示如何操作。正如您所知,我的解决方案假设您希望计算外键与第一个表中任何行的键值匹配的一组行的加权平均值。Fede的解决方案将为您获取特定外键的行。所以,请随意选择更合适的解决方案。谢谢,非常有用。我最后把它做成了一行。。。公共静态浮点加权平均值(此IEnumerable items,Func value,Func weight){返回items.Sum(item=>value(item)*weight(item))/items.Sum(weight);}我必须添加“If weightedSum.AlmostZero()返回0”计算后,当所有权重和/或所有值均为零时,防止被零除。AlmostZero是一个扩展函数,用于检查double是否为零。请注意,这将枚举记录两次。在许多应用程序中,这是可以的,但如果它们是从数据库流式传输的,或者重新枚举是不可能的或代价高昂的,那么它就会崩溃。就我个人而言,我将“records”键入为IReadOnlyCollection而不是IEnumerable,以向调用者表明他们有责任处理这个问题(例如,如果需要,首先调用.ToList())。
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.");
}