Pythonの以下のライブラリを使ってhistgramを描く。
以下のような細かい設定・よく使う設定もなるべく一緒にまとめる。
!python --version
!pip list | grep -E '^(pandas|numpy|scikit-learn|matplotlib|seaborn|plotly|plotly-express|bokeh) '
import os, sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly
import bokeh
%matplotlib inline
scikit-learnの load_iris() をDataFrameに格納して使う
詳しいデータの内容は 7.2.2. Iris plants dataset を参照。
dfdft 以上の2つを用意する。
from sklearn.datasets import load_iris
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df.head()
dft = df.copy()
dft['target'] = iris.target
dft['target_name'] = dft.target.replace({i: name for i, name in enumerate(iris.target_names)})
dft.head()
pandasはmatpllotlibを呼び出す。細かい設定をするときの引数はmatplotlibと同じ。
DataFrameに対するhistgramは、DataFrame.hist() と DataFrame.plot.hist() の2種類あり、出力が微妙に異なる。
df.hist() はカラムごとにグラフを出力するsharex=True, sharey=Truerange=(0, df.max().max())df.plot.hist() は全てのカラムを1つのグラフに出力するalpha を指定することで透明度を変更stacked=True にすることで積み上げ# v1.0.0から backendを指定できるようになったのでmatplotlibかどうか確認
pd.options.plotting.backend
df.hist()
# rangeを統一する
df.hist(sharex=True, sharey=True, range=(0, df.max().max()))
df.plot.hist()
df.plot.hist(alpha=0.3)
df.plot.hist(stacked=True)
SeriesやgroupbyしたDataFrameに対しても hist() 関数は実装されている。
df['petal length (cm)'].hist()
dft[['petal length (cm)', 'target_name']].groupby('target_name').hist()
ラベル (target_name)ごとのhistgramは groupby() や pivot() を使えばうまく出力できる
dft.groupby('target_name')['petal length (cm)'].hist(range=(0,df.max().max()), alpha=0.3)
dft.pivot(columns='target_name')['petal length (cm)'].hist(range=(0,df.max().max()))
pandasが呼び出しているのは plt.hist()
x は (n,) array or sequence of (n,) arrays なので、DataFrameを渡すときは注意# NG
print(df[['petal length (cm)']].shape)
plt.hist(x=df[['petal length (cm)']])
# OK
print(df.loc[:,'petal length (cm)'].shape)
plt.hist(x=df.loc[:,'petal length (cm)'])
label にラベル名(カラム名)を渡し、
plt.legend()を実行すると、凡例を出力できる
凡例の位置は調整可能 (参考: https://qiita.com/matsui-k20xx/items/291400ed56a39ed63462)
cols = ['petal length (cm)','petal width (cm)']
print(df.loc[:,cols].T.shape)
plt.hist(x=df.loc[:,cols].T, label=cols)
plt.legend()
histtype を指定すると、細かい設定をせずに出力できる
typeは {'bar', 'barstacked', 'step', 'stepfilled'} の4種類ある。
(参考 https://matplotlib.org/3.1.1/gallery/statistics/histogram_multihist.html)
また、複数のグラフを表示したいときは subplots を使う
(参考 matplotlib.pyplot.subplots)
fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True, figsize=(12, 8))
for htype,ax in zip(['bar', 'barstacked', 'step', 'stepfilled'], axes.flatten()):
    ax.hist(iris.data, histtype=htype, label=iris.feature_names, bins=10)
    ax.set_title(htype)
    ax.legend(loc='upper right')
    
# fig.show()
savefig() で保存
fig.savefig('matplotlib_sample.png')
sequence of (n,) arrays であれば、labelにラベル名を渡すことができる。
異なる場所にあるデータを1つのhistgramに集約させたい場合はplt.hist() を重ねればよい。
fig, ax = plt.subplots(1,1)
for n in iris.target_names:
    # DataFrameでは本来バラバラで渡す必要はないけど、具体例のためにtarget_nameのデータを取り出しています
    x= dft.query('target_name=="{}"'.format(n))['petal length (cm)']
    ax.hist(x=x, label=n)
    
ax.legend(loc='upper right')
# fig.show()
fig, ax = plt.subplots(1,1)
x_data = []
for n in iris.target_names:
    x_data.append(dft.query('target_name=="{}"'.format(n))['petal length (cm)'])
# listで一気に渡してもよい
ax.hist(x=x_data, label=iris.target_names, histtype='barstacked')
ax.legend(loc='upper right')
# fig.show()
sns.hist() はない。histgramに適しているのは sns.distplot
kde (kernel density estimate) がデフォルトで True になっているkde=False にするa が Series, 1d-array, or list なのでSeriesをそのまま渡しても予想通りの挙動をするだいたいmatplotlibと同じように使えるが、ちょいちょい使い勝手が違うので、sns.set()して、matplotlib使う方が正直楽なところもある。
sns.set()
sns.distplot(df[['petal length (cm)']], bins=10)
# rangeなど matplotlib側の設定を変えたいときは hist_kws で渡す
sns.distplot(df[['petal length (cm)']], bins=10,kde=False, hist_kws={'range':(0,10)})
seabornでは1次元配列しか受け取れない。
そのため、複数データを1つのhistgramに集約させたい場合はsns.distplot() を重ねる必要がある。
histtype は設定できないので、stackしたり、ならべたいなら ラベルごと・複数データに対するhistgram(matplotlib編))
の方法で。
for i in range(4):
    sns.distplot(iris.data[:,i],
                 label=iris.feature_names[i],
                 kde=False, 
                 bins=10,
                 hist_kws={'range': [1,10]}
                )
    
plt.legend()
matplotlibと同じ
plt.savefig('seaborn_sample.png')
plotlyはjavascriptベースで、tooltipを自動で設定してくれるのが良い。見た目もデフォルトのままで使える。
kaggleのkernelでもよくみる。
plotlyそのままだと書くコード量が多くなる。 なるべくplotly-expressを使いたい。
細かい調整をしたくなると、結局plotly本体の設定を変更することになる。
histgramについてはだいたい https://plotly.com/python/histograms/ のページで解決する。
(参考 https://qiita.com/inoory/items/12028af62018bf367722)
plotly-expressを使わず、plotlyだけでhistgramを描くにはgo.Figure()とgo.Histgram()を使う。
go.Figure() に go.Histgram()のリストを渡すことで複数データのhistgramを作成できる。
import plotly
import plotly.graph_objs as go
# offlineで使う
plotly.offline.init_notebook_mode(connected=False)
# 以降、offlineで使う場合は fig.show()をplotly.offline.iplot(fig)に置き換える
fig = go.Figure(data=go.Histogram(x=iris.data[:,0]))
fig.show()
# target_name ごとのhistgramを一緒に表示
data=[
    go.Histogram(x=dft.query('target_name=="{}"'.format(n))['petal length (cm)'], name=n)
      for n
    in iris.target_names
]
fig = go.Figure(data=data)
fig.show()
xbins で行う。data はリストでなくても add_trace で逐一追加することも可能fig = go.Figure()
for i in range(len(iris.feature_names)):
    fig.add_trace(go.Histogram(x=iris.data[:,i], name=iris.feature_names[i],
                 xbins={'start':0, 'end':10, 'size':1}))
fig.show()
plotly.offline.plot(fig, filename='plotly_sample.html')
data=[
    go.Histogram(x=iris.data[:,i], name=iris.feature_names[i],
                 xbins={'start':0, 'end':10, 'size':0.5})
      for i 
    in range(len(iris.feature_names))
]
layout = go.Layout(barmode='overlay')
fig = go.Figure(data=data, layout=layout)
fig.update_traces(opacity=0.3)
fig.show()
data=[
    go.Histogram(x=iris.data[:,i], name=iris.feature_names[i],
                 xbins={'start':0, 'end':10, 'size':0.5},
                opacity=0.8)
      for i 
    in range(len(iris.feature_names))
]
layout = go.Layout(barmode='stack')
fig = go.Figure(data=data, layout=layout)
fig.show()
matplotlibと同じように複数グラフを表示できる (参考 https://plotly.com/python/subplots/)
from plotly.subplots import make_subplots
fig = make_subplots(rows=1, cols=2)
fig.add_trace(
    go.Histogram(x=iris.data[:,0], name=iris.feature_names[0],
                 xbins={'start':0, 'end':10, 'size':0.5}),
    row=1, col=1
)
fig.add_trace(
    go.Histogram(x=iris.data[:,1], name=iris.feature_names[1],
                 xbins={'start':0, 'end':10, 'size':0.5}),
    row=1, col=2
)
fig.update_layout(title_text="plotly.subplot")
fig.show()
plotlyを楽に呼び出して使える
color にカラム名を渡せば、渡したカラムごとに色分けできるopacity など)import plotly.express as px
fig = px.histogram(df, x='petal length (cm)')
fig.show()
fig = px.histogram(dft, x='petal length (cm)', color='target_name')
fig.show()
fig = px.histogram(dft, x='petal length (cm)', color='target_name', opacity=0.3)
fig.update_layout(barmode='overlay')
fig.show()
color など細かい設定には対応していない
fig = make_subplots(rows=1, cols=2)
fig.add_trace(
    px.histogram(dft, x='petal length (cm)')['data'][0],
    row=1, col=1
)
fig.add_trace(
    px.histogram(dft, x='petal width (cm)')['data'][0],
    row=1, col=2
)
fig.update_layout(title_text="length and width")
fig.show()
import bokeh
from bokeh.io import output_notebook, show
from bokeh.plotting import figure, output_file, show
output_notebook()
# データの作成
hist, edges = np.histogram(iris.data[:,0], range=(0,10), bins=10)
edges, hist
len(hist), len(edges)
from bokeh.plotting import ColumnDataSource
source = ColumnDataSource(data=dict(
    x=edges[:-1],
    y=hist,
    name=edges[:-1],
))
p = figure(plot_height=350,
           title=iris.feature_names[0],
           toolbar_location=None,
           tools="hover",
           tooltips="x: @x y: @y"
          )
p.vbar(top='y',x='x', width=0.9, source=source)
p.y_range.start = 0
show(p)