2010-11-09 18 views
13

Tengo una colección de persona objetos (IEnumerable) y cada persona tiene edad propiedad.estadísticas de matemáticas con Linq

quiero para generar estadísticas sobre la recogida, como Max, Min, Media, Mediana, etc en este establecimiento de la edad.

¿Cuál es la forma más elegante de hacer esto utilizando LINQ?

+1

tener cuidado si los datos están viniendo de una base de datos, como LINQ puede leer los datos más de una vez, sin embargo, en su caso LINQ debería ser una buena herramienta a medida que parecen tener su colección en la memoria RAM. –

Respuesta

23
max - persons.Max(p => p.age); 
min - persons.Min(p => p.age); 
average - persons.Average(p => p.age); 

Arreglo para el medio en el caso de un número par de elementos

int count = persons.Count(); 
var orderedPersons = persons.OrderBy(p => p.age); 
float median = orderedPersons.ElementAt(count/2).age + orderedPersons.ElementAt((count-1)/2).age; 
median /= 2; 
+1

1 por su respuesta – Aliostad

+0

pequeña nota aquí, que este algoritmo no es el momento óptimo. La mediana se puede calcular en el tiempo O (n). – Max

8

Max, Min, Promedio forman parte de LINQ:

int[] ints = new int[]{3,4,5}; 
Console.WriteLine(ints.Max()); 
Console.WriteLine(ints.Min()); 
Console.WriteLine(ints.Average()); 

La mediana es fácil:

ACTUALIZACIÓN fin

He añadido:

ints.OrderBy(x=>x).Skip(ints.Count()/2).First(); 

GUARDA

Todas estas operaciones se realizan en un bucle. Por ejemplo, ints.Count() es un bucle, por lo que si ya tiene ints.Length y lo almacena en una variable o simplemente lo usa como es, sería mejor.

+1

'ints.ElementAt (ints.Count()/2)' –

+0

para la mediana para ser justo lo que necesita para ordenar la matriz primero. –

+0

Sí, tiene razón pero ya estaba ordenado. – Aliostad

24

Aquí es una aplicación completa y genérica de la mediana que maneja adecuadamente colecciones vacías y tipos anulables. Es LINQ-amigable en el estilo de Enumerable.Average, por ejemplo:

double? medianAge = people.Median(p => p.Age); 

Esta implementación devuelve un valor nulo cuando no hay valores no nulos en la colección, pero si no te gusta el tipo de retorno anulable , puedes cambiarlo fácilmente para lanzar una excepción.

public static double? Median<TColl, TValue>(
    this IEnumerable<TColl> source, 
    Func<TColl, TValue>  selector) 
{ 
    return source.Select<TColl, TValue>(selector).Median(); 
} 

public static double? Median<T>(
    this IEnumerable<T> source) 
{ 
    if(Nullable.GetUnderlyingType(typeof(T)) != null) 
     source = source.Where(x => x != null); 

    int count = source.Count(); 
    if(count == 0) 
     return null; 

    source = source.OrderBy(n => n); 

    int midpoint = count/2; 
    if(count % 2 == 0) 
     return (Convert.ToDouble(source.ElementAt(midpoint - 1)) + Convert.ToDouble(source.ElementAt(midpoint)))/2.0; 
    else 
     return Convert.ToDouble(source.ElementAt(midpoint)); 
} 
+0

Esto, AFAIK, enumera la fuente 2 o 3 veces: primero cuando se llama 'Count()', segundo (y posiblemente tercera vez) - cuando se llama a ElementAt. – DarkWanderer

+3

Tienes toda la razón.Y, como siempre con LINQ, el impacto de esto va desde trivial a prohibitivo, dependiendo de la naturaleza de su colección. Recuerde las Reglas de Optimización: Regla 1: No lo haga. Regla 2 (solo para expertos): No lo hagas aún. (Ver http://blog.codinghorror.com/why-arent-my-optimizations-optimizing/) –