こんにちは、データサイエンティストのたぬ(@tanuhack)です!
Pythonでグラフを描画するときによく使われているライブラリとして『Matplotlib』が挙げられます。
しかし、このMatplotlibは、多機能であるが故に設定が面倒だったり、美しいグラフが描けたとしても無駄にコードが長くなってしまったりと何かと問題が付きまとうと思いませんか?
ヒストグラムで確認したいことと言えば、データの全体的な『ばらつき具合』を確認したいだけなのに、たかだかグラフの設定にそこまで時間を掛けられない…。
そこで今回はMatplotlibより、もっと簡単に美しいグラフが描ける『seaborn(シーボーン)』を使って、ヒストグラムを作成する方法を紹介します!
目次
準備
まず、seabornをインストールします。
次に、Pythonプログラムで必須ライブラリをインポートします。
今回のseabornによるグラフの描画は『Jupyter Notebook』内とします。
# 数値計算に用いるライブラリ
import math
import numpy as np
import pandas as pd
# グラフを描画するライブラリ
from matplotlib import pyplot as plt
import seaborn as sns; sns.set() # sns.set() ==> グラフの見た目をseabornに合わせる
%matplotlib inline
データセットは、Rでお馴染みの『アヤメの統計データ』をPandasのDataFrameに格納させます。
アヤメのデータセットは、seabornに予め用意されているのでそれを使います。ありがたや〜。
iris = sns.load_dataset('iris')
# type(iris) ==> pandas.core.frame.DataFrame

レコード数 | 150 |
---|---|
カラム数 | 5 |
カラム名 | 説明 | データの尺度名 |
---|---|---|
sepal_length | がく片の長さ:4.3~7.9(cm) | 間隔尺度(量的変量) |
sepal_width | がく片の幅:2.0~4.4(cm) | 間隔尺度(量的変量) |
petal_length | 花びらの長さ:1.3~6.9(cm) | 間隔尺度(量的変量) |
petal_width | 花びらの幅:0.1~2.5(cm) | 間隔尺度(量的変量) |
species | アヤメの種類(setosa, versicolor, virginica) | 名義尺度(質的変量) |
ヒストグラムを描画する
sns.distplot(
iris['sepal_width'], bins=13, color='#123456', label='data',
kde=False,
rug=False
)
plt.legend() # 凡例を表示
plt.show() # ヒストグラムを表示
seabornのdistplot
メソッドでヒストグラムを描画します。
オプション | 説明 |
---|---|
data |
Seriesまたは1d-array、listのみ |
bins |
等級値(x軸の刻み目)の数。スタージェスの公式(※後述)で最適化可能 |
color |
色の指定 |
label |
凡例の指定。plt.legend() 必須。 |
kde |
True:密度近似関数の描画 |
rug |
True:実数値の描画 |
fit |
norm:正規分布の描画 |
とりあえず、必須級のオプションはこれだけ。
もっと詳しくオプションを知りたい人は公式ライブラリ(英語)も合わせてどうぞ。
スタージェスの公式で階級幅(bins)を最適化する
ヒストグラムは階級幅のとり方によって、同じデータから作成しても形状が変わることが知られています。
ヒストグラムで実現したいことと言えば、サンプルデータの分布から、データの背景にある『真の分布』を知りたい、ということになります。
この目的を達成するためには、描画するヒストグラムの適切な階級値を設定しなければいけません。
その代表例が『スタージェスの公式』です
# スタージェスの公式で適切なbinsの値を求める
sturges = lambda n: math.ceil(math.log2(n*2))
sturges(len(iris['sepal_width']))
# ==> 9
よってサンプル数150に対して、適切な階級数は9ということがわかりました。
サンプルサイズ | スタージェスの公式で求めた階級数 |
---|---|
20 | 6 |
50 | 7 |
100 | 8 |
250 | 9 |
500 | 10 |
カーネル密度推定(kde)を描画する
seaborn.distplot
メソッドのkde
オプションをTrue
にすると、ヒストグラムに滑らかな線(いまあるデータから、全体の分布の推定)を上から重ねて描画します。
(わかりやすいようにbinの値を極端に3にしています。)
sns.distplot(
iris['sepal_width'], bins=3, color='#123456', label='data',
kde=True
)
plt.legend()
plt.show()
kdeのグラフに対しても色やラベルを指定したい場合は、kde_kws={'color':'yellow','label':'kde'}
のように記述します。
kde
の他にも、hist
, rug
, fit
オプションに関しては、{hist, kde, rug, fit}_kws
のようにグラフ自体に他のオプションを付与できます。
正規分布を描画させる
seaborn.distplot
メソッドのfit
オプションをnorm
にすると、正規分布を描くことができます。
from scipy.stats import norm
sns.distplot(
iris['sepal_width'],bins=sturges(len(iris['sepal_width'])),color='black',label='data',
kde_kws={'label': 'kde','color':'k'},
fit=norm,fit_kws={'label': 'norm','color':'red'},
rug=False
)
plt.legend()
plt.show()
ヒストグラムをカテゴリ毎に描画する
次に、irisデータには『品種(species)』の項目があるので、品種カテゴリごとにsepal_widthのヒストグラムを描写してみます。
まとめて描画する
アヤメの品種別の『sepal_width』を重ねず、それぞれ独立した状態でまとめてヒストグラムを描画させたい場合は、seabornのFacetGrid
メソッドを使います。
grid = sns.FacetGrid(iris, col='species', hue='species', col_wrap=3, size=5)
grid.map(sns.distplot, 'sepal_width', bins=7, kde=True)
plt.show()
各品種それぞれ、正規分布っぽい形をしていることが視認できますね。
重ねて描画する
次は、1つ1つのヒストグラムを独立させず、1つのグラフとして重ねたものになります。
sns.distplot(x)
sns.distplot(y)
sns.distplot(z)
こんな感じでdistplot
メソッドを連続で使用します。
sns.distplot(
iris.query('species=="setosa"')['sepal_width'],
bins=sturges(len(iris.query('species=="setosa"')['sepal_width'])),
color='red',
kde=True,
label='setosa'
)
sns.distplot(
iris.query('species=="versicolor"')['sepal_width'],
bins=sturges(len(iris.query('species=="versicolor"')['sepal_width'])),
color='blue',
kde=True,
label='versicolor'
)
sns.distplot(
iris.query('species=="virginica"')['sepal_width'],
bins=sturges(len(iris.query('species=="virginica"')['sepal_width'])),
color='green',
kde=True,
label='virginica'
)
plt.legend()
plt.show()
スマートにいきたければ、forループを使いましょう。
iris_species_arr = iris['species'].unique()
color_dict = {0:'red', 1:'blue', 2:'green'}
for index, item in enumerate(iris_species_arr):
data = iris.query('species=="'+item+'"')['sepal_width']
sns.distplot(
data,
bins=sturges(len(data)),
color=color_dict[index],
kde=True,
label=item
)
plt.legend()
plt.show()
全ての列のヒストグラムを描画する
さいごはseabornの機能ではありませんが、DataFrameのhist()
を使って、各列ごとのヒストグラムを一気に描画出来る方法があるので、それを紹介します。
irisデータは全部で5つ列がありますが、それぞれの列の値に1つでも質的データが含まれる場合、描画されません。
つまり、今回はspecies以外の列が全てヒストグラムで描画される対象になるので、合計4つプロットされます。
カテゴリを指定しない場合
from pylab import rcParams
rcParams['figure.figsize'] = 10, 10 # グラフのサイズを大きくする
iris.hist(bins=9);
plt.tight_layout() # グラフ同士が重ならないようにする
plt.show()
カテゴリを指定する場合
from pylab import rcParams
rcParams['figure.figsize'] = 10, 10
iris.query('species=="setosa"').hist(bins=7)
plt.tight_layout() # グラフ同士が重ならないようにする
plt.show()
DataFrameのquery
で条件指定するだけです。
さいごに
今回は、Pythonのseabornでヒストグラムを描画する方法を紹介しました。
自分が確認するときは、ササッと手短に。誰かに共有するときは、認知不可が無いようにわかりやすいグラフ作成を心がけましょう〜!
それでは