2010-04-26 11 views
12

Moim celem jest uzyskanie średniej ważonej z jednej tabeli, w oparciu o klucz podstawowy innej tabeli.Obliczanie średniej ważonej przy użyciu LINQ

Przykład Dane:

Tabela 1

Key  WEIGHTED_AVERAGE 

0200 0 

Tabela 2

ForeignKey Length Value 
0200   105  52 
0200   105  60 
0200   105  54 
0200   105  -1 
0200   47  55 

trzeba uzyskać średnią ważoną w oparciu o długość odcinka i muszę ignorować wartości -1 . Wiem, jak to zrobić w SQL, ale moim celem jest zrobić to w LINQ. Wygląda to mniej więcej tak w 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; 

jestem jeszcze całkiem nowy, LINQ, a twardy czas na zastanawianie się, jak bym tego tłumaczyć. Średnia ważona wyników powinna wynosić około 55,3. Dziękuję Ci.

Odpowiedz

33

zrobić to na tyle, że stworzył metoda rozszerzenia dla LINQ.

Po uzyskaniu podzbioru danych połączenie wygląda następująco.

double weightedAverage = records.WeightedAverage(x => x.Value, x => x.Length); 

Jest to bardzo przydatne, ponieważ mogę uzyskać średnią ważoną każdej grupy danych na podstawie innego pola w obrębie tego samego rekordu.

Aktualizacja

teraz sprawdzić, dzielenie przez zero i rzucać w dokładniejszy wyjątek zamiast wrócić 0. Pozwala użytkownikowi do połowu wyjątek i obsługiwać zgodnie z potrzebami.

+1

Dzięki, bardzo pomocne. I skończyłem przeprowadzenie tej jeden liner ... public static pływak WeightedAverage (ta IEnumerable przedmioty, Func wartość, Func waga) { powrót items.Sum (pozycja => wartość (pozycja) * waga (pozycja))/items.Sum (waga); } – josefresno

+2

Musiałem dodać "If weightedSum.AlmostZero() zwraca 0" po obliczeniach, aby zabezpieczyć się przed dzieleniem przez zero, gdy wszystkie wagi i lub wszystkie wartości są zerowe. AlmostZero jest funkcją rozszerzenia, która sprawdza, czy podwójne jest równe zero. – derdo

4

Jeśli masz pewność, że dla każdego klucza obcego w Tabeli 2 znajduje się odpowiedni rekord w Tabeli 1, możesz uniknąć łączenia tylko tworząc grupę przez.

W takim przypadku zapytań LINQ to tak:

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

W ten sposób można uzyskać wheighted_average zadaniu 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); 

Sposób ToList wywoływana podczas pobierania rekordów, to w celu uniknięcia wykonania kwerendy dwukrotnie podczas agregowania rekordy w dwóch oddzielnych operacji sumy.

+0

Powoduje to zwrócenie jednej wartości dla każdego innego klucza obcego. Jeśli chcesz używać tylko średniej kroczącej dla określonego i jedynego klucza obcego, możesz uniknąć GroupBy i po prostu przefiltrować rekordy z żądanym kluczem obcym, a następnie wykonać operacje agregacji. Będę edytować moją odpowiedź, aby pokazać, jak. – Fede

1

(Odbieranie komentarz JSmith do odpowiedzi powyżej)

Jeśli nie chcą przechodzić jakiejś kolekcji, można spróbować wykonać następujące czynności:

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

Tak, wiesz, moje rozwiązanie zakłada, że ​​chcesz obliczyć średnią ważoną w zestawie wierszy, których klucz obcy pasuje do wartości klucza dowolnego wiersza w pierwszej tabeli. Rozwiązanie Fede dostarczy ci wiersze dla określonego klucza obcego. Dlatego wybierz dowolne rozwiązanie, które jest bardziej odpowiednie. –