Моя цель состоит в том, чтобы получить взвешенное среднее от одной таблицы, на основе другого первичного ключа таблиц.
Данные в качестве примера:
Table1
Key WEIGHTED_AVERAGE
0200 0
Table2
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.Спасибо.
Я сделал это достаточно, чтобы создать расширение метод для 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. Позволяет пользователю перехватывать исключение и обрабатывать его по мере необходимости.
(Отвечая на комментарий 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);
Если вы уверены, что для каждого внешнего ключа в таблице 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);
ОБНОВЛЕНИЕ
Таким образом вы можете получить 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, вызываемый при выборке записей, предназначен для предотвращения выполнения запроса дважды при агрегировании записей в двух отдельных операциях Sum.