【发布时间】:2011-10-13 04:47:29
【问题描述】:
我有几个具有给定属性的记录,我想找到标准差。
我该怎么做?
【问题讨论】:
我有几个具有给定属性的记录,我想找到标准差。
我该怎么做?
【问题讨论】:
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
感谢 Dave Sag,修复了“sample_variance”
【讨论】:
sample_variance 方法中存在错误。请参阅下面的答案。
sum/(self.length - 1).to_f这行你为什么要从Enumerable的长度中减去1?
看来安吉拉可能一直想要一个现有的图书馆。在使用 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
我不能说它的统计正确性,或者你对猴子修补 Enumerable 的舒适度;但它易于使用且易于贡献。
【讨论】:
NoMethodError: undefined method zero?对于 nil:NilClass` 和 (Object doesn't support #inspect).
上面给出的答案很优雅,但有一点错误。我自己不是统计负责人,我坐下来详细阅读了许多网站,发现这个网站对如何得出标准偏差给出了最容易理解的解释。 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 中使用来自简单电子表格的数字。
#!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
【讨论】:
assert result - expected < 1e-10,添加了 require test/unit 并将第一个要求更改为 `require 'enumerable'。
我不太喜欢向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 添加方法的开销和风险。
【讨论】:
所呈现的计算效率不是很高,因为它们需要多次(至少两个,但通常是三个,因为除了标准差之外,您通常还想呈现平均值)通过数组。
我知道 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
【讨论】:
inject 的低效率,而且我不确定你对数组有什么好处,除了它创建了 n 个对象 - 但这些是短暂的对象,不应该是一个巨大的资源消耗。跨度>
作为一个简单的函数,给定一个数字列表:
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 aggregate functions
stddev(相当于 stddev_samp)至少从 postgres 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
【讨论】:
您可以将此作为辅助方法并在任何地方对其进行评估。
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
【讨论】: