インタラクティブなグラフ生成ライブラリPlotlyについて

Plotly Python データビジュアライゼーション

Plotlyというマウスでぐりぐりと動かせるようなインタラクティブなグラフを生成するライブラリがあります。

最近これをよくJupyter notebook上で可視化して遊んだりしていますので、これについてまとめます。

Pythonで使えるインタラクティブなグラフ生成ライブラリ

Plotlyの話に入る前に、Pythonで主にアドホックな分析でJupyter notebookなどで実行する環境におけるグラフ生成について少し整理します。

静的なグラフ生成に関しては、やはりMatplotlibが一番使われていることは周知なことかと思います。

他には、MatplotlibをラッパーとするPandasの可視化機能やSeabornなどが、よく使われるかと思います。

一方、インタラクティブなグラフ生成に関しては、個人的な印象としては、以下のどちらかのライブラリが使われているのかなという印象を持っています。

Bokehは、「ボケ」と読むのかな?と思ったら、本当に「ボケ」と呼ぶそうです。

Plotlyもそうですが、BokehもJupyter notebook上でグラフを生成し、対話的にグラフを動かしたりすることができます。

また、作成したグラフはHTML化でき、他人に共有したり、Webに埋め込んだりすることができるみたいです。

Bokehに関しては、以下の書籍で詳しい使い方が書いてありますのでオススメです。

Jupyter notebookの使い方の書籍ですが、Jupyter上におけるグラフ生成において、MatplotlibとBokehのことを基礎編・応用編とそれぞれ章立てで説明されています。

一方、今回紹介するPlotlyに関しては、こちらもJupyter notebook上で、マウスで動かしたりできるグラフを生成することができます。

どちらもPythonから実行できますが、他にもRやJuliaなどの他言語からも実行可能のようです。

逆に異なる点としては、Plotlyには3Dプロットのグラフもありますが、Bokehには3Dプロットがありません。

また、詳しくは後述しますが、Plotlyにはオンラインとオフライン、無償版と有償版の使い分けがあります。

また、今回は紹介しませんが、このPlotlyのグラフ生成機能をベースとしたWebアプリケーションフレームワークとして『Dash』というものもあるみたいです。

Dash: https://plot.ly/products/dash/

Plotly

Plotlyは、インタラクティブなグラフを作成できるオープンソースのライブラリです。

Python、R、JavaScript、MATLAB、Ruby、Go、Julia...など、様々な言語から実行できます。

グラフの作成はD3.jsをベースとしており、作成できるグラフは、Scatter、Bar、Line、Pie、Histogram、Box、Heatmap、Candlestick、Map、3D Scatter、3D Surfaceなど、多岐にわたります。

できる可視化に関しては、公式のギャラリーのページ(https://plot.ly/python/)を見ると良いです。

以前は3Dプロットは有償でしたが、現在は無償で利用可能となったようです。

また、Plotlyの最も特徴的な仕組みの一つとして、オンライン・オフラインで実行可能です。

オンラインでは、Plotlyのサイトでアカウント登録が必要になり、Plotlyのアカウントのサイトでグラフの作成やアップロードなどができるようになります。

アップロードできる数などに制限はあるみたいですが、有償版にすることで拡張などができます。

また、アップロードされたグラフは基本的にPublicでWeb上で公開されますが、有償にすることでPrivateにすることができます。

一方、オフラインでは、アカウントの登録は不要で、他のライブラリと同様に、ライブラリを普通にインストールして利用することができます。

正直なところ、個人的に使うにはオフラインで十分ですが、当方のように、オンラインで作成して、ブログから直接グラフをマウスで触ってもらうみたいな使い方はできます。

使い方

ライブラリのインストールは、オンライン・オフラインのいずれも pip install plotly でOKです。

オンラインで利用する場合

オンラインで実行する場合は、Plotlyのサイトでアカウント登録が必要になります。

Log in / Sign up: https://plot.ly/accounts/login/?next=%2Fsettings

アカウント登録しログインをすると、アカウントのSettings > API Keys > API Settingsのページにて、APIキーの作成ができます。

こちらでキーを作成した後にJupyter notebook上で、以下で初期化を行います。

import plotly
plotly.tools.set_credentials_file(username='', api_key='')

上記を実行すると、ローカルの端末に以下のファイルが作成されます。

$ cat ~/.plotly/.credentials
{
    "proxy_username": "",
    "stream_ids": [],
    "api_key": "",
    "proxy_password": "",
    "username": ""
}

こちらの情報を読み込んでグラフ作成&アップロードを行う仕組みになっていますので、APIキーを更新した時などには、必ず上記の初期化も行います。

この状態でグラフを作成すると、グラフが自動でアカウントのページにアップロードもされるようになります。

import numpy as np

x = np.random.normal(loc=0, scale=1, size=300)
y = np.random.normal(loc=0, scale=1, size=300)

trace = plotly.graph_objs.Scatter(x=x, y=y, mode='markers')
data = [trace]
layout = plotly.graph_objs.Layout(title='Sample')
figure = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.plotly.iplot(figure)

※Jupyter上では、マウスでぐりぐり動かせる

上記のように、作成されたグラフへのパスも表示されます。

パスへ飛ぶと、アカウントサイトで同じグラフを確認することができます。

また、グラフのページの下部にある、Shareing Link > Embedで表示されるコードをブログなどに貼り付けることで、ブログ上にインタラクティブなグラフを作成することができます。

以下はマウスで実際に動かすことができますので、触ってみて頂ければと思います。

軸をドラッグして視点移動、凡例をクリックして表示・非表示、グラフ上の任意の領域をドラッグして矩形範囲選択、ダブルクリックで初期状態に戻るなど、操作が行えます。

3Dプロットの場合は、マウスホイールで拡大・縮小ができます。

オフラインで利用する場合

オフラインで利用する場合は、普通にライブラリをimportし、以下の初期化を行います。

import plotly
plotly.offline.init_notebook_mode()

あとは、オンラインの時と同様に、以下のようにグラフを作成して表示すれば、Jupyter notebook上でマウスでぐりぐりと動かせるグラフを作成することができます。

import numpy as np

x = np.random.normal(loc=0, scale=1, size=300)
y = np.random.normal(loc=0, scale=1, size=300)

trace = plotly.graph_objs.Scatter(x=x, y=y, mode='markers')
data = [trace]
layout = plotly.graph_objs.Layout(title='Sample')
figure = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.offline.iplot(figure)

表示の時のメソッドが

  • オンライン: plotly.plotly.iplot
  • オフライン: plotly.offline.iplot

と異なる点のみ注意です。

オフラインで実行する場合はこれだけです。

まとめと可視化サンプル

以上、Plotlyの紹介でした。

オフラインは、普通にライブラリを使っているのと変わりませんので、個人的に使うだけであれば、オフラインで十分かと思います。

以下、オンラインで適当にグラフを作成してみました。

マウスで動かせるので、触ってみていただければと思います。

x = list(range(300))
y0 = np.random.normal(loc=5, scale=1, size=300)
y1 = np.random.normal(loc=0, scale=1, size=300)
y2 = np.random.normal(loc=-5, scale=1, size=300)

trace0 = plotly.graph_objs.Scatter(x=x, y=y0, name='lines', mode='lines')
trace1 = plotly.graph_objs.Scatter(x=x, y=y1, name='lines+markers', mode='lines+markers')
trace2 = plotly.graph_objs.Scatter(x=x, y=y2, name='markers', mode='markers')
data = [trace0, trace1, trace2]
layout = plotly.graph_objs.Layout(title='Sample 2')
figure = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.plotly.iplot(figure)

x = list(range(30))
y0 = np.random.normal(loc=3, scale=1, size=30)
y1 = np.random.normal(loc=0, scale=1, size=30)

trace0 = plotly.graph_objs.Bar(x=x, y=y0, name='A')
trace1 = plotly.graph_objs.Bar(x=x, y=y1, name='B')
data = [trace0, trace1]
layout = plotly.graph_objs.Layout(title='Sample 3')
figure = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.plotly.iplot(figure)

labels = ['A', 'B']
values0 = np.random.multinomial(n=1000, pvals=[0.3, 0.7])
values1 = np.random.multinomial(n=1000, pvals=[0.2, 0.8])
values2 = np.random.multinomial(n=1000, pvals=[0.1, 0.9])

colors = ['rgb(100, 200, 100)', 'rgb(200,200,200)']
trace0 = plotly.graph_objs.Pie(labels=labels, values=values0, textinfo='value', sort=False, domain=dict(x=[0, 0.3]), marker=dict(colors=colors))
trace1 = plotly.graph_objs.Pie(labels=labels, values=values1, textinfo='value', sort=False, domain=dict(x=[0.35, 0.65]), marker=dict(colors=colors))
trace2 = plotly.graph_objs.Pie(labels=labels, values=values2, textinfo='value', sort=False, domain=dict(x=[0.7, 1]), marker=dict(colors=colors))
data = [trace0, trace1, trace2]
layout = plotly.graph_objs.Layout(title='Sample 4')
figure = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.plotly.iplot(figure)

N = 30
colors = ['hsl('+str(h)+',50%'+',50%)' for h in np.linspace(0, 360, N)]

data = [plotly.graph_objs.Box(
    y=3.5*np.sin(np.pi * i/N) + i/N+(1.5+0.5*np.cos(np.pi*i/N))*np.random.rand(10),
    marker=dict(color=colors[i])
) for i in range(N)]
layout = plotly.graph_objs.Layout(title='Sample 5')
figure = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.plotly.iplot(figure)

z = np.random.normal(loc=0, scale=1, size=(15, 15))
trace = plotly.graph_objs.Heatmap(z=z, colorscale='Viridis')
data = [trace]
layout = plotly.graph_objs.Layout(title='Sample 6', width=600, height=600)
figure = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.plotly.iplot(figure)

x, y, z = np.random.multivariate_normal(np.array([0, 0, 0]), np.eye(3), 300).transpose()

trace0 = plotly.graph_objs.Scatter3d(x=x, y=y, z=z, mode='markers', marker=dict(size=3, color=z, colorscale='Viridis', opacity=0.8))
data = [trace0]
layout = plotly.graph_objs.Layout(title='Sample 7')
figure = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.plotly.iplot(figure)

z = np.abs(np.random.normal(loc=0, scale=1, size=(10,10)))

trace = plotly.graph_objs.Surface(z=z)
data = [trace]
layout = plotly.graph_objs.Layout(title='Sample 8', autosize=False, width=600, height=600)
figure = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.plotly.iplot(figure)

コメント