多変量データ解析
Contents
4. 多変量データ解析¶
4.1. はじめに¶
4.1.1. 開始前に行うこと(重要)¶
まず,Chrome 等のブラウザを利用して,本教材の URL にアクセスし,画面の指示に従い Google アカウントにログインしてください.その後,ブラウザ表示画面左上の「ファイル」をクリックし,さらにポップアップで出てくる項目から「ドライブにコピーを保存」をクリックしてください.これにより,本教材のコピーが自分のGoogleドライブに保存され,自分で書いたプログラムを実行できるようになります.
また,多くのセルが非表示になっていると思われるため,同じくブラウザ表示画面上の「表示」をクリックし,さらにその項目から「セクションを展開」をクリックし,非表示のセルを表示させてください.
Hint
各ページの上部にロケットのアイコン があるのでこれをクリックして各ページのファイルを Google Colaboratory 上で開いて利用してください.
4.1.2. 到達目標¶
pandas を利用する方法を身に着けることを目標とします.
4.1.3. pandas とは¶
pandas は Python からデータを簡単に扱うことができるようになるライブラリです.R というプログラミング言語かつ環境のデータフレームというものと類似しています.特に,行列形式に成型されたデータの扱いが得意です.
4.1.4. pandas のインポート¶
pandas は以下のようにインポートします.pandas には pd という略称を与えて利用するのが普通です.その都度 pandas とタイピングするのが面倒だからです.
import pandas as pd
一緒に利用する NumPy もインポートしておきましょう.
import numpy as np
4.1.5. 学習の進め方¶
上から順に読み進み,【実習】と書いてある箇所でコードを書いて実行してください.基本的に入力するコードの前に見本のコードが書かれており,見本と同じコードを書けば正しく動作するようになっています(見本コードの上下に入っている区切り線は入力する必要ありません).理解を深めるには,見本とは異なるコードを書いて試してみることも役立つと思います. なお, Colab では,コードが書かれている領域をコードセル,説明文等が書かれている領域をテキストセルといいます.
4.2. pandas の基本操作¶
4.2.1. 整然データ¶
1つのサンプルが複数の変数を持つ時の統計を扱う
以下のような形式のデータを整然データ(tidy data)と呼ぶ.
1つの行は1つのサンプルを表す
1つの列は1つの変数を表す
整然データの例(ある高校の生徒数)
学年 |
学科 |
生徒数 |
|---|---|---|
1年 |
普通科 |
121 |
1年 |
理数科 |
40 |
2年 |
普通科 |
119 |
2年 |
理数科 |
39 |
3年 |
普通科 |
120 |
3年 |
理数科 |
41 |
整然データでない例
生徒数 |
普通科 |
理数科 |
|---|---|---|
1年 |
121 |
40 |
2年 |
119 |
39 |
3年 |
120 |
41 |
データが整然データの形式になっていると解析がしやすくなる
整然データの形式になっていないデータは,まず整然データの形式に変換してから解析する
整然データ形式でないデータ形式で保存されるデータを雑然データ(messy data)と呼ぶ
4.2.2. 多変量データ管理¶
整然データを扱う際はpandasパッケージを使うのが便利
Dataframeと呼ばれるデータ構造に解析したいデータを読み込んで使用する
pandasでは2次元データはDataframe,1次元データはSeriesというデータ構造を使用する
日本の5都市の2018年1月と8月の気象データを解析してみる
気象庁 | 過去の気象データ
都市名 |
月 |
日照時間(時間) |
降水量(mm) |
日最低気温0℃未満日数(日) |
日最高気温30℃以上日数(日) |
最高気温(℃) |
最低気温(℃) |
|---|---|---|---|---|---|---|---|
八戸 |
1月 |
141.3 |
14.0 |
28 |
0 |
9.3 |
-9.6 |
八戸 |
8月 |
146.6 |
239.5 |
0 |
6 |
34.0 |
13.0 |
仙台 |
1月 |
158.4 |
50.0 |
26 |
0 |
12.2 |
-6.7 |
仙台 |
8月 |
161.8 |
272.5 |
0 |
14 |
37.3 |
15.7 |
東京 |
1月 |
206.1 |
48.5 |
13 |
0 |
16.0 |
-4.0 |
東京 |
8月 |
217.4 |
86.5 |
0 |
25 |
37.3 |
18.3 |
大阪 |
1月 |
172.7 |
51.5 |
8 |
0 |
14.9 |
-2.5 |
大阪 |
8月 |
260.7 |
41.5 |
0 |
31 |
37.6 |
19.9 |
那覇 |
1月 |
74.9 |
150.5 |
0 |
0 |
24.1 |
9.3 |
那覇 |
8月 |
203.4 |
310 |
0 |
27 |
32.4 |
24.2 |
【実習】5都市の気象データをデータフレームに格納する(ソースをコピペして良い).
Hint
列ごとにデータを用意し最後に結合します.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
name = ["八戸", "八戸", "仙台", "仙台", "東京", "東京", "大阪", "大阪", "那覇", "那覇"]
month = ["1月", "8月", "1月", "8月", "1月", "8月", "1月", "8月", "1月", "8月"]
sunshine = [141.3, 146.6, 158.4, 161.8, 206.1, 217.4, 172.7, 260.7, 74.9, 203.4]
rainfall = [14.0, 239.5, 50.0, 272.5, 48.5, 86.5, 51.5, 41.5, 150.5, 310]
under0 = [28, 0, 26, 0, 13, 0, 8, 0, 0, 0]
over30 = [0, 6, 0, 14, 0, 25, 0, 31, 0, 27]
max_temp = [9.3, 34.0, 12.2, 37.3, 16.0, 37.3, 14.9, 37.6, 24.1, 32.4]
min_temp = [-9.6, 13.0, -6.7, 15.7, -4.0, 18.3, -2.5, 19.9, 9.3, 24.2]
df = pd.DataFrame(
{"name" : name, "month" : month, "sunshine" : sunshine, "rainfall" : rainfall,
"under0" : under0, "over30" : over30, "max_temp" : max_temp, "min_temp" : min_temp})
df
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】気象データの先頭だけ表示する.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.head()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】気象データの先頭3行だけ表示する.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.head(3)
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】気象データの末尾だけ表示する.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.tail()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】データフレームの概要を確認する.
Note
行数,列数,各列の型,欠損値の数がわかります.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.info()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
4.2.3. データの抽出処理¶
詳細はpandasドキュメント参照
特定の列を取り出す際は
df[列ラベル]とする
【実習】気象データから最高気温の列を取り出す.
Note
この方法で取り出すとpandasのSeriesデータ(1次元用のデータ構造)として取り出されます.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df["max_temp"]
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】気象データから最高気温の列をNumPyアレイ(ndarray)として取り出す.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
print(type(df["max_temp"].values)) # 動作確認用
df["max_temp"].values
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
Hint
複数の列をデータフレームとして取り出す際はdf[列ラベルのリスト]とします.
【実習】気象データから最高気温と最低気温の列を取り出す.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df[["max_temp", "min_temp"]]
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
Note
Dataframeから行と列を指定してデータを取り出すにはdf.loc[行ラベル,列ラベル]とします.ラベルの代わりにラベルのリストを使っても良いです.スライスも使えますが通常のスライスとは仕様が異なるので注意(「開始ラベル : 最終ラベル」と指定すると開始ラベルから最終ラベルまで(終了ラベル含む)が選択される).
【実習】気象データから行ラベル1,列ラベルnameの要素を取り出す.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.loc[1,"name"]
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
Hint
行全体を取り出すには列の指定を「:」にします.
【実習】行ラベルが1の行を取り出す.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.loc[1,:]
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】行ラベルが1と3の行を取り出す.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.loc[[1,3],:]
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
Note
指定条件を満たす行のみを取り出すことができます.正確には,Dataframeに論理値からなるリストを渡すことで値がTrueの行のみを取り出すことができます.
【実習】nameが八戸の行のみを取り出す.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
print(df["name"] == "八戸") # 動作確認用
df[df["name"] == "八戸"]
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】最低気温がマイナスになっている行のみを取り出す.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df[df["min_temp"] < 0]
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
4.2.4. 各列に対する統計量の計算¶
pandas でも NumPy と同様の統計量計算関数が用意されています.標準の関数名を使うと,デフォルトで欠損値が無視されるので注意が必要です.
【実習】データフレームの各列について平均をとる.
Hint
NumPyと同名の関数meanが使えます.文字データ等の平均は算出されません.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.mean()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】データフレームの各列について各種統計量を計算する.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.describe()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
4.2.5. グループ別の統計量¶
上記において,1月と8月は分けて解析したい場合があります.そのようなとき,特定条件(今回は月)でデータをグループ化することができます.
【実習】気象データを月ごとにグループ化し,グループ一覧を表示する.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
g = df.groupby("month")
g.groups
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】グループ数をチェックする.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
len(g.groups)
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】グループ化された気象データから1月のデータのみを取り出す.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
g.get_group("1月")
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】月ごとに各列の平均値を求める.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
g.mean(numeric_only=True)
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】月ごとに各列の各種統計量を計算する.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
g.describe()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
4.2.6. 共分散と分散共分散行列¶
二つの変数の関係性をみるために使われる統計量で,ざっくりとは下記の値をとります.
正の値:片方の値が大きくなるともう片方も大きくなる傾向
負の値:片方の値が大きくなるともう片方は小さくなる傾向
0: 変数同士に関係性が見られない
分散と同様,値の大きさについては解釈しづらいです.
変数\(\boldsymbol{x}=\begin{bmatrix} x_1 \\ \vdots \\ x_N \end{bmatrix}\)と変数\(\boldsymbol{y}=\begin{bmatrix} y_1 \\ \vdots \\ y_N \end{bmatrix}\)の共分散は下記の式で定義される
\(\mathrm{Cov}(\boldsymbol{x}, \boldsymbol{y}) = \frac{1}{N}\sum_{i=1}^N (x_i - \mu_x)(y_i - \mu_y)\)
\(\mathrm{Cov}(\boldsymbol{x},\boldsymbol{y})\): \(\boldsymbol{x}\)と\(\boldsymbol{y}\)の共分散
\(\mu_x\): 変数\(\boldsymbol{x}\)の平均値
\(\mu_y\): 変数\(\boldsymbol{y}\)の平均値
分散共分散行列:以下のように分散と共分散を一つの行列で表したもの
以下,3種類のデータ\(x,y,z\)の分散共分散行列 $\(\mathrm{Cov}=\begin{bmatrix} \sigma_x^2 & \mathrm{Cov}(x,y) & \mathrm{Cov}(x,z) \\ \mathrm{Cov}(x,y) & \sigma_y^2 & \mathrm{Cov}(y,z) \\ \mathrm{Cov}(x,z) & \mathrm{Cov}(y,z) & \sigma_z^2\\ \end{bmatrix} \)$
記号
\(\mathrm{Cov}\): 分散共分散行列
\(\sigma_x^2\): 変数\(x\)の分散
\(\mathrm{Cov}(x,y)\): 変数\(x\)と変数\(y\)の共分散
【実習】共分散を図解する(コードをコピペして実行すること).
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
mean = np.array([0, 0]) # 平均を指定。
cor = [-1, -0.8, 0, 0.8, 1]
plt.figure(figsize=(16, 3))
plt.axes().set_aspect('equal')
for i in range(len(cor)):
cov = np.array([[1, cor[i]], [cor[i], 1]])
x, y = np.random.multivariate_normal(mean, cov, 2000).T
plt.subplot(1, 5, i+1)
plt.title("Negative" if i == 0 else "0" if i == 2 else "Positive" if i == 4 else "")
plt.plot(x, y, 'x')
plt.axis("equal")
plt.grid(which="major")
plt.show()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
Note
次の講義で紹介する matplotlib を先取りで使ってみました.
4.2.7. 相関係数と相関行列¶
共分散を最大値1,最小値-1になるように標準化したもの
正の値:2つの変数に正の相関がある(最大値1)
負の値:2つの変数に負の相関がある(最小値-1)
0: 変数間に相関がない
変数\(\boldsymbol{x}=\begin{bmatrix} x_1 \\ \vdots \\ x_N \end{bmatrix}\)と変数\(\boldsymbol{y}=\begin{bmatrix} y_1 \\ \vdots \\ y_N \end{bmatrix}\)の相関係数は下記の式で定義される
\(\rho_{\boldsymbol{x}\boldsymbol{y}} = \frac{\mathrm{Cov}(\boldsymbol{x}, \boldsymbol{y})}{\sigma_x^2 \sigma_y^2}\)
\(\rho_{\boldsymbol{x}\boldsymbol{y}}\): \(\boldsymbol{x}\)と\(\boldsymbol{y}\)の相関係数
\(\mathrm{Cov}(\boldsymbol{x},\boldsymbol{y})\): \(\boldsymbol{x}\)と\(\boldsymbol{y}\)の共分散
\(\sigma_x^2\): 変数\(\boldsymbol{x}\)の分散
\(\sigma_y^2\): 変数\(\boldsymbol{y}\)の分散
他の定義もあるが上記が一般的(この定義はピアソンの積率相関係数と呼ばれる)
2変数の間に何らかに関係性があったとしても,それが直線的な関係でない時は相関係数が0になってしまうことがあるので注意
相関係数行列:以下のように複数変数の相関係数を一つの行列で表したもの
以下,3種類のデータ\(\boldsymbol{x},\boldsymbol{y},\boldsymbol{z}\)の相関行列 $\(\mathrm{C}=\begin{bmatrix} 1 & \rho_{xy} & \rho_{xz} \\ \rho_{xy} & 1 & \rho_{yz} \\ \rho_{xz} & \rho_{yz} & 1 \\ \end{bmatrix} \)$
記号
\(\mathrm{C}\): 相関行列
\(\rho_{xy}\): 変数\(\boldsymbol{x}\)と変数\(\boldsymbol{y}\)の相関係数
【実習】相関係数を図解する(コードをコピペして実行すること).
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
mean = np.array([0, 0])
cor = [-2, -1.5, 0, 1.5, 2]
plt.figure(figsize=(16, 3))
plt.axes().set_aspect('equal')
for i in range(len(cor)):
cov = np.array([[1, cor[i]], [cor[i], 4]])
x, y = np.random.multivariate_normal(mean, cov, 2000).T
plt.subplot(1, 5, i+1)
plt.title("Correlation coef.: " + str(round(cor[i]/2, 2)))
plt.plot(x, y, 'x')
plt.axis("equal")
plt.grid(which="major")
plt.show()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】気象データの相関行列を計算する.
Hint
プログラム実行前にどの変数間の相関が強そうか予想してみましょう.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
df.corr(numeric_only=True)
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
【実習】最高気温と最低気温で散布図を書く.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
import matplotlib.pyplot as plt # 実行済みなら不要
%matplotlib inline
plt.scatter(df["max_temp"], df["min_temp"])
plt.xlabel("max temp")
plt.ylabel("min temp")
plt.show()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
Hint
相関が大きいことを確認しましょう.
【実習】日照時間と降水量で散布図を書く.
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
plt.scatter(df["sunshine"], df["rainfall"])
plt.xlabel("sunshine")
plt.ylabel("rainfall")
plt.show()
#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#
Hint
相関が小さいことを確認しましょう.
Note
終わりです.