Rubyの配列(Arrayクラス)を拡張して和,分散,標準偏差,相関係数を計算する

多分誰かが行っているので,車輪の再開発だとは思いますが・・・. http://raa.ruby-lang.org/project/math-statistics/ にあるので完全に車輪の再開発ですが,ちょっと機能も加わっています(減ってもいます).nakaoさん,情報ありがとうございました.

  • オブジェクト指向らしく,配列に直接命令するようにしています.
    • [1,2,3].avg => 2.0
  • 相関係数の計算もできちゃいます.
  • nilがある場合,nilを無視するようにしています
    • 以上2つは,math-statisticsに比べ追加している機能.
  • ブロックに色々与える構文には対応していません
    • これが math-statistics から減った機能
  • 分散,相関係数共に,一度のfor文で計算するようにしているので,単純に分散や相関係数の式を計算するよりは高速です(高速だと思います.実測してないので・・・)
  • 配列の要素は全てto_fして計算しています.
    • 数値以外の型の場合は要注意

使い方

配列に対して追加して定義したメソッドを与えます.

a = [31 ,29, 29, 30, 32, 33, 32, 31, 26, 28, 31,nil]
puts a.sum # aの合計 => 332.0
puts a.avg # aの平均 => 30.181818...
puts a.mean # avgの別名 => 30.181818...
puts a.var # aの分散 => 3.785
puts a.stddev # aの標準偏差 => 1.9455

b = [nil,326,364,283,369,417,436,438,296,263,389,335]
puts a.corrcoef(b) # (a[0],b[0]),(a[1],b[1]),... の相関係数 => 0.760

ソース

class Array
  def sum_with_number
    s = 0.0
    n = 0
    self.each do |v|
      next if v.nil?
      s += v.to_f
      n += 1
    end
    [s, n]
  end
  
  def sum
    s, n = self.sum_with_number
    s
  end
  
  def avg
    s, n = self.sum_with_number
    s / n
  end
  alias mean avg
  
  def var
    c = 0
    while self[c].nil?
      c += 1
    end
    mean = self[c].to_f
    sum = 0.0
    n = 1
    (c+1).upto(self.size-1) do |i|
      next if self[i].nil?
      sweep = n.to_f / (n + 1.0)
      delta = self[i].to_f - mean
      sum += delta * delta * sweep
      mean += delta / (n + 1.0)
      n += 1
    end
    sum / n.to_f
  end
  
  def stddev
    Math.sqrt(self.var)
  end

  def corrcoef(y)
    raise "Invalid Argument Array Size" unless self.size == y.size
    sum_sq_x = 0.0
    sum_sq_y = 0.0
    sum_coproduct = 0.0
    c = 0
    while self[c].nil? || y[c].nil?
      c += 1
    end
    mean_x = self[c].to_f
    mean_y = y[c].to_f
    n = 1
    (c+1).upto(self.size-1) do |i|
      next if self[i].nil? || y[i].nil?
      sweep = n.to_f / (n + 1.0)
      delta_x = self[i].to_f - mean_x
      delta_y = y[i].to_f - mean_y
      sum_sq_x += delta_x * delta_x * sweep
      sum_sq_y += delta_y * delta_y * sweep
      sum_coproduct += delta_x * delta_y * sweep
      mean_x += delta_x / (n + 1.0)
      mean_y += delta_y / (n + 1.0)
      n += 1
    end
    pop_sd_x = Math.sqrt(sum_sq_x / n.to_f)
    pop_sd_y = Math.sqrt(sum_sq_y / n.to_f)
    cov_x_y = sum_coproduct / n.to_f
    cov_x_y / (pop_sd_x * pop_sd_y)
  end

end