mk-mode BLOG

このブログは自作の自宅サーバに構築した Debian GNU/Linux で運用しています。
PC・サーバ構築等の話題を中心に公開しております。(クローンサイト: GitHub Pages

ブログ開設日2009-01-05
サーバ連続稼働時間
Reading...
Page View 合計
Reading...
今日
Reading...
昨日
Reading...

Ruby - Array クラス拡張で重回帰式計算!

[ プログラミング, 数学 ] [ Ruby ]

こんばんは。

以前、2変量(説明(独立)変数1個、目的(従属)変数1個)の「単回帰直線」の計算を Ruby の Array クラスを拡張する方法で実装しました。

今回は、説明(独立)変数2個以上、目的(従属)変数1個の「重回帰式」の計算を Ruby の Array クラスを拡張する方法で実装してみました。

0. 前提条件

  • Ruby 2.1.4-p265 での作業を想定。

1. 重回帰式について

まず、簡単に重回帰式について。
ここでは詳細に説明しない。過去の当ブログ記事等を参照のこと。
(偏微分して得られた連立方程式を解く(平方和・積和を行列に見立てて計算する)方法。分散・共分散を行列に見立てて計算する方法等もある)

連立方程式の解法には「ガウスの消去法」を使用した。

2. Ruby スクリプト作成

以下のように Array クラスを拡張してメソッドを定義してみた。
(ちなみに、x1, x2, … を左辺、y を右辺として連立方程式を解いている)

regression_multi.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
class Array
  def reg_multi(y)
    # 元の数、変量内のサンプル数
    e_size, s_size = self.size, y.size

    # 以下の場合は例外スロー
    # - 引数の配列が Array クラスでない
    # - 自身配列が空
    # - 配列サイズが異なれば例外
    raise "Argument is not a Array class!"    unless y.class == Array
    raise "Self array is nil!"                if     e_size == 0
    raise "Argument array size is invalid!"   unless self[0].size == s_size
    1.upto(e_size - 1) do |i|
      raise "Argument array size is invalid!" unless self[0].size == self[i].size
    end

    # 式(1)用
    mtx = Array.new
    ary= [s_size]
    0.upto(e_size - 1) do |i|
      ary << self[i].inject(0.0) { |sum, a| sum += a }
    end
    ary << y.inject(0.0) { |sum, a| sum += a }
    mtx << ary

    # 式(2)...用
    0.upto(e_size - 1) do |i|
      # b0
      ary = [self[i].inject(0.0) { |sum, a| sum += a }]

      # b1 ...
      0.upto(e_size - 1) do |j|
        sum = 0.0
        0.upto(s_size - 1) { |k| sum += self[i][k] * self[j][k] }
        ary << sum
      end

      # 右辺
      sum = 0.0
      0.upto(s_size - 1) { |j| sum += self[i][j] * y[j] }
      ary << sum
      mtx << ary
    end

    # 連立方程式を解く (ガウスの消去法)
    return gauss_e(mtx)
  end

  private

  # ガウスの消去法
  def gauss_e(ary)
    # 元の数
    n = ary.size

    # 前進消去
    0.upto(n - 2) do |k|
      (k + 1).upto(n - 1) do |i|
        if ary[k][k] == 0
          puts "解けない!"
          exit 1
        end
        d = ary[i][k] / ary[k][k].to_f
        (k + 1).upto(n) do |j|
          ary[i][j] -= ary[k][j] * d
        end
      end
    end

    # 後退代入
    (n - 1).downto(0) do |i|
      if ary[i][i] == 0
        puts "解けない!"
        exit 1
      end
      d = ary[i][n]
      (i + 1).upto(n - 1) do |j|
        d -= ary[i][j] * ary[j][n]
      end
      ary[i][n] = d / ary[i][i].to_f
    end

    return ary.map { |a| a[-1] }
  end
end

# 説明(独立)変数と目的(従属)変数
ary_x = [
  [17.5, 17.0, 18.5, 16.0, 19.0, 19.5, 16.0, 18.0, 19.0, 19.5],
  [30, 25, 20, 30, 45, 35, 25, 35, 35, 40]
]
ary_y = [45, 38, 41, 34, 59, 47, 35, 43, 54, 52]

# 重回帰式算出(b0, b1, b2, ...)
reg_multi = ary_x.reg_multi(ary_y)

# 結果出力
ary_x.each_with_index do |x, i|
  puts "説明変数 X#{i + 1} = {#{ary_x[i].join(', ')}}"
end
puts "目的変数 Y  = {#{ary_y.join(', ')}}"
puts "---"
p reg_multi

3. Ruby スクリプト実行

実行してみる。

1
2
3
4
5
6
$ ruby regression_multi.rb
説明変数 X1 = {17.5, 17.0, 18.5, 16.0, 19.0, 19.5, 16.0, 18.0, 19.0, 19.5}
説明変数 X2 = {30, 25, 20, 30, 45, 35, 25, 35, 35, 40}
目的変数 Y  = {45, 38, 41, 34, 59, 47, 35, 43, 54, 52}
---
[-34.71293083506825, 3.469812630117974, 0.5330094841545223]

表計算ソフト等で計算した結果と同じになっていることを確認する。


重回分析を手軽に行いたい案件があったため、今回 Array クラスを拡張してみた次第です。

以上。

Comments