Modelliert LINQ die aggregierte SQL-Funktion STDDEV()
(Standardabweichung)?
Wenn nicht, wie lässt sich dies am einfachsten / am besten berechnen?
Beispiel:
SELECT test_id, AVERAGE(result) avg, STDDEV(result) std
FROM tests
GROUP BY test_id
linq
standard-deviation
Steven
quelle
quelle
Antworten:
Sie können Ihre eigene Erweiterung erstellen, um sie zu berechnen
public static class Extensions { public static double StdDev(this IEnumerable<double> values) { double ret = 0; int count = values.Count(); if (count > 1) { //Compute the Average double avg = values.Average(); //Perform the Sum of (value-avg)^2 double sum = values.Sum(d => (d - avg) * (d - avg)); //Put it all together ret = Math.Sqrt(sum / count); } return ret; } }
Wenn Sie eine Stichprobe der Bevölkerung und nicht der gesamten Bevölkerung haben, sollten Sie diese verwenden
ret = Math.Sqrt(sum / (count - 1));
.Von Chris Bennett in Erweiterung von Hinzufügen von Standardabweichung zu LINQ umgewandelt .
quelle
stdev = g.Select(o => o.number).StdDev()
.- 1
? Nach dieser die- 1
erforderlich ist .Die Antwort von Dynami funktioniert, durchläuft jedoch mehrere Daten, um ein Ergebnis zu erhalten. Dies ist eine Single-Pass-Methode, mit der die Standardabweichung der Stichprobe berechnet wird :
public static double StdDev(this IEnumerable<double> values) { // ref: http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/ double mean = 0.0; double sum = 0.0; double stdDev = 0.0; int n = 0; foreach (double val in values) { n++; double delta = val - mean; mean += delta / n; sum += delta * (val - mean); } if (1 < n) stdDev = Math.Sqrt(sum / (n - 1)); return stdDev; }
Dies ist die Standardabweichung der Stichprobe, da sie durch dividiert wird
n - 1
. Für die normale Standardabweichung müssen Sien
stattdessen durch dividieren .Dies verwendet die Welford-Methode, die im Vergleich zur
Average(x^2)-Average(x)^2
Methode eine höhere numerische Genauigkeit aufweist .quelle
this IEnumerable<double?> values
und geändertval in values.Where(val => val != null)
. Ich werde auch bemerken, dass diese Methode (Welfords Methode) genauer und schneller ist als die obige Methode.Dies wandelt die Antwort von David Clarke in eine Erweiterung um, die der gleichen Form wie die anderen aggregierten LINQ-Funktionen wie Average folgt.
Verwendung wäre:
var stdev = data.StdDev(o => o.number)
public static class Extensions { public static double StdDev<T>(this IEnumerable<T> list, Func<T, double> values) { // ref: /programming/2253874/linq-equivalent-for-standard-deviation // ref: http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/ var mean = 0.0; var sum = 0.0; var stdDev = 0.0; var n = 0; foreach (var value in list.Select(values)) { n++; var delta = value - mean; mean += delta / n; sum += delta * (value - mean); } if (1 < n) stdDev = Math.Sqrt(sum / (n - 1)); return stdDev; } }
quelle
Average
/Min
/Max
/ etc Überladungen mit und ohne Auswahlfunktionen haben. Sie haben auch Überlastungen für integrale Typen, Schwimmer usw.var stddev = Math.Sqrt(data.Average(z=>z*z)-Math.Pow(data.Average(),2));
quelle
Die Antwort von Dynamis lautet direkt auf den Punkt (und C #> 6.0):
public static double StdDev(this IEnumerable<double> values) { var count = values?.Count() ?? 0; if (count <= 1) return 0; var avg = values.Average(); var sum = values.Sum(d => Math.Pow(d - avg, 2)); return Math.Sqrt(sum / count); }
Bearbeiten 2020-08-27:
Ich habe @ David Clarke Kommentare genommen, um einige Leistungstests durchzuführen, und dies sind die Ergebnisse:
public static (double stdDev, double avg) StdDevFast(this List<double> values) { var count = values?.Count ?? 0; if (count <= 1) return (0, 0); var avg = GetAverage(values); var sum = GetSumOfSquareDiff(values, avg); return (Math.Sqrt(sum / count), avg); } private static double GetAverage(List<double> values) { double sum = 0.0; for (int i = 0; i < values.Count; i++) sum += values[i]; return sum / values.Count; } private static double GetSumOfSquareDiff(List<double> values, double avg) { double sum = 0.0; for (int i = 0; i < values.Count; i++) { var diff = values[i] - avg; sum += diff * diff; } return sum; }
Ich habe dies mit einer Liste von einer Million zufälligen Doppeln getestet.
Die ursprüngliche Implementierung hatte eine Laufzeit von ~ 48 ms.
Die leistungsoptimierte Implementierung hat 2-3 ms.
Dies ist also eine signifikante Verbesserung.
Einige interessante Details:
Math.Pow loszuwerden bringt einen Schub von 33ms!
Liste statt IEnumerable 6ms
manuell Durchschnittliche Berechnung 4ms
For-Schleifen anstelle von ForEach-Schleifen 2ms
Array statt Liste bringt nur eine Verbesserung von ~ 2%, also habe ich dies übersprungen,
indem ich einfach statt doppelt verwendet habe, bringt nichts
Eine weitere Senkung des Codes und die Verwendung von goto (ja, GOTO ... habe dies seit dem Assembler der 90er Jahre nicht mehr verwendet ...) anstelle von for-Schleifen zahlt sich nicht aus, Gott sei Dank!
Ich habe auch parallele Berechnungen getestet, dies ist bei Listen> 200.000 Elementen sinnvoll. Es scheint, dass Hardware und Software viel initialisiert werden müssen und dies ist für kleine Listen kontraproduktiv.
Alle Tests wurden zweimal hintereinander ausgeführt, um die Aufwärmzeit zu verringern.
quelle
Count()
,Average()
undSum()
. Dies ist für kleine Werte voncount
in Ordnung, kann jedoch die Leistung beeinträchtigen, wenn siecount
groß sind.(this IList<double> values)
. Leistungstests würden die Auswirkungen zeigen und wie viele Elemente einen signifikanten Unterschied machenCount
,Average
,Sum
) jeweils Iterierte die Sammlung , so dass Sie immer noch drei volle Iterationen haben ein Ergebnis zu erzeugen.public static double StdDev(this IEnumerable<int> values, bool as_sample = false) { var count = values.Count(); if (count > 0) // check for divide by zero // Get the mean. double mean = values.Sum() / count; // Get the sum of the squares of the differences // between the values and the mean. var squares_query = from int value in values select (value - mean) * (value - mean); double sum_of_squares = squares_query.Sum(); return Math.Sqrt(sum_of_squares / (count - (as_sample ? 1 : 0))) }
quelle
count
.Einfache 4 Zeilen, ich habe eine Liste von Doppel verwendet, aber man könnte verwenden
IEnumerable<int> values
public static double GetStandardDeviation(List<double> values) { double avg = values.Average(); double sum = values.Sum(v => (v - avg) * (v - avg)); double denominator = values.Count - 1; return denominator > 0.0 ? Math.Sqrt(sum / denominator) : -1; }
quelle