Стандартное отклонение с использованием LINQ дает другой ответ от итерационных вычислений
Если я делаю расчет стандартного отклонения для образца с использованием этого кода, несколько измененного из этого вопроса SO:
public double CalculateStandardDeviation(List<double> values, bool sample = false)
{
double mean = 0.0;
double sum = 0.0;
double stdDev = 0.0;
int count = 0;
foreach (double val in values)
{
count++;
double delta = val - mean;
mean += delta / count;
sum += delta * (val - mean);
}
if (1 < count)
stdDev = Math.Sqrt(sum / (count - (sample ? 1 : 0)));
return stdDev;
}
Используя этот модульный тест:
[Test]
public void Sample_Standard_Deviation_Returns_Expected_Value()
{
//original cite: http://warrenseen.com/blog/2006/03/13/how-to-calculate-standard-deviation/
double expected = 2.23606797749979;
double tolerance = 1.0 / System.Math.Pow(10, 13);
var cm = new CommonMath();//a library of math functions we use a lot
List<double> values = new List<double> { 4.0, 2.0, 5.0, 8.0, 6.0 };
double actual = cm.CalculateStandardDeviation(values, true);
Assert.That(actual, Is.EqualTo(expected).Within(tolerance));
}
Тест проходит с результирующим значением в пределах указанного допуска.
Однако, если я использую этот код Linq-ified, он завершится неудачно, возвращая значение 2,5 (как если бы это было стандартное отклонение популяции вместо этого):
double meanOfValues = values.Average();
double sumOfValues = values.Sum();
int countOfValues = values.Count;
double standardDeviationOfValues =
Math.Sqrt(sumOfValues / (countOfValues - (sample ? 1 : 0)));
return standardDeviationOfValues;
Поскольку я никогда не брал статистику (поэтому, пожалуйста, будьте осторожны), Linq-ification (это слово) значений из списка кажется, что они должны дать мне те же результаты, но они не дают, и я не понимаю что я сделал не так Действие выбора между N & N-1 одинаково для обоих, так почему же ответ не одинаков?
3 ответа
Ваша версия LINQ не рассчитывает стандартное отклонение. Стандартное отклонение основано на сумме квадратов разностей от среднего, поэтому измените на:
double meanOfValues = values.Average();
double sumOfValues = values.Select(v => (v-meanOfValues)*(v-meanOfValues)).Sum();
int countOfValues = values.Count;
double standardDeviationOfValues =
Math.Sqrt(sumOfValues / (countOfValues - (sample ? 1 : 0)));
return standardDeviationOfValues;
Чтобы просмотреть значения один раз, вы можете использовать Aggregate
но это не лучше, чем обычная функция:
var g = values.Aggregate(new { mean = 0.0, sum = 0.0, count = 0 },
(acc, val) => {
var newcount = acc.count+1;
double delta = val-acc.mean;
var newmean = acc.mean + delta / newcount;
return new { mean = newmean, sum = acc.sum+delta*(val-newmean), count = newcount };
});
var stdDev = Math.Sqrt(g.sum / (g.count - (sample ? 1 : 0)));
Начнем с этого
values.Sum();
и сумма, которую вы получаете от
sum += delta * (val - mean);
не то же самое.
В следующий раз вы можете начать с TDD и решить все возможные проблемы.
РЕДАКТИРОВАТЬ: стандартное отклонение в LINQ
Положите образец как ложный, и вы получите тот же ответ: 2.23606797749979 Если вы установите образец как истинный, вы получите 2.5!
Таким образом, вам нужно поместить одинаковое значение "образца" в обоих местах.