はじめに
棒グラフはbar、折れ線はscatter、3Dのサーフェスはsurface。グラフの種類が変わるたびに描画ロジックを一から書き直していませんか。D3.jsで自由にチャートを組めるのは魅力的ですが、軸のスケール計算やホバー時のツールチップ、ズーム操作まで自前で実装するのは骨が折れます。
Plotly.jsは、この「グラフごとに実装が変わる」問題を解決してくれるJavaScriptのチャートライブラリです。dataとlayoutという2つのJSONを渡すだけで、棒グラフから3Dサーフェス、ヒストグラムまで同じAPI形式で描画できます。ズーム・パン・ホバー・PNGエクスポートといった操作も標準で付いてくるため、UIをゼロから組む必要がありません。
実際に触ってみると理解が早いので、先に挙動を見たい方はこちらからどうぞ。
Plotly.jsとは
Plotly.jsは、内部でD3.jsとWebGLベースのstack.glを使って描画を行うオープンソースのチャートライブラリです。PythonやRで有名なPlotlyのJavaScript版にあたり、可視化ツールDashのグラフ描画エンジンとしても採用されています。
主な特徴
- 宣言的なAPI -
data(描画データ)とlayout(見た目の設定)というJSONオブジェクトを渡すだけでグラフが完成する - 40種類以上のグラフタイプ - 棒グラフ・折れ線・散布図といった基本形から、ヒストグラムなどの統計チャート、3Dサーフェス、地図、金融チャートまで同じ関数で扱える
- 標準搭載のインタラクション - ズーム・パン・ホバーでの値表示・凡例クリックでの系列切り替え・PNG書き出しが追加コードなしで動く
- 大量データにも強い - WebGLを使う
scatterglなどのトレース型を選べば、数万点のデータでも滑らかに描画できる
インストール
npm install plotly.js
バンドラーを使わずブラウザで直接読み込みたい場合は、ビルド済みのplotly.js-distまたは軽量版のplotly.js-dist-minが便利です。
npm install plotly.js-dist-min
なお、フル機能版のplotly.jsはサイズが大きいため、地図や3Dなど使わないチャート種別がある場合はplotly.js-basic-dist(基本チャートのみ)やplotly.js-cartesian-dist(2Dのみ)といった部分バンドルを選ぶとバンドルサイズを抑えられます。
Plotly.jsのサンプルを動かす
下のフォームでは、Plotly.react()を使ってグラフの種類とデータをその場で切り替えられます。セレクトボックスで「棒グラフ」と「折れ線グラフ」を切り替え、ボタンを押すとランダムなデータで再描画します。
要点はPlotly.newPlot(要素ID, data配列, layout)の3引数です。
Plotly.newPlot('chart', [
{ x: ['月', '火', '水'], y: [42, 58, 35], type: 'bar' }
], {
title: { text: '曜日別アクセス数' },
})
実際に動かせるのが下のサンプルです。セレクトを切り替えたり「データを更新」を押したりすると、その場でグラフが変わります。
typeを'bar'から'scatter'に変えるだけでグラフの種類が切り替わっているのがポイントです。xとyの形式はそのままに、typeプロパティだけを差し替えれば別のチャートになる、というのがPlotly.jsの基本的な考え方です。
基本的な使い方
最小構成では、データ配列とレイアウトをPlotly.newPlotに渡すだけでグラフが完成します。
import Plotly from 'plotly.js-dist-min'
const data = [
{ x: [1, 2, 3, 4], y: [10, 15, 13, 17], type: 'scatter', mode: 'lines+markers' },
]
const layout = {
title: { text: 'シンプルな折れ線グラフ' },
xaxis: { title: { text: 'X軸' } },
yaxis: { title: { text: 'Y軸' } },
}
Plotly.newPlot('chart-div', data, layout, { responsive: true })
第4引数のconfigでresponsive: trueを指定すると、親要素のサイズ変更に合わせてグラフも自動でリサイズされます。dataは複数のトレース(系列)を配列で並べられるため、比較用のグラフもこの延長で組めます。
実践的なユースケース
複数系列を重ねて比較する
data配列に複数のトレースを並べると、それぞれが凡例付きで重なって描画されます。トレースにnameを指定すれば凡例に表示され、クリックすると表示・非表示を切り替えられます。
Plotly.newPlot('chart', [
{ x: months, y: thisYear, name: '今年', type: 'scatter', mode: 'lines+markers' },
{ x: months, y: lastYear, name: '前年', type: 'scatter', mode: 'lines+markers', line: { dash: 'dot' } },
])
下のサンプルでは「前年を強調」ボタンを押すと、Plotly.restyle()で前年の系列だけ不透明度を変えてハイライトします。凡例をクリックして系列自体の表示・非表示も試してみてください。
hovertemplateを使うと、ホバー時に表示する文字列を系列ごとに細かく指定できます。凡例クリックとボタンの2つの操作を組み合わせても、内部状態がずれずに描画が更新される点がPlotly.jsの扱いやすさです。
分布を可視化する
type: 'histogram'を使えば、生の数値配列を渡すだけでビン分けから集計まで自動で行われます。xbins.sizeでビンの幅を指定できます。
Plotly.newPlot('chart', [
{ x: values, type: 'histogram', xbins: { size: 5 } },
])
下のサンプルでは、スライダーでビン幅を変更するとPlotly.react()でヒストグラムが即座に再描画されます。
ビン幅を1に近づけると分布の細かい凹凸が見え、20に近づけると大まかな山の形だけが残ります。xbinsの値ひとつで集計の粒度をコントロールできるのがポイントです。
3Dグラフでサーフェスを描く
type: 'surface'を指定すると、2次元配列のzデータから3Dの曲面グラフを描画できます。内部ではWebGLベースのstack.glが使われるため、格子点が多くても滑らかに動きます。
Plotly.newPlot('chart', [
{ z: zMatrix, type: 'surface' },
], {
scene: { zaxis: { title: { text: '高さ' } } },
})
スライダーで波の周波数を変えると、サーフェスの形状がリアルタイムに変わります。マウスでドラッグすると視点も回転できます。
zは単なる数値の2次元配列なので、センサーデータや数式の計算結果をそのまま渡すだけで曲面になります。周波数を8まで上げると波の数が増え、1まで下げるとなだらかな一枚の面に近づく様子が確認できます。
まとめ
Plotly.jsは、dataとlayoutという共通のJSON形式を軸に、棒グラフから3Dサーフェスまで同じ書き方で扱えるチャートライブラリです。ズーム・ホバー・凡例トグルといった操作が標準で付いてくるため、インタラクティブなグラフをゼロから実装する手間を大きく減らせます。
まずは手元のデータをPlotly.newPlot()に渡すところから始めて、typeを変えながら自分のユースケースに合うチャートを探してみてください。
