インタラクティブなグラフを作るライブラリPlotlyのご紹介

この記事は約13分で読めます。

データの可視化は、データサイエンスやビジネス分析において必要不可欠です。
今日ではPythonにも様々なグラフ作成ライブラリがあり、ユーザーは適宜状況により適切なライブラリを選択することが重要です。

その中でも今回は、インタラクティブなグラフを作成しデータの洞察をより深めることができるPlotlyというデータ可視化ライブラリについて紹介します。
Plotlyの特長と基本的な使い方について紹介しますので、データの可視化に興味のある方はぜひお読みください。

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

Plotlyの紹介に入る前に、Jupyter notebookなどを用いたアドホックな分析環境におけるグラフ生成について少し整理します。

静的なグラフ生成に関しては、やはりMatplotlibが一番使われていることは周知のことかと思います。
他には、MatplotlibをラッパーとするPandasの可視化機能や、Seabornがよく使われているかと思います。

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

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

BokehもJupyter notebook上でグラフを生成しインタラクティブに動かしたりすることができるライブラリです。
作成したグラフはHTML化でき他人に共有したりWebに埋め込んだりすることもできます。
今回はPlotlyについて紹介しようと思っていますので、Bokehに関しては以下の書籍で詳しい使い方が書いてありますので参考にしてください。

一方、PlotlyもJupyter notebook上でマウスで動かしたりできるグラフを生成することができます。
また同様にHTML化してWebに埋め込んだりなども可能です。
実際にこの後、記事の中に埋め込んだものもご紹介します。

PlotlyもBokehもどちらも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など、多岐にわたります。
ちなみに以前は3Dプロットは有料オプションでしたが、現在は無料で利用可能となったようです。
作成できるグラフの種類は以下の公式のギャラリーページを参考にすると良いでしょう。

また、Plotlyの最も特徴的な仕組みの一つとして、オンライン・オフラインで実行できるモードがあることです。
オンラインでは、Plotlyのサイトで無料アカウント登録することで、Plotlyのご自身のアカウントページでグラフ作成やグラフをアップロードすることができるようになります。
無料版だとアップロードできる数などに制限はあるみたいですが、有料版にすることで拡張可能です。
ここは注意が必要ですが、無料版で使っている限りはアップロードしたグラフは基本的にPublicでWeb上で公開されます。
有料版にすることでPrivateにすることができます。

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

それでは、早速使い方について見てみます。

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

アカウント登録しログインをすると、アカウントの Settings > API Keys > API Settings のページにてAPIキーの作成ができます。
こちらでキーを作成した後にJupyter notebook上で、以下で初期化を行います。

import plotly
plotly.tools.set_credentials_file(username='YOUR USER NAME', api_key='YOUR API KEY')

上記を実行すると、ローカルの端末にファイル~/.plotly/.credentialsが作成されます。
このファイルの情報を読み込んでグラフ作成&アップロードを行う仕組みになっていますので、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上では、マウスでぐりぐり動かせるグラフが生成できており、同時に、上記のように作成されたグラフへのURLも表示されます。
URLに飛ぶとアカウントページで同じグラフを確認することができます。
アカウントページのグラフのページの下部にある、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)

まとめ

以上、今回はPlotlyを使ってインタラクティブなグラフを作成する方法について紹介しました。
実務においても、静的なグラフだけでなく、マウスオーバーやズームなどのインタラクティブな要素を追加することで、その場でデータを深く掘り下げることができます。
ぜひ皆さんの勉強や実践において、本記事が役立つことがあれば幸いです。