多くの教科書ではmatplotlibの基本的な使用法を解説しているが,ここではpandasから直接描画する方法と,もっと綺麗に描画するためのパッケージ seaborn について述べる.

matplotlib https://matplotlib.org/ は, PythonならびにNumPyのためのグラフ描画ライブラリである.

最初の例題として用いるのはiris(アヤメ)のデータである.

まずはpandasを用いてデータを読んでおく.

%matplotlib inline

としておくと,描画が画面にすぐに表示されるようになる.

import pandas as pd
%matplotlib inline

df = pd.read_csv(
    "http://logopt.com/data/iris.data",
    names=["sepal length", "sepal width", "petal length", "petal width", "class"],
)
df.head()
sepal length sepal width petal length petal width class
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa

ヒストグラム

まずはヒストグラム(histgram; 度数分布表)を描画してみる.

ヒストグラムは数値データの分布の概要を知る際に便利である.

pandasのデータフレームのhistメソッドを用いる.

引数binsでビンの数($x$軸の区分数)を指定できる.色々変えて試してみよう.既定値は $10$ である).

df.hist(bins=30);

問題 (SAT,GPA)

http://logopt.com/data/SATGPA.csv データを読み込み,2種類のSATの成績とGPAのヒストグラムを描画せよ. また,引数のbinsを色々変えてみよ.

問題 (ポケモン)

以下のように読み込んだポケモンのデータフレームを用いて,攻撃力(Attack),守備力(Defense)のヒストグラムを描画せよ.

pokemon = pd.read_csv("http://logopt.com/data/poke.csv", encoding="utf-8", index_col=0)
pokemon.head()
Name Type 1 Type 2 Total HP Attack Defense Sp. Atk Sp. Def Speed Generation Legendary Japanese
#
1 Bulbasaur Grass Poison 318 45 49 49 65 65 45 1 False フシギダネ
2 Ivysaur Grass Poison 405 60 62 63 80 80 60 1 False フシギソウ
3 Venusaur Grass Poison 525 80 82 83 100 100 80 1 False フシギバナ
3 VenusaurMega Venusaur Grass Poison 625 80 100 123 122 120 80 1 False フシギバナ・メガ進化
4 Charmander Fire NaN 309 39 52 43 60 50 65 1 False ヒトカゲ

散布図

散布図(scatter plot)は,2つの数値データの関係を知る際に便利である.

pandasのデータフレームのplot.scatterメソッドを用いる.

引数のxyで $x,y$ 軸に使用するデータ(データフレームの列名)を指定する.

ここでは'がく片長 'sepal length'と花びら長 'petal length'の関係を図示してみる.

df.plot.scatter(x="sepal length", y="petal length")

問題 (ダイヤモンド)

http://logopt.com/data/Diamond.csv からダイアモンドの価格データを読み込み,カラット (carat) と価格 (price) の散布図を描け.

問題(ポケモン)

ポケモンのデータフレームに対して,攻撃力(Attack),守備力(Defense)の関係を散布図に描画せよ.

他の図

pandasのデータフレームのplotの中には他にも様々な描画メソッドが含まれている.

色々試してみよう.

df.plot.area();
# 面積図
df.plot.bar();
# 棒グラフ
df.plot.box();
# 箱ひげ図
df.plot.density();
# カーネル密度推定(kdeと同じ)
df.plot.hexbin(x="sepal length", y="petal length", gridsize=30);
# 6角形格子図(散布図と同様に,x,y軸を入れる.)
df.plot.line();
# 線グラフ df.plot()でも同じ

問題 (ポケモンの応用)

ポケモンのデータを自分なりに可視化せよ.その際,どの列に対して,どの図を選択すれば良いか考え,考察とともにレポートせよ.

多次元データ 1: 並行座標図

散布図では3次元までの図を可視化できる(3次元の場合の方法については教科書参照)が,4次元以上だと人間の目では無理だ.

多次元データを可視化するための方法として並行座標図がある.

これは散布図の $y$ 軸を $x$ 軸と並行にしたものである.複数の $y$ 軸があっても大丈夫だが,あまり多いとよく分からなくなる.

pandasではplottingにあるparallel_coordinates 関数を用いて並行座標図を描画できる.

第1引数はデータフレームであり,第2引数は分けて描画したいクラスを表す列名を与える.

並行座標図は,比例関係は平行な直線で,反比例関係は1つの点で交わる直線群で描画する.

例として,$x= 0,1,2, \ldots,9$,$y=2x$, $z=10-x$ を描画してみよう.

from pandas.plotting import parallel_coordinates
data = []  # x,y,zを入れたリストを作成
for i in range(10):
    row = [i, 2 * i, 10 - i, "dummy"]  # クラス名を指定するためにダミーの列を最後に追加
    data.append(row)
testdf = pd.DataFrame(data)
testdf
0 1 2 3
0 0 0 10 dummy
1 1 2 9 dummy
2 2 4 8 dummy
3 3 6 7 dummy
4 4 8 6 dummy
5 5 10 5 dummy
6 6 12 4 dummy
7 7 14 3 dummy
8 8 16 2 dummy
9 9 18 1 dummy
parallel_coordinates(testdf, 3);
# ダミーの列の名前は 3

問題 (アヤメとポケモン)

  1. iris(アヤメ)のデータフレームをアヤメの種類 "class" ごとに並行座標図に描画せよ.

  2. ポケモンデータを並行座標図で描画せよ. ただし,伝説ポケモンか否か(Legendary)でクラス分けを行い,軸としては体力(HP),攻撃力(Attack),守備力(Defense)を用いよ.

iris = pd.read_csv(
    "http://logopt.com/data/iris.data",
    names=["sepal length", "sepal width", "petal length", "petal width", "class"],
)

多次元データ 2: Andrew曲線

多次元データを可視化するためのもう1つの方法としてAndrew曲線がある.

これは.多次元データ $x_1,x_2,\ldots$ をフーリエ曲線

$$ f_x(t) = \frac{x_1}{\sqrt 2} + x_2 \sin(t) + x_3 \cos(t) + x_4 \sin(2t) + x_5 \cos(2t) + \cdots $$

で変換し,$-\pi < t < \pi$ の間に描画したものである.

pandasではplottingにあるandrews_curves 関数を用いて並行座標図を描画できる.

第1引数はデータフレームであり,第2引数は分けて描画したいクラスを表す列名を与える.

例として,$x= 0,1,2, \ldots,9$,$y=2x$, $z=10-x$ を描画してみよう.

from pandas.plotting import andrews_curves
andrews_curves(testdf, 3);

問題 (アヤメとポケモン)

  1. アヤメ(iris)データをアヤメの種類 (class) ごとにAndrew曲線で描画せよ.

  2. ポケモンデータをAndrew曲線で描画せよ. ただし,伝説ポケモンか否か(Legendary)でクラス分けを行い,軸としては体力(HP),攻撃力(Attack),守備力(Defense)を用いよ.

データ解析用描画パッケージ seaborn

seaborn https://seaborn.pydata.org/ を用いることによって,さらに簡単にデータ解析ができ,描画も綺麗になる.

まずはseabornパッケージをsnsという別名で読み込んでおく.

import seaborn as sns

%matplotlib inline

ヒストグラム

seabornでヒストグラム(histgram, 度数分布表)に対応するものはdisplot(distribution plot)だ.

例としてiris(アヤメ)のデータの「がく片長」(sepal length)を描画してみる.

引数としては,以下のものがあるので,色々試してみよう.

  • bins: ビンの数
  • kde: カーネル密度推定の有無
  • rug: データの位置(敷物:rug)の有無
df = pd.read_csv(
    "http://logopt.com/data/iris.data",
    names=["sepal length", "sepal width", "petal length", "petal width", "class"],
)
df.head()
sepal length sepal width petal length petal width class
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
sns.displot(df["sepal length"], bins=20, rug=True, kde=True);

問題 (SAT, GPA)

http://logopt.com/data/SATGPA.csv データを読み込み,GPAの分布をdistplotを用いて描画せよ.

散布図

seabornで散布図に対応するものはjointplotだ.おまけとして分布も表示される.

引数としては,$x,y$ と $data$ の他に種類を表すkindがある.

kindは以下のものがとれるので,色々試してみよう.

  • "scatter":普通の散布図
  • "reg" : 回帰分析
  • "resid" : 回帰の残差
  • "kde" : カーネル密度推定
  • "hex" : 6角形格子
sns.jointplot(x="sepal length", y="petal length", data=df, kind="scatter");

データフレームに含まれる複数の列間の散布図を一度に描画するにはpairplotを用いる.

引数としてhue(色調)をとることができ,色を変えたい列名(以下の例ではアヤメの種類(class)を指定して描画している.

sns.pairplot(df, hue="class");

問題 (SAT,GPA)

  1. http://logopt.com/data/SATGPA.csv データを読み込み,MathSATとGPAの関係をjointplotを用いて描画せよ.
  2. http://logopt.com/data/SATGPA.csv データを読み込み,MathSAT, VerbalSAT, GPAの相互関係をpairplotを用いて描画せよ.

回帰プロット

seabornでは統計パッケージstatsmodelsを用いた回帰分析の結果を描画することができる.

カテゴリカルデータ別の回帰を行いたい場合には lmplot を用いる.

例としてtipsデータを用いる.

FacetGridでは行(row)と列(col)を表すカテゴリカルデータを引数で指定できる他に,色調(hue)も用いることができる.

以下の例では,tipsデータに対して支払総額(total_bill)とチップ(tip)の関係を, 行に喫煙者か否か(row="smoker"),列に性別(col="sex"),色調に昼食か夕食か(hue="time")を設定して分析する.

tips = sns.load_dataset("tips")
tips.head()
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
sns.lmplot(x="total_bill", y="tip", data=tips, row="smoker", col="sex", hue="time");

カテゴリカルデータの分析

カテゴリカルデータ別に集計したい場合には,catplotを用いると便利だ.

引数は以下の通り.

  • x: $x$軸
  • y: $y$軸
  • data: データフレーム
  • row: 行名
  • col: 列名
  • hue: 色調
  • kind: グラフの種類 (point, bar, count, box, violin, strip)
  • split: バイオリン図(violin:箱ひげ図の拡張)の場合だけ有効で,Trueのとき左右に色調を分けて描画する.

以下の例では,tipsデータに対し,日ごと(x)のチップ(y)を喫煙者か否か(row="smoker"),性別(col="sex"), 昼食か夕食か(hue="time")ごとにバイオリン図で描画している.

sns.catplot(
    x="day",
    y="tip",
    data=tips,
    row="smoker",
    col="sex",
    hue="time",
    split=True,
    kind="violin",
);

問題(タイタニック)

  1. titanicデータを読み込み,運賃(fare)と生存確率(survived)のロジスティック回帰を,性別(sex)ごとに行え (ヒント:ロジスティック回帰を行うには,lmplotの引数のlogisticをTrueにする).

  2. titanicデータを読み込み,客室クラス(class)と性別(sex)ごとの生存確率(survived)を集計した結果を描画せよ.

titanic = sns.load_dataset("titanic")
titanic.head()
survived pclass sex age sibsp parch fare embarked class who adult_male deck embark_town alive alone
0 0 3 male 22.0 1 0 7.2500 S Third man True NaN Southampton no False
1 1 1 female 38.0 1 0 71.2833 C First woman False C Cherbourg yes False
2 1 3 female 26.0 0 0 7.9250 S Third woman False NaN Southampton yes True
3 1 1 female 35.0 1 0 53.1000 S First woman False C Southampton yes False
4 0 3 male 35.0 0 0 8.0500 S Third man True NaN Southampton no True