c# linq weighted-average

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.