varianza variacion poblacional para estandar ejemplos desviacion datos coeficiente agrupados ruby standard-deviation

ruby - variacion - desviacion estandar y varianza



¿Cómo puedo hacer desviación estándar en Ruby? (9)

Tengo varios registros con un atributo dado, y quiero encontrar la desviación estándar.

¿Cómo puedo hacer eso?


Como una función simple, dada una lista de números:

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


En caso de que la gente esté usando postgres ... proporciona funciones agregadas para stddev_pop y stddev_samp - funciones agregadas postgresql

stddev (equivalente a stddev_samp) disponible desde al menos postgres 7.1, ya que se proporcionan 8.2 samp y pop.


La respuesta dada arriba es elegante pero tiene un ligero error. Al no ser un jefe de estadísticas, me senté y leí en detalle una serie de sitios web y encontré que este daba la explicación más comprensible de cómo derivar una desviación estándar. http://sonia.hubpages.com/hub/stddev

El error en la respuesta anterior está en el método sample_variance .

Aquí está mi versión corregida, junto con una prueba de unidad simple que muestra que funciona.

en ./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

en ./test usando números derivados de una hoja de cálculo simple.

#!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


Los cálculos presentados no son muy eficientes porque requieren varios (al menos dos, pero a menudo tres porque normalmente desea presentar un promedio además de std-dev) a través de la matriz.

Sé que Ruby no es el lugar para buscar eficiencia, pero aquí está mi implementación que calcula el promedio y la desviación estándar con una sola pasada sobre los valores de la lista:

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


No soy un gran fan de agregar métodos a Enumerable ya que podría haber efectos secundarios no deseados. También proporciona métodos realmente específicos a una serie de números para cualquier clase heredada de Enumerable , lo que no tiene sentido en la mayoría de los casos.

Si bien esto está bien para pruebas, scripts o aplicaciones pequeñas, es riesgoso para aplicaciones más grandes, así que aquí hay una alternativa basada en la respuesta de @tolitius que ya era perfecta. Esto es más para referencia que cualquier otra cosa:

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

Y luego lo usas como tal:

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

El comportamiento es el mismo, pero evita los gastos generales y los riesgos de agregar métodos a Enumerable .


O ¿qué tal

class Stats def initialize( a ) @avg = a.count > 0 ? a.sum / a.count.to_f : 0.0 @stdev = a.count > 0 ? ( a.reduce(0){ |sum, v| sum + (@avg - v) ** 2 } / a.count ) ** 0.5 : 0.0 end end


Parece que Angela puede haber estado queriendo una biblioteca existente. Después de jugar con statsample, array-statisics y algunos otros, recomendaría la gema descriptive_statistics si intentas evitar reinventar la rueda.

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

No puedo hablar de su corrección estadística, ni de su comodidad con el parche de monos Enumerable; pero es fácil de usar y es fácil de contribuir.


Si los registros disponibles son de tipo Integer o Rational , es posible que desee calcular la varianza utilizando Rational lugar de Float para evitar errores introducidos por el redondeo.

Por ejemplo:

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

(Sería prudente agregar el manejo de casos especiales para listas vacías y otros casos de borde).

Entonces la raíz cuadrada se puede definir como:

def std_dev(list) Math.sqrt(variance(list)) end


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

Probandolo

a = [ 20, 23, 23, 24, 25, 22, 12, 21, 29 ] a.standard_deviation # => 4.594682917363407

17/01/2012:

arreglando "sample_variance" gracias a Dave Sag