Up

Data Visualizationとグラフ描画

 

Spiegelhalter (2019)は、data visualization (dataviz)の概念の導入において、いくつかのグラフ描画を説明している。以下において、ビンの中のゼリービーンの個数の推測データ異性パートナーの人数調査病院ごとの手術成功率明白な関係があるが無相関の例人口増加の折れ線グラフ人口動態散布図の各グラフ描画のPythonスクリプト例を試みた。

Pythonスクリプトの作成においては、Spiegelhalter(2019), p. xviに記載されているウェブサイトからダウンロードしたファイルを参考・利用した。

 

 

瓶の中のゼリービーンの個数の推測データ

 

ビンの中のゼリービーンの個数を推測させることをSpiegelhalterらが行ったYou-Tubeでの調査では、915名による回答が得られ、推定個数値は、219個から31,337個であった。推定個数の分布を表すグラフを描画するスクリプトをリスト1のように作成した。実行すると図1のグラフが表示される。データファイル02-1-bean-data-full-x.csvは、Spiegelhalter(2019), p. xviに記載されているウェブサイトからダウンロードしたファイルに含まれていたものである。

図1

 

左側に素データのstrip-chart、箱図、ヒストグラムが表示され、右側に素データの対数変換値のグラフが表示されている。

 

 

 

リスト1 個数の推定数の分布

 

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sb

import numpy as np

 

#        ダウンロードしたファイルの読み込み

d_csv = pd.read_csv('02-1-bean-data-full-x.csv', header=None)

data = d_csv.values

x_ck = np.array([200, 500, 2000, 5000, 20000])

 

fig, ax = plt.subplots(nrows=3, ncols=2, sharex='col', figsize=(12,7))

fig.suptitle('Guess at number of beans in jar', fontsize=20)

 

sb.stripplot(data, orient='h', edgecolor='b', linewidth=1, alpha=0.5,

             ax=ax[0][0])

sb.stripplot(np.log10(data), orient='h',  edgecolor='b',

             linewidth=1, alpha=0.5, ax=ax[0][1])

 

ax[1][0].boxplot(data, vert=False)

ax[1][1].boxplot(np.log10(data), vert=False)

 

ax[2][0].hist(data, bins=20)

ax[2][0].set_xlabel('Raw data', fontsize=16)

 

ax[2][1].hist(np.log10(data), bins=20)

ax[2][1].set_xlabel('Logarithmic scale', fontsize=16)

ax[2][1].set_xticks(np.log10(x_ck), [f'{v:d}' for v in x_ck])

 

plt.tight_layout()

plt.show()

 

 

 

異性パートナーの人数調査

 

性生活についての調査(the National Sexual Attitudes and Lifestyle Survey: Natsal)の3回目のデータ(Natsal-3)における男女別の異性パートナーの数を棒グラフで表示するスクリプトをリスト2のように作成した。リスト2のスクリプトを実行すると、図2のグラフが表示される。データファイル02-4-sexual-partners-counts-x.csvは、Spiegelhalter(2019), p. xviに記載されているウェブサイトからダウンロードしたファイルに含まれていたものである。

図2

 

切りの良い数、10203040の比率が不連続に多いことを、Spiegelhalter(2019)は指摘している。

 

 

リスト2 異性パートナー数の分布

 

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sb

import numpy as np

 

#        ダウンロードしたファイルの読み込み

d_csv = pd.read_csv('02-4-sexual-partners-counts-x.csv')

data = d_csv.values

NumPartners = d_csv['NumPartners']

MenPercnt = d_csv['MenPercent']

WomenPercnt = d_csv['WomenPercent']

plt.xlim(0,50)

plt.bar(NumPartners, MenPercnt, width=0.4, align='edge',

        color='g')

plt.bar(NumPartners, WomenPercnt, width=-0.4, align='edge',

        color='r')

plt.ylabel('Percentage', fontsize=14)

plt.xlabel('Reported numberof lifetime opposite-sex partners',

           fontsize=14)

plt.text(25, 8, 'Men 35-44', c='g', fontsize=16)

plt.text(25, 10, 'Women 35-44', c='r', fontsize=16)

plt.tight_layout()

plt.show()

 

 

 

病院ごとの手術成功率

 

Spiegelhalter (2019), p.57には、病院ごとの手術数と30日生存確率の散布図が掲載されている。このグラフを描くスクリプトをリスト3のように作成してみた。リスト3のスクリプトを実行すると図3のグラフが表示される。データファイル02-5-child-heart-surgery-1991-x.csv02-4-sexual-partners-counts-x.csvは、Spiegelhalter(2019), p. xviに記載されているウェブサイトからダウンロードしたファイルに含まれていたものである。

図3

 

上のグラフが19911995のデータであり、下のグラフは20122015のものである。

 

 

リスト3 手術数と30日生存確率の散布図描画スクリプト

 

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sb

import numpy as np

 

#        ダウンロードしたファイルの読み込み

#         Survival in under-1s, 1991-1995

#

d_csv1991 = pd.read_csv('02-5-child-heart-surgery-1991-x.csv')

data1991 = d_csv1991.values

Hospitals = d_csv1991['Hospital']

Operations = d_csv1991['Operations']

ThirtyDay = d_csv1991['ThirtyDaySurvival']

 

plt.figure(figsize=(7,7))

plt.subplot(211)

for Hosp, Ope, Thirty in zip(Hospitals, Operations, ThirtyDay):

    plt.scatter(Ope, Thirty, label=Hosp)

plt.legend(bbox_to_anchor=(1.0, 1.0), fontsize=8)

plt.title('Survival in under-1s, 1991-1995')

plt.xlabel('Number of operations')

plt.ylabel('% 30-day survival')

plt.tight_layout()

 

#

#               ダウンロードしたファイルの読み込み

#              Survival for all children, 2012-2015

#

d_csv2012 = pd.read_csv('02-5-child-heart-surgery-2012-x.csv')

print(d_csv2012)

data2012 = d_csv2012.values

Hospitals = d_csv2012['Hospital']

Operations = d_csv2012['Operations']

ThirtyDay = d_csv2012['ThirtyDaySurvival']

 

plt.subplot(212)

for Hosp, Ope, Thirty in zip(Hospitals, Operations, ThirtyDay):

    plt.scatter(Ope, Thirty, label=Hosp)

plt.legend(bbox_to_anchor=(1.0, 1.0), fontsize=8)

plt.title('Survival for all children, 2012-2015')

plt.xlabel('Number of operations')

plt.ylabel('% 30-day survival')

plt.tight_layout()

 

plt.show()

 

 

 

明白な関係があるが無相関の例

 

Spiegelhalter (2019)p.59に、関係が見られるパターンであるが無相関である散布図が掲載されている。2変量の間に明瞭な逆U字型の関係があるが、相関係数が0であるパターンを描くスクリプトをリスト4のように作成してみた。リスト4のスクリプトを実行すると、図4のグラフが表示される。

図4

 

変数Yは、変数Xの逆U字型の関数として関係があるが、相関係数は0である。

 

 

リスト4 関係があるが無相関の例

 

import numpy as np

import matplotlib.pyplot as plt

 

x = np.linspace(1,100,100)

y = (x - np.mean(x))**2 * (-0.01)

y = 100 + y                        #    y = 100 - 0.01 * (x - x_mean)**2

 

r = np.corrcoef(x,y)

 

plt.title(f'r = {r[0,1]:.3f}', fontsize=24)

plt.plot(x, y, 'o', lw=0)

plt.xlabel('X', fontsize=16)

plt.ylabel('Y', fontsize=16)

plt.show()

 

 

 

人口増加の折れ線グラフ

 

人口増加を描く折れ線を素データに対するものと対数変換値に対するものを描くスクリプトを、リスト5およびリスト6に示すように作成してみた。

 

リスト5のスクリプトを実行すると図5のグラフが表示される。

図5

 

データ02-7-pop-past-future-2.7-necessities-mod.csvは、、Spiegelhalter(2019), p. xviに記載されているウェブサイトからダウンロードしたファイルに含まれていたファイル02-7-pop-past-future-2.7-necessities.csvにおいて、列変数名NorthernAmericaLatinAmericaandCarribeanN AmericanS Americanに変更したものである。

 

 

リスト6のスクリプトを実行すると、図6のグラフが表示される。

図6

 

リスト6におけるデータファイル02-7-pop-past-future million in 51.csvは、Spiegelhalter(2019), p. xviに記載されているウェブサイトからダウンロードしたファイルに含まれていたものである。

図6のグラフは、対数変換された値に対するグラフである。色付きの折れ線は、地域別の人口を表し、灰色の折れ線は国別の人口を表している。

 

 

 

リスト5 人口増加のグラフ(素データ)

 

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sb

import numpy as np

 

#

#        ダウンロードしたファイルの読み込み

#         地域別データ

#

d_csv_std = pd.read_csv('02-7-pop-past-future-2.7-necessities-mod.csv')

d_keys = d_csv_std.keys()

data = d_csv_std.values

names = d_keys[1:8]

data = data.T[:8]

for i in range(7):

    plt.plot(data[0], data[i+1], label=names[i])

plt.xlabel('Year')

plt.ylabel('Millions')

plt.legend(bbox_to_anchor=(1.0, -0.1), ncols=4)

plt.tight_layout()

plt.show()

 

 

 

リスト6 人口増加のグラフ(対数変換)

 

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sb

import numpy as np

#

#           ダウンロードしたデータの読み込み

#                    国別データ

#

d_csv_all = pd.read_csv('02-7-pop-past-future million in 51.csv')

data_all = d_csv_all.values

data_all = data_all.T

year_all = data_all[0]

data_all = np.log10(data_all[1:]/1000)          #   対数変換

for i in range(len(data_all)):

    plt.plot(year_all, data_all[i], c='k', alpha=0.1)

 

#

#        ダウンロードしたファイルの読み込み

#         地域別データ

#

d_csv_std = pd.read_csv('02-7-pop-past-future-2.7-necessities-mod.csv')

d_keys = d_csv_std.keys()

data = d_csv_std.values

names = d_keys[1:8]

data = data.T[:8]

year = data[0]

data = np.log10(data[1:8])                       #   対数変換

for i in range(7):

    plt.plot(year, data[i], lw=5, label=names[i])

plt.xlabel('Year')

plt.ylabel('Millions')

#

#           対数変換の目盛り用

#

pops = [1, 5, 10, 50, 100, 500, 1000, 5000, 10000]

log_pops = np.log10(pops)

plt.yticks(log_pops, pops)

 

plt.legend(bbox_to_anchor=(1.0, -0.1), ncols=4)

plt.tight_layout()

plt.show()

 

 

 

人口動態の散布図

 

人口動態の散布図を描くスクリプトをリストのように作成してみた。入力データファイル02-8-pop-past-future-x.xlsxは、Spiegelhalter(2019), p. xviに記載されているウェブサイトからダウンロードしたファイルに含まれていた02-8-pop-past-future-x.csvExcelのファイルに変換したものである。ダウンロードしたCSVファイルをpandas.read_csvで読み込もうとすると文字コードエラーが出たが、Excelファイルに変換するとpandas.read_excelで読み込めた。CSVファイルからExcelファイルへの変換は、CSVファイルをExcelで読み込んだ後、Excelファイルとして保存して行った。Excelファイルとしての保存は、ファイルの保存名のファイル拡張子として.xlsxを選ぶとExcelファイルとして保存される。

リストのスクリプトを実行すると図7のグラフが表示される。

図7

 

縦軸は、1951年と2015年の人口比であり、横軸が地域名である。横軸の値は、円が重ならないようにランダムなジッターが加えられている。

円の大きさは、1951年の人口に応じて設定されている。人口の多い国は大きい円で表されていて目立つようになっている。

 

 

 

リスト7 人口動態の散布図

 

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sb

import numpy as np

 

#

#        データファイルの読み込み

#

with pd.ExcelFile('02-8-pop-past-future-x.xlsx') as f_xlsx:

    data_xlsx = pd.read_excel(f_xlsx)

v_data_xlsx = data_xlsx.values           #   ndarray型に変換

Country = v_data_xlsx.T[1]               #   国(地域)名の取り出し

Country[62] = "Cote d'Ivoire"            #   国名データの修正

Pops = data_xlsx[1951].values / 1000     #    1951年の人口

 

#      2015年の人口の1951年の人口に対する比

Increase = data_xlsx[2015].values / data_xlsx[1951].values

#

#        Indexの値により国を地域に分類するときの参照に用いる配列

#

idx_lst = np.arange(0, 273+1)

idx_africa = np.concatenate((idx_lst[15:34+1],idx_lst[36:44+1],

                             idx_lst[46:58+1],idx_lst[60:76+1]))

idx_asia = np.concatenate((idx_lst[79:86+1], idx_lst[89:93+1],

                           idx_lst[95:103+1], idx_lst[105:115+1],

                           idx_lst[117:134+1]))

idx_europe = np.concatenate((idx_lst[137:146+1], idx_lst[148:160+1],

                             idx_lst[162:177+1], idx_lst[179:187+1]))

idx_northam = idx_lst[241:245+1]

idx_latinam = np.concatenate((idx_lst[190:215+1], idx_lst[217:224+1],

                              idx_lst[226:239+1]))

idx_oceania = np.concatenate((idx_lst[248:249+1], idx_lst[251:255+1],

                              idx_lst[257:263+1], idx_lst[265:273+1]))

Index = data_xlsx['Index'].values

Continent = np.zeros(273)      #     初期値0

for i in range(273):

    if np.count_nonzero(idx_africa - Index[i]) < len(idx_africa):

        Continent[i] = 1       #    Africa

    elif np.count_nonzero(idx_asia - Index[i]) < len(idx_asia):

        Continent[i] = 2       #    Asia

    elif np.count_nonzero(idx_europe - Index[i]) < len(idx_europe):

        Continent[i] = 3       #    Europe

    elif np.count_nonzero(idx_latinam - Index[i]) < len(idx_latinam):

        Continent[i] = 4       #    Latin America

    elif np.count_nonzero(idx_northam - Index[i]) < len(idx_northam):

        Continent[i] = 5       #    North America

    elif np.count_nonzero(idx_oceania - Index[i]) < len(idx_oceania):

        Continent[i] = 6       #    Oceania

 

idx_Classified = Continent > 0         #     Continentに値が割り振られたか

idx_Bigger = Pops > 1                  #     Popsの値が1より大きいか

#

#          上記2つの条件が満たされているか

#

idx_Keepers = np.logical_and(idx_Classified, idx_Bigger)

#

#          上記2つの条件を満たすデータを選ぶ

#

Kpt_Continent = Continent[idx_Keepers]

Kpt_Increase = Increase[idx_Keepers]

Kpt_Pops = Pops[idx_Keepers]

Kpt_Country = Country[idx_Keepers]

#

#       散布図における円の重なりを防ぐためのランダムジッター

#

Kpt_Continent += np.random.uniform(-0.3, 0.3, len(Kpt_Continent))

 

v_min = np.log10(np.min(Kpt_Pops))

v_max = np.log10(np.max(Kpt_Pops))

def s_marker(v):                   #    vの値に対応する円の大きさ

    v = np.log10(v)

    return 10 + 10 * (v - v_min) / (v_max - v_min)

#

#         散布図において名前を表示する国のリスト

#

Targets = {"Cote d'Ivoire", "Saudi Arabia", "Japan", "Georgia", "Albania",

           "Papua New Guinea"}

 

ContinentNames = ["Africa", "Asia", "Europe", "Latin\nAmerica",

                  "North\nAmerica", "Oceania"]

 

plt.figure(figsize=(9, 7))

for i in range(len(Kpt_Continent)):

    plt.plot(Kpt_Continent[i], Kpt_Increase[i], 'o', c='k',

             markersize=s_marker(Kpt_Pops[i]), fillstyle='none')

    if Kpt_Country[i] in Targets:

        plt.text(Kpt_Continent[i], Kpt_Increase[i], Kpt_Country[i],

                 ha='center', va='bottom', fontsize=12)

       

plt.plot([], 'o', c='k', markersize=s_marker(5), fillstyle='none', label='\n5')

plt.plot([], 'o', c='k', markersize=s_marker(10), fillstyle='none', label='\n10')

plt.plot([], 'o', c='k', markersize=s_marker(50), fillstyle='none', label='\n50')

plt.plot([], 'o', c='k', markersize=s_marker(100), fillstyle='none', label='\n100')

plt.legend(bbox_to_anchor=(1.15, 1), title='Population\n(millions)')

plt.xticks(np.arange(1, 6.1), ContinentNames, fontsize=16)

plt.yticks(fontsize=14)

plt.ylabel("Relative increase in population, 1951-2015", fontsize=16)

 

plt.tight_layout()

plt.show()

 

 

 

 

参考文献

岡本安晴(2019) いまさら聞けないPythonでデータ分析.丸善出版

Spiegelhalter, D. (2019). The art of statistics: Learning from data. Pelican

 

 

 

Home