math stats with Linq
I have a 开发者_如何学JAVAcollection of person objects (IEnumerable) and each person has an age property.
I want to generate stats on the collection such as Max, Min, Average, Median, etc on this age property.
What is the most elegant way of doing this using LINQ?
Here is a complete, generic implementation of Median that properly handles empty collections and nullable types. It is LINQ-friendly in the style of Enumerable.Average, for example:
double? medianAge = people.Median(p => p.Age);
This implementation returns null when there are no non-null values in the collection, but if you don't like the nullable return type, you could easily change it to throw an exception instead.
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));
}
var max = persons.Max(p => p.age);
var min = persons.Min(p => p.age);
var average = persons.Average(p => p.age);
Fix for median in case of even number of elements
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;
Max, Min, Average are part of Linq:
int[] ints = new int[]{3,4,5};
Console.WriteLine(ints.Max());
Console.WriteLine(ints.Min());
Console.WriteLine(ints.Average());
Median is easy:
UPDATE
I have added order:
ints.OrderBy(x=>x).Skip(ints.Count()/2).First();
BEWARE
All these operations are done in a loop. For example, ints.Count() is a loop so if you already get ints.Length and stored to a variable or simply use it as it is, would be better.
Get median using Linq (works for even or odd number of elements)
int count = persons.Count();
if (count % 2 == 0)
var median = persons.Select(x => x.Age).OrderBy(x => x).Skip((count / 2) - 1).Take(2).Average();
else
var median = persons.Select(x => x.Age).OrderBy(x => x).ElementAt(count / 2);
精彩评论