LoginSignup
4
2

More than 3 years have passed since last update.

Arrayクラスを拡張して、回帰直線を求める(配列の平均/分散/不偏分散/標準偏差/共分散/を定義する)

Last updated at Posted at 2019-10-27

概要

ポートフォリオで体重管理アプリを作成しているのですが、過去の数値から将来の体重を予測する機能を実装中です。
最小二乗法で回帰直線の傾きと切片を導くロジックをRubyで書きました。

詳細

y = 2x となるような配列を用意しました。(実際はモデルの変数を使う予定です)
時間が空いたときに解説を書きます...(すみません)

Arrayクラスを拡張

ついでに不偏分散と標準偏差も考えました。

module ArrayStatistics
  refine Array do
    def average # 平均
      sum.fdiv(size)
    end

    def variance # 分散
      @average = average
      inject(0) { |result,n| result + (n - @average) ** 2 }.fdiv(size)
    end

    def unbiasedvariance # 不偏分散
      @average = average
      inject(0) { |result,n| result + (n - @average) ** 2 }.fdiv(size - 1)
    end

    def stadiv # 標準偏差
      Math.sqrt(variance)
    end

    def convariance # 共分散
      array1 = map{|xs| xs[0]}
      array2 = map{|ys| ys[1]}
      @average1 = array1.average
      @average2 = array2.average
      inject(0) { |result,n| result + (n[0] - @average1) * (n[1] - @average2)}.fdiv(size)
    end
  end
end

@ average = averageにすることでメモ化され次の処理速度があがります
除算は'/'ではなく、'fdiv'を使用してます。

実行するクラス

class Report < ApplicationRecord
  using ArrayStatistics
  def ols
    xs = [1,2,3,4,5]
    ys = [2,4,6,8,10]
    arry = xs.zip(ys)

    a = array.convariance.fdiv(xs.variance) # 回帰直線の傾き(a = XとYの共分散 / Xの分散)
    b = ys.average - a * xs.average # 回帰直線の切片(b = Yの平均 - 回帰直線の傾き * Xの平均)
    {a: a,b: b}
  end
end

実行結果

Image from Gyazo

a = 2
b = 0
が表示されました。

ちなみに...

Hashクラスでもできますが、重回帰分析とかには、変更しにくそうなので、
Arrayクラスがおすすめかなと思います。

Hashを拡張

module HashStatistics
  refine Hash do
    class Hash
      def convariance #共分散
        ary1 = keys
        ary2 = values
        each_with_index.inject(0) { |result,(n,index)| result + (ary1[index] - ary1.average) * (ary2[index] - ary2.average)} / size
      end
    end
  end
end

実行するクラス(Hash対応版)

class Report < ApplicationRecord
  using ArrayStatistics
  using HashStatistics
  def ols
    xs = [1,2,3,4,5]
    ys = [2,4,6,8,10]

    ary = [xs,ys].transpose
    hsh = Hash[*ary.flatten]

    a = hsh.convariance / xs.variance #回帰直線の傾き(a = XとYの共分散 / Xの分散)
    b = ys.average - a * xs.average #回帰直線の切片(b = Yの平均 - 回帰直線の傾き * Xの平均)

    {a: a,b: b}
  end
end

参考資料

Basic Knowledge on Data Analysis 単回帰分析とは

あとがき

追記(2019/10/28)

scivola様より非常にありがたいアドバイスをコメントにいただき、書き直しました。

4
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2