61

我有几条具有给定属性的记录,我想找到标准偏差。

我怎么做?

4

10 回答 10

99
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

2012 年 1 月 17 日:

感谢 Dave Sag,修复了“sample_variance”

于 2011-10-13T04:56:29.033 回答
37

看来安吉拉可能一直想要一个现有的图书馆。在使用 statsample、array-statistics 和其他一些工具之后,如果您想避免重新发明轮子,我会推荐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 

我不能说它的统计正确性,或者你对猴子修补 Enumerable 的舒适度;但它易于使用且易于贡献。

于 2012-09-06T16:38:03.617 回答
31

上面给出的答案很优雅,但有一点错误。我自己不是统计主管,我坐下来详细阅读了许多网站,发现这个网站对如何得出标准偏差给出了最容易理解的解释。http://sonia.hubpages.com/hub/stddev

上面答案的错误在于sample_variance方法。

这是我的更正版本,以及显示它有效的简单单元测试。

./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使用从简单电子表格中得出的数字。

带有示例数据的 Numbers 电子表格的屏幕快照

#!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
于 2011-11-24T02:07:37.743 回答
10

我不喜欢添加方法,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.

于 2014-01-15T16:59:21.103 回答
2

所呈现的计算效率不是很高,因为它们需要多次(至少两个,但通常是三个,因为除了 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
于 2015-08-06T09:20:31.737 回答
2

作为一个简单的函数,给定一个数字列表:

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
于 2016-07-29T00:44:55.877 回答
1

如果手头的记录是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
于 2017-02-06T19:38:05.110 回答
0

如果人们使用 postgres ...它为 stddev_pop 和 stddev_samp 提供聚合函数 - postgresql 聚合函数

stddev(相当于 stddev_samp)至少从 postgres 7.1 开始可用,因为 8.2 提供了 samp 和 pop。

于 2015-02-26T21:12:44.347 回答
0

或者怎么样:

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
于 2015-06-02T15:33:15.113 回答
0

您可以将此作为辅助方法并在任何地方进行评估。

def calc_standard_deviation(arr)
    mean = arr.sum(0.0) / arr.size
    sum = arr.sum(0.0) { |element| (element - mean) ** 2 }
    variance = sum / (arr.size - 1)
    standard_deviation = Math.sqrt(variance)
end
于 2021-10-06T16:34:12.807 回答