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文で計算するようにしているので,単純に分散や相関係数の式を計算するよりは高速です(高速だと思います.実測してないので・・・)
- http://en.wikipedia.org/wiki/Correlation の Computing correlation accurately in a single pass を参照
- 配列の要素は全て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.9455b = [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