Как определить стандартное отклонение (стандартное отклонение) набора значений?
Мне нужно знать, если число по сравнению с набором чисел находится за пределами 1 стандартного отклонения от среднего значения и т. Д..
13 ответов
Хотя алгоритм суммы квадратов работает в большинстве случаев нормально, он может вызвать большие проблемы, если вы имеете дело с очень большими числами. В принципе, вы можете получить отрицательную дисперсию...
Кроме того, никогда, никогда, никогда не вычисляйте a^2 как pow(a,2), a * a почти наверняка быстрее.
Безусловно, лучший способ вычисления стандартного отклонения - это метод Уэлфорда. Мой C очень ржавый, но может выглядеть примерно так:
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));
}
Если у вас есть целая совокупность (в отличие от выборочной совокупности), используйте return Math.Sqrt(S / (k-1));
,
РЕДАКТИРОВАТЬ: я обновил код в соответствии с замечаниями Джейсона...
РЕДАКТИРОВАТЬ: Я также обновил код в соответствии с замечаниями Алекса...
Решение в10 раз быстрее, чем у Хайме, но учтите, что, как указал Хайме:
"Хотя алгоритм суммирования квадратов в большинстве случаев работает нормально, он может вызвать большие проблемы, если вы работаете с очень большими числами. В принципе вы можете получить отрицательную дисперсию"
Если вы считаете, что имеете дело с очень большими числами или с очень большим количеством чисел, вам следует рассчитать оба метода, если результаты совпадают, вы точно знаете, что можете использовать "мой" метод для вашего случая.
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;
}
Принятый ответ Хайме великолепен, за исключением того, что вам нужно разделить на k-2 в последней строке (вам нужно разделить на "number_of_elements-1"). Еще лучше, начните k с 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));
}
Библиотека Math.NET предоставляет это для вас из коробки.
PM> Инсталляционный пакет MathNet.Numerics
var populationStdDev = new List<double>(1d, 2d, 3d, 4d, 5d).PopulationStandardDeviation();
var sampleStdDev = new List<double>(2d, 3d, 4d).StandardDeviation();
См. http://numerics.mathdotnet.com/docs/DescriptiveStatistics.html для получения дополнительной информации.
Фрагмент кода:
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));
}
Вы можете избежать двух проходов по данным, накапливая среднее и среднеквадратичное значения.
cnt = 0
mean = 0
meansqr = 0
loop over array
cnt++
mean += value
meansqr += value*value
mean /= cnt
meansqr /= cnt
и формирование
sigma = sqrt(meansqr - mean^2)
Фактор cnt/(cnt-1)
также часто уместно.
Кстати, первый проход по данным в ответах Деми и McWafflestix скрыт в звонках Average
, Подобные вещи, конечно, тривиальны в небольшом списке, но если список превышает размер кеша или даже рабочего набора, это становится выгодной сделкой.
С методами расширения.
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);
}
}
}
Я обнаружил, что полезный ответ Роба не совсем соответствует тому, что я видел, используя Excel. Чтобы соответствовать Excel, я передал Среднее значение для valueList в расчет StandardDeviation.
Вот мои два цента... и, очевидно, вы могли бы вычислить скользящее среднее (ma) из valueList внутри функции - но я уже сделал это, прежде чем нуждался в стандартном отклонении.
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));
}
Мы можем использовать модуль статистики в Python. Он имеет команды stedev () и pstdev () для вычисления стандартного отклонения выборки и совокупности соответственно.
Подробности здесь: https://www.geeksforgeeks.org/python-statistics-stdev/
импортировать статистику как st print(st.ptdev(dataframe['имя столбца']))
/// <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));
}
Ни в одном из существующих ответов не учитывается, что вам может потребоваться включить дельта-степени свободы, например, при портировании кода из NumPy или аналогичных библиотек Python/R. В таком случае:
public static float StandardDeviation(double[] data, int deltaDegreesOfFreedom = 1)
{
double avg = data.Average();
double accu = data.Select(x => x - avg).Select(z => z * z / (data.Length - deltaDegreesOfFreedom)).Sum();
return Math.Sqrt(accu);
}
Проблема всех остальных ответов в том, что они предполагают, что ваши данные хранятся в большом массиве. Если ваши данные поступают на лету, это был бы лучший подход. Этот класс работает независимо от того, как или если вы храните свои данные. Это также дает вам выбор метода Вальдорфа или метода суммы квадратов. Оба метода работают с использованием одного прохода.
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(); }
}
Это стандартное отклонение населения
private double calculateStdDev(List<double> values)
{
double average = values.Average();
return Math.Sqrt((values.Select(val => (val - average) * (val - average)).Sum()) / values.Count);
}
Для образца стандартного отклонения просто измените [values.Count] на [values.Count -1] в приведенном выше коде.
Убедитесь, что в вашем наборе нет только одной точки данных.