統計的因果探索について #1:相関をみてみる
はじめに
データ分析を行うとき、対象データをグラフに書きまくる、さらに変数間の相関をみることはとても当たり前に行うと思います。 でも、相関が高いからといって軽々と変数間が影響しあっていると考えることは時期尚早です。 これはデータを生成している事象の因果関係をしっかり捉えないと間違えてしまうので、意外に注意が必要なわけです。 以下の記事は、自分なりのメモとしてまとめることを第一にしているので、わかりにくい点や間違いがあるかもしれません。 メインの参考文献はMLPシリーズの「統計的因果探索」1です。 まずは基本中の基本である、データの相関について手を動かしながら見ていきます。
Hands on
相関
相関とはあるデータに対して、一つの特徴量を説明変数、また他の特徴量を目的変数としたとき、 これらの変数どうしが互いに関わり合うように変化しているのか?を調べるものです。 として正の相関があれば、が増加するとも増加する傾向がある。 逆にとして負の相関があれば、が増加するとは減少する傾向があることになります。 しかし、相関があるからといって、互いの変数が直接影響しあっているといえるわけではなく、 データ分析で簡単に求められる値ですが奥が深いものです。
データセットの特徴をつかむ
以下では例を示しながら進めたほうが楽しいので、pythonのseabornを利用することで簡単に利用できるtitanicのデータを見ながらすすめることにします。 kaggle2でも利用されているので、seabornを利用しなくても入手できるものです。
import seaborn as sns dataset_name = sns.get_dataset_names() dataset = sns.load_dataset(dataset_name[-1])
titanicデータセットを覗いてみると次のようなカラムから構成されています。 どうやら意味が類似するカラムがあるためか、kaggleのcsvとは少しだけ異なるみたいです。
dataset.head()
各カラムの意味は次の通りで、typeはdataset.ftypesで参照したもの。 columnに取り消し線を引いているものは今回の解析では使わないため、delして扱います。
column | type | description |
---|---|---|
survived | int64 | 生存者であるか |
pclass | int64 | チケットのクラス |
sex | object | 性別 |
age | float64 | 年齢 |
sibsp | int64 | 同乗した兄弟/配偶者の数 |
parch | int64 | 同乗した親/子供の数 |
fare | float64 | 運賃 |
object | 出港した港の名前 | |
category | チケットのクラス、Fisrt/Second/Third | |
object | sexがmaleならman, femaleならwoman | |
bool | sexがmaleならTrueだと思う | |
category | 客室番号?A~Gまである | |
embark_town | object | 出港した街、{nan, 'Southampton', 'Queenstown', 'Cherbourg'}のいずれか |
object | 生きているかどうか | |
bool | ぼっちかどうか、sibspが0かつparchが0ならばTrue |
このデータをいくつかグラフ化してみます。 ただし注意が必要な点は現実データと同様にtitanicデータにも欠損値が存在しており、ageなどが抜けているところがあります。 欠損値を補完する方法を考え始めると一大テーマになってしまうので、pandas.DataFrame.dropna()メソッドを使いNaN(欠損値)が含まれるデータは捨ててしまうことにしました。 なぜ注意が必要なのかというと、一部のカラムが欠損しているかといってそのサンプルを捨てると、データセットの傾向を歪ませる可能性があるからです。 例えば、欠損値を含むデータセットのまま基本統計量を見ると、
dataset.describe()
となりますが、欠損値を含むサンプルを捨ててしまうことにすると、
dataset.dropna().describe()
となります。 見比べると明らかですが、データセットの全体のサンプル数は、891から182に激減してしまい、 平均Ageは29.699118から35.623187へ、 平均Fareは32.204208から78.919735と倍以上の値になってしまいました。 Fareの標準偏差1は49.693429から76.490774となり、幅広く分布している可能性がわかります。
年齢Ageが上がれば稼ぎも増える気がするので運賃Fareも高くなるかも?という仮説を立てて、この2変数間の関係を見てることにしました。 欠損値をdropしてからAgeとFareのプロットを行い、Sexによって色分けをおこなってみます。 sns.pairplot()で行うと縦軸の表記がおかしかったため、それぞれを別個に作図しました。
dataset_wona = dataset.dropna() g = sns.FacetGrid(pd.DataFrame([dataset_wona.age, dataset_wona.sex]).T, hue='sex') g = g.map(sns.distplot, 'age') plt.legend() plt.show()
g = sns.FacetGrid(pd.DataFrame([dataset_wona.fare, dataset_wona.sex]).T, hue='sex') g = g.map(sns.distplot, 'fare') plt.legend() plt.show()
sns.scatterplot(x=dataset_wona.fare, y=dataset_wona.age, hue=dataset_wona.sex) plt.show()
欠損を含むサンプルを除去したデータであること念頭にいれつつ結果を眺めると、 性別のヒストグラムからほぼ同等の比率でタイタニック号に乗船していたことがわかります。 若干Femaleのほうが若いことから当時の外国人男性は自身の年齢よりも年下の女性を好んでいたのかもしれません。 一方で、Fareのヒストグラムからは一部に富裕層の影が見て取れますが、多くの乗客は同等の運賃を払っているようです。 ただし運賃だけでなく出港地も鑑みないと、これも富裕層であるのか、はたまた他の人より長距離乗船しているだけなのかわかりません。 データ分析をおこなう上では、非観測変数の存在を考えて、あらゆる可能性を踏まえておくことが重要です。 さて、AgeとFareの散布図を見てみるとこの2変数のデータに相関はないように見えます。 実際に、AgeとFareの相関値は-0.0907と非常に相関値が低く、ほぼ無相関と言えます。
dataset_wona.corr()['fare']['age']
見るからに相関もないので無駄かもしれませんが、説明変数をFare、目的変数をAgeとして線形回帰モデルをフィットさせて、回帰係数と決定係数の様子を確認します。 回帰係数は線形回帰を行うときの傾きであり、これは説明変数の重みを意味することになります。 決定係数は予測式(ここでは線形回帰)がどれだけデータにうまくフィットしているのかを示し、大きいほど当てはまりがよく、小さいほど悪い。 なお決定係数と相関係数の関係はこちらのサイト3にわかりやすくまとめられています。
from sklearn import linear_model clf = linear_model.LinearRegression(normalize=True) x1 = dataset_wona.fare.values.reshape(-1, 1) x2 = dataset_wona.age.values.reshape(-1, 1) clf.fit(x1, x2) print(clf.coef_) # 回帰係数 print(clf.score(x1, x2)) # 決定係数
回帰係数は-0.01858906、決定係数は0.00823となりました。 つまり説明変数としたFareはあまりAgeを説明できているとはいえず、 線形回帰モデルも適していないことが言えます。 検定を行うことで有意に相関がないことを示す必要もありますが、思いの外長くなってしまったので、 今回は"年齢Ageが上がれば稼ぎも増える気がするので運賃Fareも高くなるかも?という仮説"は誤っていそう、というところで止めにしておきます。 しかし、例えばですが、年齢が上がると、稼ぎも増えるかもしれませんが、遠出することが億劫になり運賃は大して上がらないかもしれません。 次回は多変数間の因果関係にまで踏み込んで見たいと思います。
まとめ
今回はtitanicデータセットを使い簡単なデータ分析を行いました。 相関はかんたんな値ですが、事象の因果関係や非観測変数の存在を加味しながら眺めなければいけません。 次回は、擬似相関や多変数間の因果関係の話を考えてみます。