Как я могу сделать стандартное отклонение в Ruby?
У меня есть несколько записей с заданным атрибутом, и я хочу, чтобы найти стандартное отклонение.
Как мне это сделать?
9 ответов:
module Enumerable def sum self.inject(0){|accum, i| accum + i } end def mean self.sum/self.length.to_f end def sample_variance m = self.mean sum = self.inject(0){|accum, i| accum +(i-m)**2 } sum/(self.length - 1).to_f end def standard_deviation Math.sqrt(self.sample_variance) end end
тестирование это:
a = [ 20, 23, 23, 24, 25, 22, 12, 21, 29 ] a.standard_deviation # => 4.594682917363407
17.01.2012:
исправление "sample_variance" благодаря Dave Sag
похоже, что Анджела, возможно, хотела существующую библиотеку. После игры с statsample, array-Statistics и несколькими другими, я бы рекомендовал descriptive_statistics драгоценный камень, если вы пытаетесь избежать изобретения колеса.
gem install descriptive_statistics
$ irb 1.9.2 :001 > require 'descriptive_statistics' => true 1.9.2 :002 > samples = [1, 2, 2.2, 2.3, 4, 5] => [1, 2, 2.2, 2.3, 4, 5] 1.9.2p290 :003 > samples.sum => 16.5 1.9.2 :004 > samples.mean => 2.75 1.9.2 :005 > samples.variance => 1.7924999999999998 1.9.2 :006 > samples.standard_deviation => 1.3388427838995882
Я не могу говорить о его статистической правильности или о вашем комфорте с Перечислимым исправлением обезьян; но он прост в использовании и прост в использовании.
ответ, приведенный выше, элегантен, но имеет небольшую ошибку в нем. Не будучи главой статистики, я сел и подробно прочитал ряд веб-сайтов и обнаружил, что этот дал наиболее понятное объяснение того, как получить стандартное отклонение. http://sonia.hubpages.com/hub/stddev
ошибка в ответе выше находится в
sample_variance
метод.вот моя исправленная версия, а также простой модульный тест, который показывает, что он работает.
in
./lib/enumerable/standard_deviation.rb
#!usr/bin/ruby module Enumerable def sum return self.inject(0){|accum, i| accum + i } end def mean return self.sum / self.length.to_f end def sample_variance m = self.mean sum = self.inject(0){|accum, i| accum + (i - m) ** 2 } return sum / (self.length - 1).to_f end def standard_deviation return Math.sqrt(self.sample_variance) end end
на
./test
использование чисел, полученных из простой электронной таблицы.#!usr/bin/ruby require 'enumerable/standard_deviation' class StandardDeviationTest < Test::Unit::TestCase THE_NUMBERS = [1, 2, 2.2, 2.3, 4, 5] def test_sum expected = 16.5 result = THE_NUMBERS.sum assert result == expected, "expected #{expected} but got #{result}" end def test_mean expected = 2.75 result = THE_NUMBERS.mean assert result == expected, "expected #{expected} but got #{result}" end def test_sample_variance expected = 2.151 result = THE_NUMBERS.sample_variance assert result == expected, "expected #{expected} but got #{result}" end def test_standard_deviation expected = 1.4666287874 result = THE_NUMBERS.standard_deviation assert result.round(10) == expected, "expected #{expected} but got #{result}" end end
Я не большой поклонник добавления методов в
Enumerable
так как там могут быть нежелательные побочные эффекты. Он также дает методы, действительно специфичные для массива чисел для любого класса, наследующего отEnumerable
, что в большинстве случаев не имеет смысла.хотя это нормально для тестов, скриптов или небольших приложений, это рискованно для больших приложений, поэтому вот альтернатива, основанная на ответе @tolitius, который уже был идеальным. Это больше для справки, чем ничего остальное:
module MyApp::Maths def self.sum(a) a.inject(0){ |accum, i| accum + i } end def self.mean(a) sum(a) / a.length.to_f end def self.sample_variance(a) m = mean(a) sum = a.inject(0){ |accum, i| accum + (i - m) ** 2 } sum / (a.length - 1).to_f end def self.standard_deviation(a) Math.sqrt(sample_variance(a)) end end
и затем вы используете его как таковой:
2.0.0p353 > MyApp::Maths.standard_deviation([1,2,3,4,5]) => 1.5811388300841898 2.0.0p353 :007 > a = [ 20, 23, 23, 24, 25, 22, 12, 21, 29 ] => [20, 23, 23, 24, 25, 22, 12, 21, 29] 2.0.0p353 :008 > MyApp::Maths.standard_deviation(a) => 4.594682917363407 2.0.0p353 :043 > MyApp::Maths.standard_deviation([1,2,2.2,2.3,4,5]) => 1.466628787389638
поведение такое же, но оно избегает накладных расходов и рисков добавления методов к
Enumerable
.
представленные вычисления не очень эффективны, потому что они требуют нескольких (по крайней мере двух, но часто трех, потому что вы обычно хотите представить среднее значение в дополнение к std-dev) проходит через массив.
Я знаю, что Ruby-это не место для поиска эффективности, но вот моя реализация, которая вычисляет среднее и стандартное отклонение с одним проходом по значениям списка:
module Enumerable def avg_stddev return nil unless count > 0 return [ first, 0 ] if count == 1 sx = sx2 = 0 each do |x| sx2 += x**2 sx += x end [ sx.to_f / count, Math.sqrt( # http://wijmo.com/docs/spreadjs/STDEV.html (sx2 - sx**2.0/count) / (count - 1) ) ] end end
как простая функция, учитывая список чисел:
def standard_deviation(list) mean = list.inject(:+) / list.length.to_f var_sum = list.map{|n| (n-mean)**2}.inject(:+).to_f sample_variance = var_sum / (list.length - 1) Math.sqrt(sample_variance) end
если записи под рукой имеют тип
Integer
илиRational
, вы можете вычислить дисперсию с помощьюRational
вместоFloat
чтобы избежать ошибок, вносимых округлением.например:
def variance(list) mean = list.reduce(:+)/list.length.to_r sum_of_squared_differences = list.map { |i| (i - mean)**2 }.reduce(:+) sum_of_squared_differences/list.length end
(было бы разумно добавить обработку специальных случаев для пустых списков и других крайних случаев.)
тогда квадратный корень можно определить как:
def std_dev(list) Math.sqrt(variance(list)) end
в случае, если люди используют postgres ... он предоставляет агрегатные функции для stddev_pop и stddev_samp -агрегатные функции postgresql
stddev (equiv of stddev_samp) доступно по крайней мере с postgres 7.1, так как 8.2 предоставляются как samp, так и pop.