c# - Cálculo de la media ponderada con LINQ
weighted-average (3)
(Respondiendo el comentario de jsmith a la respuesta de arriba)
Si no deseas pasar por alguna colección, puedes probar lo siguiente:
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);
Mi objetivo es obtener un promedio ponderado de una tabla, basado en otra clave primaria de tablas.
Datos de ejemplo:
Tabla 1
Key WEIGHTED_AVERAGE
0200 0
Tabla 2
ForeignKey Length Value
0200 105 52
0200 105 60
0200 105 54
0200 105 -1
0200 47 55
Necesito obtener un promedio ponderado basado en la longitud de un segmento y debo ignorar los valores de -1. Sé cómo hacerlo en SQL, pero mi objetivo es hacerlo en LINQ. Se ve algo como esto en 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;
Todavía soy bastante nuevo en LINQ, y me resulta difícil descubrir cómo traducirlo. El promedio ponderado de resultados debería llegar a aproximadamente 55.3. Gracias.
Hago esto lo suficiente como para crear un método de extensión para 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");
}
Después de obtener su subconjunto de datos, la llamada se ve así.
double weightedAverage = records.WeightedAverage(x => x.Value, x => x.Length);
Esto se ha vuelto extremadamente útil porque puedo obtener un promedio ponderado de cualquier grupo de datos basado en otro campo dentro del mismo registro.
Actualizar
Ahora verifico la división por cero y lanzo una excepción más detallada en lugar de devolver 0. Permite al usuario detectar la excepción y manejarla según sea necesario.
Si está seguro de que para cada clave externa en la Tabla 2 hay un registro correspondiente en la Tabla 1, puede evitar la unión simplemente haciendo un grupo.
En ese caso, la consulta LINQ es así:
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);
ACTUALIZAR
Así es como puede obtener el wheighted_average
para un foreign_key
específico.
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);
El método ToList llamado al obtener los registros, es evitar la ejecución de la consulta dos veces mientras se agregan los registros en las dos operaciones Sumas separadas.