Development Tip

Ruby에서 표준 편차를 어떻게 할 수 있습니까?

yourdevel 2021. 1. 9. 11:03
반응형

Ruby에서 표준 편차를 어떻게 할 수 있습니까?


주어진 속성을 가진 여러 레코드가 있고 표준 편차를 찾고 싶습니다.

어떻게하나요?


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"수정


Angela는 기존 도서관을 원했던 것 같습니다. 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에 대한 당신의 편안함에 대해 말할 수 없습니다. 하지만 사용하기 쉽고 기여하기 쉽습니다.


위에 주어진 대답은 우아하지만 약간의 오류가 있습니다. 나는 통계가 아닌 내가 앉아서 여러 웹 사이트를 자세히 읽었고 이것이 표준 편차를 도출하는 방법에 대한 가장 이해하기 쉬운 설명을 제공한다는 것을 알았습니다. 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

나는 Enumerable원치 않는 부작용이있을 수 있기 때문에 방법을 추가하는 것을 좋아하지 않습니다 . 또한에서 상속하는 모든 클래스에 숫자 배열에 특정한 메서드를 제공합니다 Enumerable. 이는 대부분의 경우 의미가 없습니다.

While this is fine for tests, scripts or small apps, it's risky for larger applications, so here's an alternative based on @tolitius' answer which was already perfect. This is more for reference than anything else:

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

And then you use it as such:

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

The behavior is the same, but it avoids the overheads and risks of adding methods to Enumerable.


The presented computation are not very efficient because they require several (at least two, but often three because you usually want to present average in addition to std-dev) passes through the array.

I know Ruby is not the place to look for efficiency, but here is my implementation that computes average and standard deviation with a single pass over the list values:

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

As a simple function, given a list of numbers:

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사용하여 분산을 계산할 수 있습니다 .RationalFloat

예를 들면 :

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와 동일)는 samp와 pop이 모두 제공되는 8.2 이후 최소한 postgres 7.1부터 사용 가능합니다.


또는 어떻습니까?

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

참조 URL : https://stackoverflow.com/questions/7749568/how-can-i-do-standard-deviation-in-ruby

반응형