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.Dataframe

    • pandasでは2次元データはDataframe,1次元データはSeriesというデータ構造を使用する

都市名

月

日照時間(時間)

降水量(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. データの抽出処理

【実習】気象データから最高気温の列を取り出す.

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

終わりです.