2010-11-09 19 views
13

Ich habe eine Sammlung von Person Objekte (IEnumerable) und jede Person hat eine Alter Eigentum.math stats mit Linq

Ich möchte Statistiken über die Sammlung wie Max, Min, Durchschnitt, Median, etc. auf dieser Alterseigenschaft generieren.

Was ist die eleganteste Art, dies mit LINQ zu tun?

+1

vorsichtig sein, wenn die Daten aus einer Datenbank comming, wie Linq die Daten mehr als einmal in Ihrem Fall jedoch lesen kann Linq sollte ein gutes Werkzeug sein, wie Sie scheinen Ihre Sammlung in ram zu haben. –

Antwort

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

Fix für Median bei gerade Anzahl von Elementen

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

Mein +1 für Ihre Antwort – Aliostad

+0

Kleiner Hinweis hier, dass dieser Algorithmus nicht zeitoptimal ist. Median kann in O (n) Zeit berechnet werden. – Max

8

Max, Min, Durchschnitt sind Teil Linq:

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

Median ist einfach:

UPDATE

Ich habe hinzugefügt, um:

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

PASSEN

Alle diese Operationen werden in einer Schleife ausgeführt. Zum Beispiel ist ints.Count() eine Schleife, also wenn Sie ints.Length bereits erhalten und in einer Variablen gespeichert haben oder sie einfach so verwenden, wie sie ist, wäre es besser.

+1

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

+0

Damit das Median richtig ist, müssen Sie das Array zuerst bestellen. –

+0

Ja, Sie haben Recht, aber es wurde bereits bestellt. – Aliostad

24

Hier ist eine komplette, generische Implementierung von Median, die richtig leeren Sammlungen und Nullable Types behandelt. Es ist LINQ freundlich im Stil Enumerable.Average, zum Beispiel:

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

Diese Implementierung gibt null zurück, wenn es keine Nicht-Null-Werte in der Sammlung sind, aber wenn Sie nicht wie der Nullable-Rückgabetyp Sie könnten es einfach ändern, um stattdessen eine Ausnahme auszulösen.

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

Dies, AFAIK, zählt die Quelle zwei- oder dreimal auf: zuerst, wenn 'Count()' aufgerufen wird, zweites (und möglicherweise drittes Mal) - wenn ElementAt aufgerufen wird. – DarkWanderer

+3

Sie haben absolut recht.Und - wie immer bei LINQ - wird die Wirkung je nach Art Ihrer Sammlung von trivial bis prohibitiv reichen. Merken Sie sich die Regeln der Optimierung: Regel 1: Tun Sie es nicht. Regel 2 (nur für Experten): Tun Sie es noch nicht. (Siehe http://blog.codinghorror.com/why-arent-my-optimizations-optimizing/) –