【Dash Plotlyの使い方 基本編】Plotlyで描く折れ線グラフ徹底まとめ

皆さん、こんにちは!

今回は、Pythonのデータ可視化ライブラリの1つである、Dash Plotlyを学んでいきます。

この記事では、Plotlyで描画出来る円グラフをまとめています。

Plotyの関連記事はこちらです。今後もコンテンツを増やしていく予定ですのでお楽しみに!

【Dash Plotlyの使い方 基本編】Plotlyで描く円グラフ徹底まとめ

Plotlyとは?

Plotlyはインタラクティブなオープンソースのデータ可視化ライブラリで、統計、財務、地理、科学、3次元データなど幅広いタイプの可視化をカバーする40種類上のグラフタイプが実装可能です。

Plotlyは元々はJavaScriptライブラリ(plotly.js)上に構築されているため、基本的にはPythonコードのみでデータがヌルヌル動くようなダッシュボードを構築できます。

JupyterNotebook上や、Webアプリケーションの一部としてWebベースの可視化も可能です。

Webベースの可視化を行うためには、Dash(中身はFlask)を使う必要があるため、PlotlyとDashはセットで扱われることも多いです。

https://plotly.com/dash/industry-app-catalog/

Plotly Expressとは?

Plotly ExpressはPlotlyの中でも非常にカンタンに可視化が出来るライブラリです。

この記事では、特に断りが無い限りは、Plotly Expressを使います。

基本の折れ線グラフ

折れ線グラフでもPlotly Expressを使うことが出来ます。

折れ線グラフを描画する場合、px.line関数を用います。xとyにそれぞれデータを渡せば最低限グラフを描くことが出来ます。

データさえあれば、必要な行数はわずか1行です。

まずはライブラリをインポートし、データセットをダウンロードします。

今回は、Plotlyの標準で用意されているgapminderのデータセットを使います。

このデータセットには、世界の国々の平均寿命や、人口、GDPなど基本的なデータが年度別に含まれています。

折れ線グラフは時系列データを描画するのが得意ですので、ピッタリかと思います。

import plotly.express as px

# gapminderデータセットをダウンロード
df = px.data.gapminder().query("country=='Japan'")
df

折れ線グラフを描画するには、px.line()関数を用います。

xにx軸、yにy軸のデータを与えると、以下のように折れ線グラフを描画できます。

fig = px.line(df, x="year", y="lifeExp", title='Life expectancy in Japan')
fig.show()

しっかりとPlotlyのツール群(画像保存や拡大縮小)も設定されており、マウスホバーでも値が表示されています。

折れ線グラフの色を指定

次は折れ線グラフを複数表示し、色分けを行っていきます。

色を分けるにはデータ以前と同じく、データフレームを渡して、Color変数に列名を指定するだけです。

この列名のユニーク値を元にグラフが描画されます。(なので、あらかじめ表示したいデータのみを含むデータフレームとなるよう調整が必要。)

import plotly.express as px

df = px.data.gapminder().query("continent=='Oceania'")
# 折れ線グラフを描画、データフレームを渡して、colorを変更するための変数を指定すればOK
fig = px.line(df, x="year", y="lifeExp", color='country')
fig.show()

マーカー付き折れ線グラフ

次はマーカー付き折れ線グラフです。

マーカーを付けるには、markers引数をTrueとするだけでOKです。

import plotly.express as px
df = px.data.gapminder().query("continent == 'Oceania'")
# markers = TrueとすればOK
fig = px.line(df, x='year', y='lifeExp', color='country', markers=True)
fig.show()

ちなみに、シンボル(上記図では●)を変更するには、graph objectsを変更する必要があるので、Plotly Expressでは難しいです。

X軸のソートをして折れ線グラフを表示

PlotlyではX軸とY軸に与えるデータセットでグラフ描画を行います。

そのためX軸に関してはあらかじめソートしておかないと不自然なグラフになってしまいます。

X軸でソートをかけない場合とかけた場合の結果を確認します。

ソートの際は、df.sort_valuesを用いて、X軸に相当する列を選択して実施します。

import plotly.express as px
import pandas as pd

# x軸が昇順に並んでないデータセットを用意
df = pd.DataFrame(dict(
    x = [1, 3, 2, 4],
    y = [1, 2, 3, 4]
))
# ソートせずに描画する
fig = px.line(df, x="x", y="y", title="Unsorted Input") 
fig.show()

# ソートして描画する
df = df.sort_values(by="x")
fig = px.line(df, x="x", y="y", title="Sorted Input") 
fig.show()

他のデータと連動した折れ線グラフ

次は折れ線グラフに対して、別のデータを載せるというものです。

text引数に対して、書き込みたい列を指定することで表示が可能です。

import plotly.express as px

df = px.data.gapminder().query("country in ['Canada', 'Botswana']")

# textに相当する列を選択することで、グラフにデータを書き込むことが可能
# oclor に指定した列名で、グラフが分割される
fig = px.line(df, x="lifeExp", y="gdpPercap", color="country", text="year")
fig.update_traces(textposition="bottom right")
fig.show()

X軸を日付とした場合の折れ線グラフ

折れ線グラフが得意とするのが時系列表示です。

Plotlyで時系列をグラフを描画するには、データフレームから日付が記載されている列を選択すればOKです。

「2022-06-19」のように「ハイフン」で区切られたデータであれば、date型でなくても自動で、表記を変更してくれます。

import plotly.express as px

df = px.data.stocks()
fig = px.line(df, x='date', y="GOOG")
fig.show()

Graph Object

次はGraph Objectを用いて、折れ線グラフを書いていきます。

Graph ObjectはPlotlyでグラフを描画するための一番基本的な要素です。

いままで扱ってきたPlotly Expressは内部でイイ感じにGraph Objectを使って描画してくれています。

こちらを使うことで、Plotlyのグラフを細部までカスタマイズすることが出来ます。

Graph Objectを用いた基本のグラフ

Graph Objectを用いてグラフを書く場合、go.Figure関数を用います。

折れ線グラフを描画する場合、go.scatter関数を用いるため、go.Figure関数のdata引数に対してデータを与えます。

また、Graph Objectの場合は、lineではなく、Scatterとなります。Scatterですが、デフォルトは折れ線グラフになります。

import plotly.graph_objects as go
import numpy as np

x = np.arange(10)

# Graph Objectを用いて折れ線グラフを描画する
fig = go.Figure(data=go.Scatter(x=x, y=x**3 - 50*x))
fig.show()

複数のグラフを表示

次は、1つのFirgure要素の中に対して、複数のグラフを描画してきます。

先ほどと同じように、Figure要素を作成しておき、内部に要素を追加していくイメージです。

要素の追加には、add_trace関数を用います。

先ほどデフォルトが折れ線と言いましたが、単純な散布時に変更したい場合、mode = “lines”とすればOKです。

import plotly.graph_objects as go

# Create random data with numpy
import numpy as np
np.random.seed(1)

N = 100
random_x = np.linspace(0, 1, N)
random_y0 = np.random.randn(N) + 5
random_y1 = np.random.randn(N)
random_y2 = np.random.randn(N) - 5

# Create traces
fig = go.Figure()
fig.add_trace(go.Scatter(x=random_x, y=random_y0,
                    mode='lines',
                    name='lines'))
fig.add_trace(go.Scatter(x=random_x, y=random_y1,
                    mode='lines+markers',
                    name='lines+markers'))
fig.add_trace(go.Scatter(x=random_x, y=random_y2,
                    mode='markers', name='markers'))

fig.show()

None値を含んだグラフ

PlotlyではYの値にNaNやNone値を含んだグラフであっても描画が可能です。

※Plotly Expressでも欠損値のあるグラフは描画可能です。

実際のデータでは、欠損値が、None(NaN)で入っていればいいですが、「9999」や「-」など様々だと思います。

そんな時は、データフレームに対して以下を適用すればOKです。

df = df.replace([“-“], [None])

これで、「-」と書かれているデータをNoneとすることができます。

import plotly.graph_objects as go

x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

fig = go.Figure()
# add_traceでscatterplotを追加
# 欠損値はNoneであればOK(-などは事前にNoneに変更しておく必要あり)
fig.add_trace(go.Scatter(
    x=x,
    y=[10, 20, None, 15, 10, 5, 15, None, 20, 10, 10, 15, 25, 20, 10],
    name = '<b>No</b> Gaps', # Style name/legend entry with html tags
    connectgaps=True # override default to connect the gaps
))
fig.add_trace(go.Scatter(
    x=x,
    y=[5, 15, None, 10, 5, 0, 10, None, 15, 5, 5, 10, 20, 15, 5],
    name='Gaps',
))

fig.show()

ステップ関数を描く

次はあまり使う機会はないかもしれませんが、折れ線グラフの補間についてです。

デフォルトは線形補間になっているので、直線で線を引いてくれますが、これをスプライン曲線やステップ関数などに変えることが可能です。

import plotly.graph_objects as go
import numpy as np

x = np.array([1, 2, 3, 4, 5])
y = np.array([1, 3, 2, 3, 1])

fig = go.Figure()
# データ間を線形補間
fig.add_trace(go.Scatter(x=x, y=y, name="linear",
                    line_shape='linear'))
# データ間をスプライン曲線補間
fig.add_trace(go.Scatter(x=x, y=y + 5, name="spline",
                    text=["tweak line smoothness<br>with 'smoothing' in line object"],
                    hoverinfo='text+name',
                    line_shape='spline'))
# 以下はステップ関数のバリエーション vh がいわゆるステップ関数
fig.add_trace(go.Scatter(x=x, y=y + 10, name="vhv",
                    line_shape='vhv'))
fig.add_trace(go.Scatter(x=x, y=y + 15, name="hvh",
                    line_shape='hvh'))
fig.add_trace(go.Scatter(x=x, y=y + 20, name="vh",
                    line_shape='vh'))
fig.add_trace(go.Scatter(x=x, y=y + 25, name="hv",
                    line_shape='hv'))

fig.update_traces(hoverinfo='text+name', mode='lines+markers')
fig.update_layout(legend=dict(y=0.5, traceorder='reversed', font_size=16))

fig.show()

カスタマイズした折れ線グラフ

次は折れ線グラフを更にカスタマイズしていきます。

グラフの背景色や、テキストなど細かく設定すると、ここまできれいに描画できる!という目安程度に考えれば良いと思います。

カスタマイズできる範囲は非常に幅広いですが、

  • グラフのメイン要素はadd_traceで追加
  • レイアウトはupdate_layoutで変更
  • グラフ内にテキストを挿入するときは、annotations

と覚えておけば何とかなりそうです。

折れ線グラフに限らず全てのPlotlyグラフで共通なので、ぜひ覚えておきましょう。

import plotly.graph_objects as go
import numpy as np

title = 'Main Source for News'
labels = ['Television', 'Newspaper', 'Internet']
colors = ['rgb(67,67,67)', 'rgb(115,115,115)', 'rgb(49,130,189)']

mode_size = [8, 8, 12, 8]
line_size = [2, 2, 4, 2]

x_data = np.vstack((np.arange(2001, 2014),)*3)

y_data = np.array([
    [74, 82, 80, 74, 73, 72, 74, 70, 70, 66, 66, 69],
    [45, 42, 50, 46, 36, 36, 34, 35, 32, 31, 31, 28],
    [13, 14, 20, 24, 20, 24, 24, 40, 35, 41, 43, 50],
])

fig = go.Figure()

for i in range(0, 3):
    # 折れ線グラフとして、3回ループを回し、add_trace
    # あらかじめ、ラベル名や色、データセットなども同じ順序になるように用意しておく
    fig.add_trace(go.Scatter(x=x_data[i], y=y_data[i], mode='lines',
        name=labels[i],
        line=dict(color=colors[i], width=line_size[i]),
        connectgaps=True,
    ))

    # 一番先頭と、最後のデータのみ、マーカー付きScatterで上書きする
    # x_data[i][0]は最初のデータ、[-1]は最後のデータ
    fig.add_trace(go.Scatter(
        # np.arrayデータをscatter plotに与える。
        x=[x_data[i][0], x_data[i][-1]],
        y=[y_data[i][0], y_data[i][-1]],
        mode='markers',
        marker=dict(color=colors[i], size=mode_size[i])
    ))

# 全体のレイアウトを設定する。
fig.update_layout(
    # X軸
    xaxis=dict(
        showline=True,
        showgrid=False,
        showticklabels=True,
        linecolor='rgb(204, 204, 204)',
        linewidth=2,
        ticks='outside',
        tickfont=dict(
            family='Arial',
            size=12,
            color='rgb(82, 82, 82)',
        ),
    ),
    # Y軸 線は全て描画しない。
    yaxis=dict(
        showgrid=False,
        zeroline=False,
        showline=False,
        showticklabels=False,
    ),
    autosize=False, # グラフ全体のサイズを固定する
    # 固定する際の余白などを規定
    margin=dict(
        autoexpand=False,
        l=100,
        r=20,
        t=110,
    ),
    showlegend=False,
    plot_bgcolor='white' # 背景色を白に(デフォルトはブルー)
)

annotations = []

# Adding labels
for y_trace, label, color in zip(y_data, labels, colors):
    # ラベルを左端に記載
    annotations.append(dict(xref='paper', x=0.05, y=y_trace[0],
                                  xanchor='right', yanchor='middle',
                                  text=label + ' {}%'.format(y_trace[0]),
                                  font=dict(family='Arial',
                                            size=16),
                                  showarrow=False))
    # 右端に最後のデータを描画する
    annotations.append(dict(xref='paper', x=0.95, y=y_trace[11],
                                  xanchor='left', yanchor='middle',
                                  text='{}%'.format(y_trace[11]),
                                  font=dict(family='Arial',
                                            size=16),
                                  showarrow=False))
# 左上のタイトルを記入 
annotations.append(dict(xref='paper', yref='paper', x=0.0, y=1.05,
                              xanchor='left', yanchor='bottom',
                              text='Main Source for News',
                              font=dict(family='Arial',
                                        size=30,
                                        color='rgb(37,37,37)'),
                              showarrow=False))
# データの出典を記載(グラフの真下)
annotations.append(dict(xref='paper', yref='paper', x=0.5, y=-0.1,
                              xanchor='center', yanchor='top',
                              text='Source: PewResearch Center & ' +
                                   'Storytelling with data',
                              font=dict(family='Arial',
                                        size=12,
                                        color='rgb(150,150,150)'),
                              showarrow=False))

fig.update_layout(annotations=annotations)

fig.show()

塗りつぶした折れ線グラフ

次は塗りつぶした折れ線グラフです。

1つのデータセットに対して、ある程度幅を持つようなデータ(実践で各データが平均値、上側が最大値、下側が最大値のようなイメージ)を描画するのに使えると思います。

基本的には他の折れ線グラフと同じようにデータセットが用意されていれば問題ありません。

import plotly.graph_objects as go
import numpy as np

x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
x_rev = x[::-1]

# Line 1
y1 = [5, 2.5, 5, 7.5, 5, 2.5, 7.5, 4.5, 5.5, 5]
y1_upper = [5.5, 3, 5.5, 8, 6, 3, 8, 5, 6, 5.5]
y1_lower = [4.5, 2, 4.4, 7, 4, 2, 7, 4, 5, 4.75]
y1_lower = y1_lower[::-1]

fig = go.Figure()

fig.add_trace(go.Scatter(
    x=x+x_rev,
    y=y1_upper+y1_lower,
    fill='toself',
    fillcolor='rgba(0,100,80,0.2)',
    line_color='rgba(255,255,255,0)',
    showlegend=False,
    name='Fair',
))

fig.add_trace(go.Scatter(
    x=x, y=y1,
    line_color='rgb(0,100,80)',
    name='Fair',
))

fig.update_traces(mode='lines')
fig.show()

まとめ

以上、Plotlyの円グラフについてまとめていきました。

最小で3行程度でインタラクティブなグラフを描くことが出来るPlotlyですが、その一方で、アノテーションなどを用いて手の込んだグラフを描くことも可能です。

皆さんもぜひ試してみてください!

当ブログでは、Plotlyの使い方を様々解説しています。

他の記事もぜひ参考にしてみてください!

【Dash Plotlyの使い方 基本編】Plotlyで描く円グラフ徹底まとめ