CUBE SUGAR CONTAINER

技術系のこと書きます。

統計: 偏相関係数で擬似(無)相関の有無を調べる

以前、このブログでは共分散や相関係数について扱ったことがある。 共分散や相関係数というのは、二つの変数間に線形な関係があるかを調べる方法だった。

blog.amedama.jp

しかし、実はただの相関係数では「第三の変数」からの影響を受けてしまう場合がある。 それというのは、第三の変数の存在によって、あたかも相関しているように見える (疑似相関) あるいは相関していないように見える (疑似無相関) というもの。

これは実際の例がないと、なかなか分かりづらいものだと思うんだけど良い例があったので紹介してみる。 今回はプロ野球の打撃成績に潜む疑似無相関を偏相関係数であぶり出してみることにする。

データをスクレイピングする

ひとまずデータがないと話にならないので、まずはスクレイピングしてくるところから始める。

使った環境は次の通り。

$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.12.5
BuildVersion:   16F73
$ python --version
Python 3.6.1

今回は全て Python を使って処理する。 使うライブラリは pandasnumpy だけ。 ただし pandas をスクレイピングに使うときは別途依存ライブラリが必要になる。

$ pip install pandas beautifulsoup4 requests lxml html5lib

各年度の選手成績はプロ野球の公式サイトから得られる。

npb.jp

試しに規定打席に達した選手の打撃成績を取得してみよう。 pandas#read_html() にスクレイピングしたい URL を指定する。

>>> import pandas as pd
>>> url = 'http://npb.jp/bis/2016/stats/bat_c.html'
>>> tables = pd.read_html(url)

これだけでページに含まれるテーブルを上手いことパースして DataFrame に落とし込んでくれる。

>>> df_with_header = tables[0]  # 成績のテーブルを取り出す
>>> df_with_header
                            0       1    2     3    4    5    6    7    8   \
0   規定打席 :チーム試合数×3.1 (端数は四捨五入)     NaN  NaN   NaN  NaN  NaN  NaN  NaN  NaN
1                          順 位     選 手  打 率   試 合  打 席  打 数  得 点  安 打  二塁打
2                            1   角中 勝也  (ロ)  .339  143  607  525   74  178
3                            2   西川 遥輝  (日)  .314  138  593  493   76  155
4                            3   浅村 栄斗  (西)  .309  143  611  557   73  172
5                            4   糸井 嘉男  (オ)  .306  143  616  532   79  163
6                            5   柳田 悠岐  (ソ)  .306  120  536  428   82  131
7                            6   内川 聖一  (ソ)  .304  141  605  556   62  169
8                            7   秋山 翔吾  (西)  .296  143  671  578   98  171
9                            8    陽 岱鋼  (日)  .293  130  555  495   66  145
10                           9    中村 晃  (ソ)  .287  143  612  488   69  140
...

十年分のデータを取得する

先ほど実行した内容にもとづいて十年分のデータを取得してみよう。 これは、単年ではデータ点数が少なくてあまり信頼のおけるものではなくなるため。

今回扱うのは「安打数」と「三振数」と「打席数」なので、それを入れる変数を用意しておく。

>>> import numpy as np
>>> h = pd.Series(dtype=np.int64)
>>> bb = pd.Series(dtype=np.int64)
>>> ab = pd.Series(dtype=np.int64)

あとは上記の変数に十年分のセ・パのデータを追加していく。

>>> import itertools
>>> import time
>>> for year, league in itertools.product(range(2006, 2017), ['p', 'c']):
...     url = 'http://npb.jp/bis/{year}/stats/bat_{league}.html'.format(year=year, league=league)  # スクレイピング対象 の URL
...     tables = pandas.read_html(url)  # テーブルを抽出する
...     df_with_header = tables[0]  # 成績のテーブルを取り出す
...     features = df_with_header[2:]  # ヘッダを捨てる
...     h = h.append(features[8])  # 安打数のデータを取り出す
...     bb = bb.append(features[21])  # 三振数のデータを取り出す
...     ab = ab.append(features[5])  # 打席数のデータを取り出す
...     time.sleep(1)  # リクエスト間で負荷をかけないように少し待つ
...

これで 636 人分の選手データが集まった。

>>> len(h)
636
>>> len(bb)
636
>>> len(ab)
636

安打と三振の相関係数を計算する

データが揃ったので、次は相関係数を計算してみる。

仮説として、安打をたくさん打つ選手ほど三振の数は少ないのではないか?と考えて両者の相関係数を調べてみよう。 この仮説が正しければ安打と三振の間には負の相関があるはずだ。

それでは numpy#corrcoef() を使って相関行列を計算させてみよう。

>>> np.corrcoef(h, bb)  # 安打数と三振数の相関行列を計算する
array([[ 1.        ,  0.06108444],
       [ 0.06108444,  1.        ]])

安打と三振の相関係数は 0.06 なので、ほとんど相関がないといえる。 これだけ見ると、仮説は間違っていたのだろうか?と思える。

偏相関係数を計算する

しかし、実際には第三の変数によって疑似無相関になっている可能性がある。

そんなときは第三の変数の影響を無くした相関係数として「偏相関係数」を計算してみよう。 次の数式が偏相関係数を求めるためのもの。

 r_{yz_x} = \frac{r_{yz} - r_{xy} r_{xz}}{\sqrt{1 - r_{xy}^2} \sqrt{1 - r_{xz}^2}}

ここで  r_{ab} は変数 ab の相関係数を表している。 上記は第三の変数 x の影響を取り除いた yz の偏相関係数を求める式になっている。

調べたところ、どうやら Python の既知のライブラリには偏相関係数を計算する実装がないようだ。 なので、ひとまず自前で関数を用意した。

>>> import math
>>> def partial_corrcoef(x, y, z):
...     """第三の変数 x の影響を除いた y と z の相関係数 (偏相関係数) を求める関数"""
...     correlation_matrix = np.corrcoef((x, y, z))
...     r_xy = correlation_matrix[0, 1]
...     r_xz = correlation_matrix[0, 2]
...     r_yz = correlation_matrix[1, 2]
...     r_yz_x = (r_yz - r_xy * r_xz) / (math.sqrt(1 - r_xy ** 2) * math.sqrt(1 - r_xz ** 2))  # noqa
...     return r_yz_x
...

上記の関数を使って「安打数」と「三振数」の相関係数から「打席数」の影響を取り除いてみよう。

>>> partial_corrcoef(ab, h, bb)  # 打席数の影響を取り除いた安打数と三振数の相関係数を計算する
-0.31105000209505901

結果は -0.311 と「弱い負の相関」があることが分かった。 どうやら打席数という第三の変数が影響することで擬似無相関になっていたらしい。

まあ、実際には第三の変数を見つけることが大変だから一通り偏相関係数を求めた上で相関係数との変動の大小を比べるのが良いのかな。

まとめ

  • 単純な相関係数では第三の変数の影響により疑似相関・疑似無相関になっている恐れがある
  • 第三の変数の影響を取り除くには偏相関係数を計算すれば良い