我如何在Ruby中做标准偏差?
我有几个具有给定属性的logging,我想find标准偏差。
我怎么做?
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 +(im)**2 } sum/(self.length - 1).to_f end def standard_deviation return Math.sqrt(self.sample_variance) end end
testing它:
a = [ 20, 23, 23, 24, 25, 22, 12, 21, 29 ] a.standard_deviation # => 4.594682917363407
2012年1月17日:
修复“sample_variance”感谢Dave Sag
看来,安吉拉可能一直想要一个现有的图书馆。 在玩statsample,array-statisics和其他一些游戏之后,如果你试图避免重新发明轮子,我会推荐descriptive_statistics gem。
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
方法中。
这是我修正的版本,以及一个简单的unit testing,显示它的作品。
在./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
inheritance的任何类的数组数组提供了真正特定的方法,这在大多数情况下是没有意义的。
虽然这对testing,脚本或小应用程序来说很好,但是对于大型应用程序来说是很危险的,所以这里有一个基于@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
添加方法的开销和风险。
所提出的计算效率不高,因为它们需要几个(至less两个,但通常是三个,因为除了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
如果手头的loggingtypes为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(相当于stddev_samp)可用,因为至lesspostgres 7.1,因为8.2提供了samp和pop。
或者如何:
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