Python、機械学習

【Python】matplotlibとseabornのグラフの書き方の違い、データ分析でよく見るグラフ化手法

Pythonでデータ分析するときは、必ずグラフを描きます。

グラフ化することでデータの全体像や特徴をつかんだり、相関関係を把握したり、外れ値はないかチェックすることができます。

しかしながら、グラフ化の方法は色々あって、なかなか頭に入ってこない!定着しない!

ということで、良く使うグラフ化手法と、とても便利なグラフ化手法を一気にまとめました。

matplotlibのpyplotとseabornの使い分けについても書いていますので、参考になると思います。

グラフ化で困ったときに戻ってこれる記事だと思います。

まずは必要なライブラリをインポート

まずは必要なライブラリをインポートします。

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
%matplotlib inline
import seaborn as sns

今回は、scikit-learnにも収録されている「Boston house-prices(ボストンの住宅価格)」のデータセットを使ってグラフを描いていきます。

from sklearn.datasets import load_boston

boston = load_boston()

df = pd.DataFrame(boston.data, columns= boston.feature_names, 
index=np.arange(506))
df.head()

ビニングをして離散値化した変数も作る

連続した数字のデータばかりですので、ビニングをして離散値化した変数も作りたいと思います。

ビニングとは、連続値のデータに区切りを設けていくつかのカテゴリーに分けて離散値化する処理です。10以下、10以上20以下、20以上の3つの離散値にするといったやり方です。

yesかnoかの2択の変数も、二値にビニングされた変数です。

データには往々にして離散値化した変数も含みます。そして、離散値化した変数に特有のグラフ化手法というのもあります。

ですので、ここでは敢えて離散値化した変数を用意することにします。

# yesかnoの2つのレベルに分割した指標を作る
df.loc[df['CRIM'] > 0.25, '犯罪は多いか?'] = 'yes'
df.loc[df['CRIM'] <= 0.25, '犯罪は多いか?'] = 'no'
df.loc[df['LSTAT'] > 11, '地位は低いか?'] = 'yes'
df.loc[df['LSTAT'] <= 11, '地位は低いか?'] = 'no'

# 5つのレベルに分割した指標も追加する
df['犯罪発生レベル'] = pd.qcut(df['CRIM'], 5)
df['地位の低さレベル'] = pd.qcut(df['LSTAT'], 5)

CRIMは犯罪発生率、LSTATは給与の低い職業に従事する人口の割合です。

それぞれしきい値を設けて、しきい値より大きいとyes、しきい値以下だとnoの二値化したカラムを作りました。

df[‘犯罪は多いか?’] 、df[‘地位は低いか?’] のことです。

それから、CRIM、LSTATをそれぞれ5分割して5つのカテゴリーに分けた(つまり五値化した)カラムも作りました。

df[‘犯罪発生レベル’] 、df[‘地位の低さレベル’] のことです。

最後に、目的変数である価格住宅のカラムdf[‘price’]を足します。

df['price'] = boston.target
df.head()

データ数を減らしたdf_miniもこしらえておきます。(あとで便利なので)

df_mini =df.iloc[:16]

目的変数の分布を調べる:ヒストグラム

主に目的変数の分布を調べるためにヒストグラムを描きます。

matplotlibのpyplotでヒストグラムを描く

plt.hist()で描けます。

plt.hist(df['price'], color='red', alpha=0.5, bins=10)
plt.xlabel('price')
plt.show()

alphaというのは色の透明度です。0から1までです。0.5にしているので少し透けた色合いになっています。

binsはbin(棒)の数です。

sns.set()を入れると、seaborn風のかっこいい外観に変わります。

sns.set()
plt.hist(df['price'], color='red', alpha=0.5, bins=10)
plt.xlabel('price')
plt.show()

seabornでヒストグラムを描く

sns.distplot()で描けます。

sns.distplot(df['price'], kde=True, rug=True, color='red', bins=10)
plt.xlabel('price')
plt.show()

かなりいい感じになりました。

kdeはkernel density estimate(カーネル密度推定)の略で、「滑らかなヒストグラムのグラフを入れますか?」というものです。上の図でいう赤い曲線がそうです。これを外すならkde=Falseにします。

rugとはrug plot(ラグプロット)のこと。ヒストグラムの下の方にある、バーコード状のプロットがそうです。

離散値をとる変数で場合分けをして、目的変数のヒストグラムを作る

yesかnoの二値化された変数 df[‘犯罪は多いか?’] で場合分けをして、目的変数のヒストグラムを描きます。ここでは例としてseabornを使います。

sns.distplot(df['price'][df['犯罪は多いか?'] =='yes'], 
kde=True, rug=True, color='red', bins=10)
sns.distplot(df['price'][df['犯罪は多いか?'] =='no'], 
kde=True, rug=True, color='blue', bins=10)

plt.xlabel('price')
plt.show()

「犯罪が多いか?」がyesの方(赤色)が、住宅価格が安くなる傾向が見て取れます。

index vs. 目的変数の関係を可視化する:折れ線グラフ

主にindexと目的変数の関係を見える化するときに折れ線グラフを使います。時系列データの場合は、目的変数の経時変化が可視化されますので便利です。

matplotlibのpyplotで折れ線グラフを描く

plt.plot()で簡単に描けます。

plt.plot(df['price'], color='red', alpha=0.5)
plt.ylabel('price')
plt.show()

sns.set()を使うとこんな感じになります。

sns.set()
plt.plot(df['price'], color='red', alpha=0.5)
plt.show()

seabornで折れ線グラフを描く

sns.lineplot()で描けます。

sns.lineplot(x=df.index, y='price', data=df, color='red', alpha=0.5)

あるいは、sns.relplot()として、カッコ内で折れ線グラフモードを選択する方法も可能です。

sns.relplot(x=df.index, y='price', data=df, kind='line', color='red', 
alpha=0.5)

離散値をとる変数で場合分けをして、目的変数の折れ線グラフを作る

yesかnoの二値化された変数 df[‘犯罪は多いか?’] で場合分けをして、目的変数の折れ線グラフを描きます。ここでも例としてseabornを使います。

場合分けに使う変数(離散値)はhueに指定すればOKです。hueとは色合いのことで、ここの例で言うと、犯罪が多いか少ないかの2パターンで見た目(色など)を変えて表示しましょう、という意味です。

sns.lineplot(x=df.index, y='price', data=df, hue='犯罪は多いか?', 
alpha=0.5)

hueを使わずに、こういう書き方もできます。

sns.lineplot(x=df[df['犯罪は多いか?'] =='yes'].index, y='price', 
data=df[df['犯罪は多いか?'] =='yes'], color='red', alpha=0.5)

sns.lineplot(x=df[df['犯罪は多いか?'] =='no'].index, y='price', 
data=df[df['犯罪は多いか?'] =='no'], color='blue', alpha=0.5)

index vs. 目的変数の関係を可視化する:棒グラフ その1

主にindexと目的変数の関係を見える化するときに棒グラフを使います。時系列データの場合は、目的変数の経時変化が可視化されます。

ここでは、見やすさのため、データ数を減らしたdf_miniを使います。

matplotlibのpyplotで棒グラフを描く

plt.bar()で簡単に描けます。横軸、縦軸の順でデータを指定したのち、width:棒の幅、tick_label:棒のラベルの情報を入れます。

plt.bar(df_mini.index, df_mini['price'], width=0.7, color='red', 
alpha=0.5, tick_label=df_mini.index)
plt.ylabel('price')
plt.show()

seabornで棒グラフを描く

sns.barplot()で描けます。

sns.barplot(x=df_mini.index, y='price', data=df_mini)

sns.catplot()で、カッコ内で棒グラフモードを選択する方法も可能です。

sns.catplot(x=df_mini.index, y='price', data=df_mini, kind='bar')

説明変数(離散値) vs. 目的変数の関係を可視化する:棒グラフ その2

主に離散値を取る説明変数と目的変数の関係を見える化するときにも棒グラフが使えます。seabornで描いてみます。

yesかnoの二値化された変数 df[‘犯罪は多いか?’] と、目的変数の関係を可視化してみます。

sns.barplot(x='犯罪は多いか?', y='price', data=df_mini)

自動でエラーバーがつきます。

離散値をとる変数をカウントして棒グラフにする

sns.countplot()で描けます。

sns.countplot(x='犯罪は多いか?', data=df)

離散値をとる変数が2つあれば、このようなカウント方法も可能になります。

sns.countplot(x='犯罪は多いか?', data=df, hue='地位は低いか?')

説明変数 vs. 目的変数を可視化する:散布図

データ分析の中でもよく使うのが、この散布図だと思います。

matplotlibのpyplotで散布図を描く

横軸をCRIM、縦軸をpriceにして散布図を描きます。plt.scatter()で描けます。

plt.scatter(df['CRIM'], df['price'], color='red', alpha=0.5)
plt.xlabel('CRIM')
plt.ylabel('price')
plt.show()

seabornで散布図を描く

sns.scatterplot()で描けます。

sns.scatterplot(x='CRIM', y='price', data=df, color='red', alpha=0.5)

sns.relplot()とし、カッコ内で散布図モードを選択する方法も可能です。

sns.relplot(x='CRIM', y='price', data=df, kind='scatter', 
color='red', alpha=0.5)

hueを使えば、離散値をもつ変数で場合分けをすることができます。

sns.scatterplot(x='CRIM', y='price', data=df, color='red', 
alpha=0.5, hue='地位は低いか?')

離散値をもつ変数同士を軸にマッピングする:ヒートマップ

seabornではヒートマップも作成することができます。まずは、集計表を作成します。df.pivot_table()で、集計表を作成できます。

# 説明変数(離散値)2つのそれぞれの組合せに当てはまる目的変数の平均を出す
df_for_heatmap = df.pivot_table(index='犯罪は多いか?', 
columns='地位は低いか?', values='price')

df_for_heatmap

そして、sns.heatmap()とし、カッコ内で先ほど作成した集計表を指定することでヒートマップが描けます。

# annotは数字を入れるかどうか。cmapは色合い。fmtは数字の表し方。
sns.heatmap(df_for_heatmap, annot=True, cmap='Purples', fmt="1.2f")

・annotはannotation(注釈)の略で、ヒートマップ上に数値を載せるかどうかを指定します。

・cmapはカラーマップの略で、色合いを指定します。カラーマップの種類はたくさんあり、こちらのURLにその一覧が載っています。
Choosing Colormaps in Matplotlib

・fmtはformatの略で、数値の表し方を指定します。1.2fの「1」は、注釈として入れる数値の幅のことで、これが大きいと数値の左にスペースが大きく入ります。見た目の注釈の位置を揃えるためのものです。「.2f」は小数点以下のケタ数のことで、小数点以下2ケタにするなら、2を「.」と「f」で挟みます。ここら辺のルールは、こちらのサイトが参考になります。

Pythonの文字列フォーマット(formatメソッドの使い方)

説明変数の離散値の数に応じて、ヒートマップの碁盤目が細かくなります。

# ビニングしたあとの値をstrにしないとヒートマップがかけない(エラーが出る)。
# よって、strに変換する。
df['犯罪発生レベル']=df['犯罪発生レベル'].astype(str)
df['地位の低さレベル']=df['地位の低さレベル'].astype(str)
df_for_heatmap = df.pivot_table(index='犯罪発生レベル', 
columns='地位の低さレベル', values='price')

df_for_heatmap

ビニングして作成した変数(離散値)はintervalという形式になっていて、そのままだとヒートマップが描けません。エラーが出ます。

したがって、DataFrameでtypeを変換するメソッド:astype()を使って文字列に変換しています。

ヒートマップを描くとこんな感じになります。

sns.heatmap(df_for_heatmap, annot=True, cmap='Purples', fmt="1.2f")

便利な組み合わせグラフ

散布図とヒストグラムの組み合わせ

sns.jointplot(x='CRIM', y='price', data=df, color='red', alpha=0.5)

全変数の組み合わせの散布図

sns.pairplot(df)

変数(離散値)をhueに指定すると、その変数の違いで色分けすることができます。

sns.pairplot(df, hue='地位は低いか?')

相関係数のヒートマップ

各変数同士の相関係数は、次のように一括算出できます。

df.corr()

この相関係数の大小をヒートマップにして可視化すると便利です。

plt.figure(figsize=(12,7))
sns.heatmap(df.corr(), annot=True, cmap='Purples', fmt="1.1f")

散布図の上に、線形回帰グラフと95%信頼区間を可視化する

2つの変数の散布図に、「線形回帰のグラフ」と、「グラフの95%信頼区間」を重ねて可視化できるのがsns.regplot()です。

sns.regplot(x='CRIM', y='price', data=df, color='red')

2つの変数(離散値)の全組み合わせごとにグラフの枠を用意し、その枠に別の2変数の関係をグラフにしたものをのせる:FacetGrid

ちょっと何言ってるかわからない、とサンドウィッチマンの富澤に言われそうですが、下のコードと図を見ればわかると思います。

まず、2つの変数(離散値)を選びます。両方とも5つの離散値から成るとしたら、次の操作で5×5のグラフスペースができます。

g = sns.FacetGrid(df, col='犯罪発生レベル', row='地位の低さレベル')

例えば、一番左上のグラフスペースは、「地位の低さレベル」が (1.729, 6.29) で、かつ「犯罪発生レベル」が (0.00532, 0.0642)であることが前提の空間になります。

グラフスペースに載せたいグラフを次のように指定すれば、グラフを描くことができます。

g = sns.FacetGrid(df, col='犯罪発生レベル', row='地位の低さレベル')
g = g.map(sns.scatterplot, 'RM', 'price')

データの傾向をつかむのにとても便利です。

ちなみに、FacetGridのFacetは宝石のカット面、Gridは格子という意味です。宝石のカット面みたいにきれいに場合分けして、格子状にグラフを並べたものというニュアンスです。

そして、gというのはgridの頭文字をとった変数です。gじゃなくてgridとする人もいます。

複数のグラフを並べてのせる方法

こちらもよく使います。長くなってきたので、別記事で紹介します。