pythonのpandasによる簡単な統計処理:第5回 グラフ作成と相関の検証

カテゴリー名: [pandasによる簡単な統計処理

2018/03/10

 身長と体重の間に相関がみられるかをチェックします。

 相関の検定を行う前に、散布図を描いて可視化します。

 当Webページで紹介するスクリプトや素材データ一式は、
pandas05.zip という圧縮ファイルに同梱しておきます。

 素材データは、「第1回」〜「第4回」と同じ
pt_source.xls です。

 「ID、性別、身長、体重」の4列からなる 400人分のデータです。

    


《このページの目次》


    

はじめに

 pythonをめぐる環境は、基本的に「第3回」までと同じですが、
グラフを描くための matplotlib というライブラリを加えます。

 また、Excelファイルに画像を挿入するため xlwt でなく
XlsxWriter を使います。

 xlwt でも bmpファイルなら挿入できるようですが、
pngファイルの方が何かと扱いやすいので XlsxWriter にします。

 「第4回」から間があいたのでバージョン番号が更新されていますが、
当サイトのスクリプトの実行は、旧バージョンでも大丈夫だとおもいます。

目次に戻る


1. 散布図の作成

 400人のデータのうち、身長と体重の両方ともそろっているのは 388人です。

 その 388人の抽出方法は前回取り上げました。

 相関の検定を行う前に、身長と体重の散布図を描いてみます。

    

(1) 英字表記でグラフを描く

 グラフのタイトルや軸名として日本語を盛り込むとなると
日本語フォントの指定という操作が必要になるので
まずは英字表記のグラフを作成します。

 import matplotlib.pyplot as plt としてインポートしている場合、
基本的には次のようにすると散布図のpngファイルを書き出すことができます。

 横軸に身長、縦軸に体重をとる形にしました。

plt.figure(figsize=(4.8, 3.6), dpi=100)  # 画像の大きさを指定
plt.scatter(dtf.height, dtf.weight)  # 散布図を描画
plt.title("Height & Weight Plot")  # タイトル
plt.xlabel("height")  # X軸の名前
plt.ylabel("weight")  # Y軸の名前
# plt.grid(True)  # grid線を引く
plt.savefig("pd01.png")  # 画像ファイルの書き出し

    

◇ 散布図の描写とタイトルなどの付加

 散布図を描くには scatter() を使います。

 引数は、X軸に配置するデータ(身長)と、Y軸に配置するデータ(体重)です。

 他にも指定できるオプションがいろいろありますが、
たいていはデフォルトで大丈夫ではないかとおもいます。

 画像のタイトルは plt.title()
X軸とY軸の名前は plt.xlabel()plt.ylabel() で付加します。

 グリッド線(格子線)は、今回 コメントアウトしています。

 各点の位置を把握するというよりは、
身長と体重のおおざっぱな関連を見るのが目的なので。

    

◇ 画像の大きさの設定

 figure() は画像の大きさなどを設定します。

 大きさを指定しないと、私のところでは
python2 だと 横幅8インチ×高さ6インチ
python3 では 4.8インチ×3.6インチになります。

 dpi(dots per inch)は、python2, python3 とも 100 です。

 作成したpngファイルの大きさをWindowsで確認すると、
800×600ピクセル、あるいは 640×480ピクセルと出てきます。
(画像ファイルのプロパティを見れば確認できます。)

 ピクセルは、厳密には違うのかもしれませんが、
通常、画像を描くためのドット(画素)を意味し、
画像全体が何個のドットから構成されているかを示すときに用います。

 1インチあたりのドット個数 dpi が 100 だと、
インチの値に 100 を掛け算するとピクセルの値になります。

 ちなみに、1インチ=約25.4ミリなので大きさをミリで示すと下のとおり。
(参考として、A5版などの近い用紙サイズも記します。)

 サンプルスクリプト pd01.py では、
plt.figure(figsize=(4.8, 3.6), dpi=100) として
横幅4.8インチ×高さ3.6インチ、dpi=100 にしています。

    

◇ 画像ファイルの保存形式

 plt.savefig() がサポートする保存形式は、
png, pdf, svg, ps, eps です。
(png, pdf, …… はファイル拡張子として使われます。)

 上記のうち、XlsxWriter で挿入できるのは png だけのようです。

    

◇ Excelファイルへの画像の挿入

 XlsxWriter で画像ファイルを挿入するには次のようにします。

 A1セルに「身長と体重の散布図」と書き込み、
A2セル以降に pd01.png という画像ファイルを貼り付けています。

workbook = xlsxwriter.Workbook('pd01.xlsx')
worksheet = workbook.add_worksheet()
worksheet.write('A1', u'身長と体重の散布図')
worksheet.insert_image('A2', 'pd01.png')
workbook.close()

目次に戻る


(2) 日本語表記でグラフを描く

 画像のタイトルや軸の名前を日本語にします。

 そのためには日本語フォントを用意して、
そのフォントを使う旨をpythonに知らせる必要があります。

 Windowsには当然ながら日本語フォントが用意されていますが、
バージョンによってフォントファイルの名前が違ったりするようなので
ここではIPA(独立行政法人 情報処理推進機構)のフォントを使います。

    

◇ IPAexフォントのダウンロード

IPAexフォントのダウンロード から
IPAexfont00201.zip をダウンロードします。

 解凍すると IPAexfont00201 というフォルダができて
その下に ipaexg.ttf, ipaexm.ttf などのファイルがあります。

 このフォルダがカレントディレクトリまたは
C:/Users/Public の下にあるとの前提で話を進めます。

    

◇ フォントファイル(ttfファイル)の指定

 次の2行でフォントファイルの指定ができます。

from matplotlib.font_manager import FontProperties
fp = FontProperties(fname='./IPAexfont00201/ipaexg.ttf')

 上の2行をスクリプトの前半の方に記述します。

 ipaexg.ttf はゴシック、ipaexm.ttf なら明朝です。

    

◇ タイトルや軸名の書き込み

 タイトルおよび軸名を次ぎのように設定します。

plt.title(u"身長と体重の散布図", fontproperties=fp)
plt.xlabel(u"身長", fontproperties=fp)
plt.ylabel(u"体重", fontproperties=fp)

    

◇ サンプルスクリプト pd02.py の全体像

 この辺で pd02.py を掲げておきます。

 1# pd02.py (coding: cp932)
 2import os, sys
 3import pandas as pd
 4import matplotlib.pyplot as plt
 5import xlsxwriter
 6
 7  # IPAフォントの存在確認とFontPropertiesの設定
 8ttf_list = ['./IPAexfont00201/ipaexg.ttf',
 9  'C:/Users/Public/IPAexfont00201/ipaexg.ttf']
10ttf_file = ''
11for fpath in ttf_list:
12    if os.path.isfile(fpath) == True:
13        ttf_file = fpath
14        break
15if ttf_file == '':
16    sys.stderr.write("IPAのフォントがみつかりません.\n")
17    sys.exit(1)
18from matplotlib.font_manager import FontProperties
19fp = FontProperties(fname=ttf_file)
20
21from platform import python_version
22if int(python_version()[0]) < 3:
23    reload(sys)
24    sys.setdefaultencoding('cp932')  # デフォルト文字コードを変更
25
26xls_file = "pt_source.xls"
27dtf = pd.read_excel(xls_file)
28dtf = dtf[dtf[u'身長'].notnull() & dtf[u'体重'].notnull()]
29
30image_file = "pd02.png"
31plt.figure(figsize=(4.8, 3.6), dpi=100)
32plt.scatter(dtf[u'身長'], dtf[u'体重'])
33plt.title(u"身長と体重の散布図", fontproperties=fp)
34plt.xlabel(u"身長", fontproperties=fp)
35plt.ylabel(u"体重", fontproperties=fp)
36# plt.grid(True)
37plt.savefig(image_file)
38
39workbook = xlsxwriter.Workbook('pd02.xlsx')
40worksheet = workbook.add_worksheet()
41worksheet.insert_image('A2', image_file, {'x_scale': 1, 'y_scale': 1})
42workbook.close()

    

 worksheet.insert_image() のところに出てくる {'x_scale': 1, 'y_scale': 1} は、
画像の拡大・縮小の倍率の指定です。

 指定を省略すると 1倍(拡大も縮小もしない)になります。

 {'x_scale': 0.5, 'y_scale': 0.5} のように指定します。

目次に戻る


2. 相関の検証

 身長が高いほど体重が重くなるかという相関を見ます。

 二つの事柄の関連性の強弱を示すのが相関係数ですが、
主に次の三つがあります。

 単に「相関係数」といえばピアソンの相関係数のことのようです。

 身長や体重のような数値データについて、その値の関係を検証します。

 それに対して、スピアマンの係数とケンドールの係数は、
数値そのものではなく順位に着目して関係性を見ます。

 身長順で順位の高い人は、体重順の順位も高いかどうかを見ます。

    

(1) pythonにおける三つの相関係数の取得

 python の scipy というライブラリにはピアソン、スピアマン、ケンドールの
各相関係数を得るためのメソッドがあります。

 以下、import scipy.stats as stats を予め記述しておくことを前提にします。

◇ ピアソンの積率相関係数

 ここで取り上げている素材データでは、身長は正規分布に即していますが、
体重は違います。

 なので、厳密にはピアソンの方法を使うべきではないのかもしれません。

 ですが、参考まで試してみます。

 ピアソンの相関係数は、二つのデータセットの
線形(linear)の関連性を示す尺度です。

 片方が増加すれば、もう一方も同じ割合だけ増加する(比例する)のが
線形の特徴です。

 値として -1〜1 の範囲になり、0 は線形関係なしを意味します。

 -1 なら逆比例の関係、1 だと正比例の関係です。

 次のように実行します。

r, p_value = stats.pearsonr(height, weight)

 変数 r に相関係数が代入され、
p_value に有意確率(p値)が入ります。

 p_value < 0.05 であれば、「相関係数が0である」(帰無仮説)を棄却できます。

 つまり、「相関が認められる」といえます。

 今回の素材データで身長と体重の相関を見ると

 p_value がとても小さな値なので、相関が認められるといえます。

 相関係数がそれほど高い訳ではありませんが、
身長が大きいほど体重も大きいという正比例に似た関係といえます。

    

◇ スピアマンの相関係数

 スピアマンの相関係数は、二つのデータセットの
単調性(monotonicity)の関連性を示す尺度です。

 データセットが正規分布に即しているかどうかは問いません。

 この相関係数(ρ, rhoと表記)は、順位に着目して計算され、
ピアソンの係数と違って比例関係(同じ割合の増減)を想定しません。

 単に「一方が増減すれば、連動して他方も増減する」の関係を見ます。

 値として -1〜1 の範囲になり、0 は単調な関係なしを意味します。

 次のように実行します。

rho, p_value = stats.spearmanr(height, weight)

 今回の素材データでは下のような結果になります。

 相関が認められます。

    

◇ ケンドールの相関係数

 ケンドールの相関係数は、二組の順位データの
対応関係(correspondence)を示す尺度です。

 データが正規分布に即しているかどうかは問いません。

 この相関係数(τ, tauと表記)は、順位の一致度に着目して計算されます。

 順位が完全に一致しているなら 1 になり、
順位が完全に一致していない場合は -1 になります。

 相関がない(互いに独立)なら 0 です。

 次のように実行します。

tau, p_value = stats.kendalltau(height, weight)

 今回の素材データでは下のような結果になります。

 係数は比較的低い値ですが、相関が認められます。

 なお、kendalltau() では initial_lexsort というオプションを指定できます。

 順位を定めるためのソートの方法を指定するもので、
initial_lexsort=True なら lexicographical order sort が採用され、
initial_lexsort=False だと quick sort になります。

 quick sort は、比較的小規模なデータを
迅速に整列させることができるようです。

目次に戻る


(2) サンプルスクリプト pd03.py

 三つの相関係数を取得して、Excelファイルに書き出します。

 また、散布図も挿入します。

 日本語IPAのフォントを使います。

 以下、pd03.py を掲げます。

 1# pd03.py (coding: cp932)
 2import os, sys
 3import pandas as pd
 4import numpy as np
 5import scipy.stats as stats
 6import matplotlib.pyplot as plt
 7import xlsxwriter
 8
 9  # IPAフォントの存在確認とFontPropertiesの設定
10ttf_list = ['./IPAexfont00201/ipaexg.ttf',
11  'C:/Users/Public/IPAexfont00201/ipaexg.ttf']
12ttf_file = ''
13for fpath in ttf_list:
14    if os.path.isfile(fpath) == True:
15        ttf_file = fpath
16        break
17if ttf_file == '':
18    sys.stderr.write("IPAのフォントがみつかりません.\n")
19    sys.exit(1)
20from matplotlib.font_manager import FontProperties
21fp = FontProperties(fname=ttf_file)
22
23from platform import python_version
24if int(python_version()[0]) < 3:
25    reload(sys)
26    sys.setdefaultencoding('cp932')  # デフォルト文字コードを変更
27
28xls_file = "pt_source.xls"
29dtf = pd.read_excel(xls_file)
30asc_col = ['ID', 'gender', 'height', 'weight']  # 英字の列ラベル
31dtf.columns = asc_col
32dtf = dtf[dtf.height.notnull() & dtf.weight.notnull()]
33
34  # 3種類の相関係数を取得
35p = stats.pearsonr(dtf.height, dtf.weight)
36s = stats.spearmanr(dtf.height, dtf.weight)
37k = stats.kendalltau(dtf.height, dtf.weight)
38dtf2 = pd.DataFrame([p, s, k])
39dtf2.columns = [u'相関係数', 'p-value']
40dtf2.index = ['pearson', 'spearman', 'kendall']
41
42  # 散布図の作成(pngファイル)
43image_file = "pd03.png"
44plt.figure(figsize=(4.8, 3.6), dpi=100)
45plt.scatter(dtf.height, dtf.weight)
46plt.title(u"身長と体重の散布図", fontproperties=fp)
47plt.xlabel(u"身長", fontproperties=fp)
48plt.ylabel(u"体重", fontproperties=fp)
49plt.savefig(image_file)
50
51writer = pd.ExcelWriter('pd03.xlsx', engine='xlsxwriter')
52dtf2.to_excel(writer, sheet_name='Sheet1')
53worksheet = writer.sheets['Sheet1']
54worksheet.insert_image(len(dtf2)+2, 0, image_file)
55writer.save()

    

 今回は これで終了です。

Copyright (C) T. Yoshiizumi, 2017 All rights reserved.