2010-04-26 167 views
12

我的目標是根據另一個表主鍵從一個表中獲得加權平均值。用LINQ計算加權平均值

實施例的數據:

表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。謝謝。

回答

33

我這樣做不夠,我創建了一個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

謝謝,非常有幫助。我結束了這使其成爲一個班輪...公共靜態浮動WeightedAverage (這IEnumerable的項目,Func鍵值,Func鍵重量) { 回報items.Sum(項目=>值(項目)*重量(項目) )/ items.Sum(重量); } – josefresno 2010-10-19 22:00:07

+2

在計算之後,我必須添加「如果weightedSum.AlmostZero()返回0」,以防止在所有權重和/或所有值都爲零時被零除。 AlmostZero是一個擴展函數,用於檢查double是否爲零。 – derdo 2011-07-13 22:10:46

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); 

UPDATE

這是你可以得到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方法,是爲了避免在執行查詢的兩倍,而在兩個獨立的和運算聚集的記錄。

+0

這將爲每個不同的ForeignKey返回一個值。如果你只想要一個特定的唯一的ForeignKey,那麼你可以避免GroupBy,只需要用所需的外鍵過濾記錄,然後執行聚合操作。我會編輯我的答案,告訴你如何。 – Fede 2010-04-26 18:09:00

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); 
+0

大家知道,我的解決方案假定您想計算一組外鍵與第一個表中任何行的鍵值相匹配的行的加權平均值。 Fede的解決方案將爲您獲取特定外鍵的行。所以,隨時挑選哪種解決方案更合適。 – 2010-04-26 18:35:49