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メソッドは、2つの別々のSum操作でレコードを集約しているときにクエリを2回実行しないようにするためのものです。

于 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 に答える