高度な対話型の可視化が可能なPlotlyを紹介する。

インストール方法

本家サイトを参照.

https://plotly.com/python/getting-started/

図の構成要素

Plotlyにおける図は、以下のように構成される。

  • トレース: 基本描画単位(散布図 Scatter, 線グラフ Line, 棒グラフ Bar などのオブジェクト)
  • データ: トレースのリスト(複数のトレースを1つの画面に描画)
  • レイアウト: 図のレイアウト.タイトル,軸などを定義
  • 図: データとレイアウトから構成されるオブジェクト

データ以外は,辞書(の辞書)から成るデータ構造で保管される.

描画

  • plotly.offline.plot(図オブジェクト) でhtmlファイルを出力(と同時にブラウザで表示)
  • 図オブジェクト.show() でJupyterのノートに出力 (Jupyter Labのバージョンによっては表示されない.)

Plotlyの図はmatplotlibのような画像ではないので,拡大・縮小だけでなく,トレースの選択やマウスオーバーでデータの表示などの対話型の操作ができる.

import plotly
import plotly.graph_objs as go

# Google Colab.でプロットするためには, 以下を実行する.
# import plotly.io as pio
# pio.renderers.default = "colab"

最初の例

最初の例題として、簡単な散布図 (scatter plot)を描画する。グラフオブジェクト goScatter クラスを用いる.

引数は以下の通り.

  • x: $x$ 軸の要素を表すリスト
  • y: $y$ 軸の要素を表すリスト
  • mode: マーカー(点) markers か,線 linesか,両方かを指定する
  • text: マウスオーバー時のテキストを指定
  • marker: マーカーの属性を指定した辞書.大きさ size,線の属性を表す辞書 line,色 color,透過度 opacity などを指定する
trace = go.Scatter(
    x=[1, 2, 3, 4, 5],
    y=[10, 20, 30, 20, 10],
    mode="markers + lines",
    text=["A", "B", "C", "D", "E"],
    marker=dict(size=14, line=dict(width=1), color="red", opacity=0.3),
)

data = [trace]

fig = go.Figure(data)
plotly.offline.plot(fig);

2つ以上のトレースをもつ図も、同様に作ることができる。 トレースをクリックすることによって選択が可能であり, マウスを点の上に移動させると,データが表示されることを確認しよう.

trace1 = go.Scatter(
    x=[1, 2, 3, 4, 5],
    y=[10, 20, 30, 20, 10],
    mode="markers + lines",
)

trace2 = go.Scatter(
    x=[1, 2, 3, 4, 5],
    y=[20, 20, 20, 20, 20],
    mode="markers + lines",
)

data = [trace1, trace2]

layout = go.Layout(
    title="2つのトレース",
)

fig = go.Figure(data, layout)
plotly.offline.plot(fig);

棒グラフの例

次の例として,2つのトレースをもつ棒グラフを描画してみよう.

1つの棒には身長を,もう1つの棒には体重のデータを表示する.これには2つのトレースを作成し,それを入れたデータリストを作ればよい.

トレースの名称は,引数 name で指定する.

trace1 = go.Bar(x=["Sara", "Kitty", "Mickey"], y=[160, 64, 83], name="Height")
trace2 = go.Bar(x=["Sara", "Kitty", "Mickey"], y=[60, 12, 20], name="Weight")

data = [trace1, trace2]

fig = go.Figure(data)
plotly.offline.plot(fig);

レイアウト

上で作成した棒グラフにレイアウト情報を付加しよう.

ここでは積み上げ棒グラフにして,さらに表題を追加する.

layout = go.Layout(
    barmode="stack",
    title="身長と体重",
)
fig = go.Figure(data=data, layout=layout)
plotly.offline.plot(fig);

ヒストグラム

データフレームに保管されているデータをヒストグラム(histgram; 度数分布表)として描画してみよう.

例題として用いるのはiris(アヤメ)のデータである.

まずはpandasを用いてデータを読んでおく.

import pandas as pd

iris = pd.read_csv(
    "http://logopt.com/data/iris.data",
    names=["sepal length", "sepal width", "petal length", "petal width", "class"],
)
iris.head()
sepal length sepal width petal length petal width class
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa

アヤメのデータのヒストグラムを描画する.

ヒストグラムは数値データの分布の概要を知る際に便利である.

# グラフオブジェクトの Histgram クラスを用いて,インスタンスを生成する.
trace1 = go.Histogram(x=iris["sepal length"], opacity=0.75, name="sepal length")
trace2 = go.Histogram(x=iris["sepal width"], opacity=0.75, name="sepal width")
trace3 = go.Histogram(x=iris["petal length"], opacity=0.75, name="petal length")
trace4 = go.Histogram(x=iris["petal width"], opacity=0.75, name="petal width")

# データはトレースのリストである.
data = [trace1, trace2, trace3, trace4]

# レイアウトでグラフのタイトルや軸名を設定する.
# レイアウトも辞書(の辞書)のような形式である.
layout = go.Layout(
    title="Iris Histgram", xaxis=dict(title="Length/Width"), yaxis=dict(title="Count")
)

# 図(Figure)はデータとレイアウトを合わせたオブジェクトである.
fig = go.Figure(data=data, layout=layout)
plotly.offline.plot(fig);

問題(ポケモン)

以下のように読み込んだポケモンのデータフレームを用いて,攻撃力(Attack),守備力(Defense)のヒストグラムを描画せよ (ヒント:データフレームから一部の列を切り出す方法については,pandasの練習問題を参考にせよ).

pokemon = pd.read_csv("http://logopt.com/data/poke.csv", encoding="utf-8", index_col=0)
pokemon.head()
Name Type 1 Type 2 Total HP Attack Defense Sp. Atk Sp. Def Speed Generation Legendary Japanese
#
1 Bulbasaur Grass Poison 318 45 49 49 65 65 45 1 False フシギダネ
2 Ivysaur Grass Poison 405 60 62 63 80 80 60 1 False フシギソウ
3 Venusaur Grass Poison 525 80 82 83 100 100 80 1 False フシギバナ
3 VenusaurMega Venusaur Grass Poison 625 80 100 123 122 120 80 1 False フシギバナ・メガ進化
4 Charmander Fire NaN 309 39 52 43 60 50 65 1 False ヒトカゲ

問題 (SAT,GPA)

http://logopt.com/data/SATGPA.csv データを読み込み,2種類のSATの成績とGPAのヒストグラムを描画せよ.

散布図

散布図(scatter plot) は,2つの数値データの関係を知る際に便利である.

グラフオブジェクトとしてはScatterクラスを用いる.

引数の x と y で $x,y$ 軸に使用するデータ(データフレームの列名)を指定する.

ここではがく片長 sepal lengthと花びら長 petal length の関係を図示してみる.

trace = go.Scatter(
    x=iris["sepal length"],
    y=iris["petal width"],
    mode="markers",
)

layout = go.Layout(
    title="Iris Scatter",
    xaxis=dict(title="sepal length"),
    yaxis=dict(title="petal width"),
)
data = [trace]
fig = go.Figure(data, layout)
plotly.offline.plot(fig);

問題(車)

http://logopt.com/data/auto-mpg.data から車のデータを読み込み, 重さ weight と燃費 mpg の散布図を描け.

L = [
    "mpg",
    "cylinders",
    "displacement",
    "horsepower",
    "weight",
    "acceleration",
    "year",
    "origin",
    "name",
]
car = pd.read_csv(
    "http://logopt.com/data/auto-mpg.data", delim_whitespace=True, names=L
)

問題(ダイヤモンド)

http://logopt.com/data/Diamond.csv からダイアモンドの価格データを読み込み,カラット carat と価格 price の散布図を描け.

問題(ポケモン)

ポケモンのデータフレームに対して,攻撃力(Attack),守備力(Defense)の関係を散布図に描画せよ.

pokemon = pd.read_csv("http://logopt.com/data/poke.csv", encoding="utf-8", index_col=0)
pokemon.head()
Name Type 1 Type 2 Total HP Attack Defense Sp. Atk Sp. Def Speed Generation Legendary Japanese
#
1 Bulbasaur Grass Poison 318 45 49 49 65 65 45 1 False フシギダネ
2 Ivysaur Grass Poison 405 60 62 63 80 80 60 1 False フシギソウ
3 Venusaur Grass Poison 525 80 82 83 100 100 80 1 False フシギバナ
3 VenusaurMega Venusaur Grass Poison 625 80 100 123 122 120 80 1 False フシギバナ・メガ進化
4 Charmander Fire NaN 309 39 52 43 60 50 65 1 False ヒトカゲ

その他のグラフ

他にも色々なグラフが容易に描画できる.

詳細については、本家サイト https://plotly.com/python/ を参照されたい.