c# - varianza - ¿Cómo determino la desviación estándar(stddev) de un conjunto de valores?
desviacion estandar en java (10)
Necesito saber si un número comparado con un conjunto de números está fuera de 1 stddev de la media, etc.
Con los métodos de extensión.
using System;
using System.Collections.Generic;
namespace SampleApp
{
internal class Program
{
private static void Main()
{
List<double> data = new List<double> {1, 2, 3, 4, 5, 6};
double mean = data.Mean();
double variance = data.Variance();
double sd = data.StandardDeviation();
Console.WriteLine("Mean: {0}, Variance: {1}, SD: {2}", mean, variance, sd);
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
public static class MyListExtensions
{
public static double Mean(this List<double> values)
{
return values.Count == 0 ? 0 : values.Mean(0, values.Count);
}
public static double Mean(this List<double> values, int start, int end)
{
double s = 0;
for (int i = start; i < end; i++)
{
s += values[i];
}
return s / (end - start);
}
public static double Variance(this List<double> values)
{
return values.Variance(values.Mean(), 0, values.Count);
}
public static double Variance(this List<double> values, double mean)
{
return values.Variance(mean, 0, values.Count);
}
public static double Variance(this List<double> values, double mean, int start, int end)
{
double variance = 0;
for (int i = start; i < end; i++)
{
variance += Math.Pow((values[i] - mean), 2);
}
int n = end - start;
if (start > 0) n -= 1;
return variance / (n);
}
public static double StandardDeviation(this List<double> values)
{
return values.Count == 0 ? 0 : values.StandardDeviation(0, values.Count);
}
public static double StandardDeviation(this List<double> values, int start, int end)
{
double mean = values.Mean(start, end);
double variance = values.Variance(mean, start, end);
return Math.Sqrt(variance);
}
}
}
Descubrí que la respuesta útil de Rob no coincidía exactamente con lo que veía usando Excel. Para hacer coincidir Excel, pasé el promedio para valueList en el cálculo de StandardDeviation.
Aquí están mis dos centavos ... y claramente se puede calcular el promedio móvil (ma) de valueList dentro de la función, pero ya he necesitado antes la desviación estándar.
public double StandardDeviation(List<double> valueList, double ma)
{
double xMinusMovAvg = 0.0;
double Sigma = 0.0;
int k = valueList.Count;
foreach (double value in valueList){
xMinusMovAvg = value - ma;
Sigma = Sigma + (xMinusMovAvg * xMinusMovAvg);
}
return Math.Sqrt(Sigma / (k - 1));
}
El problema con todas las demás respuestas es que asumen que tienes tus datos en una gran matriz. Si sus datos llegan sobre la marcha, este sería un mejor enfoque. Esta clase funciona independientemente de cómo y si almacena sus datos. También le da la opción del método Waldorf o el método de suma de cuadrados. Ambos métodos funcionan usando un solo pase.
public final class StatMeasure {
private StatMeasure() {}
public interface Stats1D {
/** Add a value to the population */
void addValue(double value);
/** Get the mean of all the added values */
double getMean();
/** Get the standard deviation from a sample of the population. */
double getStDevSample();
/** Gets the standard deviation for the entire population. */
double getStDevPopulation();
}
private static class WaldorfPopulation implements Stats1D {
private double mean = 0.0;
private double sSum = 0.0;
private int count = 0;
@Override
public void addValue(double value) {
double tmpMean = mean;
double delta = value - tmpMean;
mean += delta / ++count;
sSum += delta * (value - mean);
}
@Override
public double getMean() { return mean; }
@Override
public double getStDevSample() { return Math.sqrt(sSum / (count - 1)); }
@Override
public double getStDevPopulation() { return Math.sqrt(sSum / (count)); }
}
private static class StandardPopulation implements Stats1D {
private double sum = 0.0;
private double sumOfSquares = 0.0;
private int count = 0;
@Override
public void addValue(double value) {
sum += value;
sumOfSquares += value * value;
count++;
}
@Override
public double getMean() { return sum / count; }
@Override
public double getStDevSample() {
return (float) Math.sqrt((sumOfSquares - ((sum * sum) / count)) / (count - 1));
}
@Override
public double getStDevPopulation() {
return (float) Math.sqrt((sumOfSquares - ((sum * sum) / count)) / count);
}
}
/**
* Returns a way to measure a population of data using Waldorf''s method.
* This method is better if your population or values are so large that
* the sum of x-squared may overflow. It''s also probably faster if you
* need to recalculate the mean and standard deviation continuously,
* for example, if you are continually updating a graphic of the data as
* it flows in.
*
* @return A Stats1D object that uses Waldorf''s method.
*/
public static Stats1D getWaldorfStats() { return new WaldorfPopulation(); }
/**
* Return a way to measure the population of data using the sum-of-squares
* method. This is probably faster than Waldorf''s method, but runs the
* risk of data overflow.
*
* @return A Stats1D object that uses the sum-of-squares method
*/
public static Stats1D getSumOfSquaresStats() { return new StandardPopulation(); }
}
Fragmento de código:
public static double StandardDeviation(List<double> valueList)
{
if (valueList.Count < 2) return 0.0;
double sumOfSquares = 0.0;
double average = valueList.Average(); //.NET 3.0
foreach (double value in valueList)
{
sumOfSquares += Math.Pow((value - average), 2);
}
return Math.Sqrt(sumOfSquares / (valueList.Count - 1));
}
La biblioteca de Math.NET proporciona esto para usted de la caja.
PM> Install-Package MathNet.Numerics
var populationStdDev = new List<double>(1d, 2d, 3d, 4d, 5d).PopulationStandardDeviation();
var sampleStdDev = new List<double>(2d, 3d, 4d).StandardDeviation();
Consulte http://numerics.mathdotnet.com/docs/DescriptiveStatistics.html para obtener más información.
La respuesta aceptada por Jaime es genial, excepto que necesitas dividir por k-2 en la última línea (debes dividir por "number_of_elements-1"). Mejor aún, comience k en 0:
public static double StandardDeviation(List<double> valueList)
{
double M = 0.0;
double S = 0.0;
int k = 0;
foreach (double value in valueList)
{
k++;
double tmpM = M;
M += (value - tmpM) / k;
S += (value - tmpM) * (value - M);
}
return Math.Sqrt(S / (k-1));
}
Puede evitar hacer dos pasadas sobre los datos acumulando la media y la media cuadrática
cnt = 0
mean = 0
meansqr = 0
loop over array
cnt++
mean += value
meansqr += value*value
mean /= cnt
meansqr /= cnt
y formando
sigma = sqrt(meansqr - mean^2)
Un factor de cnt/(cnt-1)
es a menudo apropiado.
Por cierto, el primer pase sobre los datos en las respuestas de Demi y McWafflestix está oculto en las llamadas a Average
. Ese tipo de cosas es ciertamente trivial en una lista pequeña, pero si la lista excede el tamaño de la memoria caché, o incluso el conjunto de trabajo, se convierte en una oferta.
Si bien el algoritmo de suma de cuadrados funciona bien la mayor parte del tiempo, puede causar grandes problemas si se trata de números muy grandes. Básicamente puede terminar con una varianza negativa ...
Además, nunca, jamás, calcule un ^ 2 como pow (a, 2), a * a es casi seguro más rápido.
Con mucho, la mejor manera de calcular una desviación estándar es el método de Welford . Mi C está muy oxidado, pero podría ser algo así como:
public static double StandardDeviation(List<double> valueList)
{
double M = 0.0;
double S = 0.0;
int k = 1;
foreach (double value in valueList)
{
double tmpM = M;
M += (value - tmpM) / k;
S += (value - tmpM) * (value - M);
k++;
}
return Math.Sqrt(S / (k-2));
}
Si tiene toda la población (a diferencia de una población de muestra ), use return Math.Sqrt(S / (k-1));
.
EDITAR: He actualizado el código de acuerdo con los comentarios de Jason ...
EDITAR: también actualicé el código de acuerdo con los comentarios de Alex ...
Solución 10 veces más rápida que la de Jaime, pero tenga en cuenta que, como señaló Jaime:
"Si bien el algoritmo de suma de cuadrados funciona bien la mayor parte del tiempo, puede causar un gran problema si se trata de números muy grandes . Básicamente, puede terminar con una varianza negativa"
Si cree que está tratando con números muy grandes o una gran cantidad de números, debe calcular usando ambos métodos, si los resultados son iguales, puede estar seguro de que puede usar "mi" método para su caso.
public static double StandardDeviation(double[] data)
{
double stdDev = 0;
double sumAll = 0;
double sumAllQ = 0;
//Sum of x and sum of x²
for (int i = 0; i < data.Length; i++)
{
double x = data[i];
sumAll += x;
sumAllQ += x * x;
}
//Mean (not used here)
//double mean = 0;
//mean = sumAll / (double)data.Length;
//Standard deviation
stdDev = System.Math.Sqrt(
(sumAllQ -
(sumAll * sumAll) / data.Length) *
(1.0d / (data.Length - 1))
);
return stdDev;
}
/// <summary>
/// Calculates standard deviation, same as MATLAB std(X,0) function
/// <seealso cref="http://www.mathworks.co.uk/help/techdoc/ref/std.html"/>
/// </summary>
/// <param name="values">enumumerable data</param>
/// <returns>Standard deviation</returns>
public static double GetStandardDeviation(this IEnumerable<double> values)
{
//validation
if (values == null)
throw new ArgumentNullException();
int lenght = values.Count();
//saves from devision by 0
if (lenght == 0 || lenght == 1)
return 0;
double sum = 0.0, sum2 = 0.0;
for (int i = 0; i < lenght; i++)
{
double item = values.ElementAt(i);
sum += item;
sum2 += item * item;
}
return Math.Sqrt((sum2 - sum * sum / lenght) / (lenght - 1));
}