Plotlyのscatter plotでlineの先っぽに矢印をつけるの巻

Plotlyのscatter plotでlineの先っぽに矢印をつけるの巻

はじめに

PlotlyのScatter plotを使ってプロットされた座標同士を線でつなぐ場合があるのですが、見た目に方向性を持たせたいときがあるぞ、と。

なので、線の先っぽに矢印をつけたい。どうしましょ、という話です。

これを、

plotly_arrowhead_on_line01

こうしたい、と。

plotly_arrowhead_on_line02

そのやり方の備忘録です。

やりかた

text annotationsの矢印を使ってそれらしく描画してしまいます。

これのこと。

plotly_arrowhead_on_line03

スマートじゃないと思うけど今のところ他に方法が見当たらないので。。。

以下でやってみます。

まずは普通にScatter plotを描画

まずは普通にScatter plotを描画してみます。linesとmarkersとtextで表現。

import plotly.graph_objects as go

x=[1, 4, 10]
y=[1, 6, 8]

fig = go.Figure()

fig.add_trace(go.Scatter(
    x=x,
    y=y,
    mode="lines+markers+text",
    text=["A", "B", "C" ],
    textposition="top left",
))

fig.show()

描けました。

plotly_arrowhead_on_line01

矢印を追加します

これに矢印をつけるには、矢印のannotationの始点やら終点やら方向やらを、

もともとのデータの座標とぴったり合わせたものを作って追加してやればいいぞ、と。

というわけで先ほどのコードに追記したものを次に記載します。

import plotly.graph_objects as go


x=[1, 4, 10]
y=[1, 6, 8]

fig = go.Figure()

fig.add_trace(go.Scatter(
    x=x,
    y=y,
    mode="markers+text",
    text=["A", "B", "C" ],
    textposition="top left",
))


arrows =[
    go.layout.Annotation(dict(
        x=x[1], #x position of arrowhead
        y=y[1], #y position of arrowhead
        showarrow=True,
        xref = "x", #reference axis of arrow head coordinate_x 
        yref = "y", #reference axis of arrow head coordinate_y 
        arrowcolor="#636efa", #color of arrow
        arrowsize = 1.5, #size of arrow head
        arrowwidth = 2, #width of arrow line
        ax = x[0], #arrow tail coordinate_x 
        ay = y[0], #arrow tail coordinate_y
        axref= "x", #reference axis of arrow tail coordinate_x 
        ayref= "y", #reference axis of arrow tail coordinate_y 
        arrowhead=5, #annotation arrow head style, from 0 to 8
        )),
    go.layout.Annotation(dict(
        x=x[2], #x position of arrowhead
        y=y[2], #y position of arrowhead
        showarrow=True,
        xref = "x", #reference axis of arrow head coordinate_x 
        yref = "y", #reference axis of arrow head coordinate_y 
        arrowcolor="#636efa", #color of arrow
        arrowsize = 1.5, #size of arrow head
        arrowwidth = 2, #width of arrow line
        ax = x[1], #arrow tail coordinate_x 
        ay = y[1], #arrow tail coordinate_y
        axref= "x", #reference axis of arrow tail coordinate_x 
        ayref= "y", #reference axis of arrow tail coordinate_y 
        arrowhead=5, #annotation arrow head style, from 0 to 8
        ))
        ]

fig.update_layout(annotations=arrows)

fig.show()

コードの説明

・矢印のパラメータを記述したgo.layout.Annotaiton()を、arrowsというリストに格納してから(今回は矢印2本分)、fig.update_layout(annotations=arrows)のところで渡しています

・矢印のhead側とtail側の各座標をx, y と ax, ayで指定しています

・arrowcolorをlinesのデフォルトのcolor(#636efa)と合わせておくと違和感がないかと(もとの線を消してもあまり変わらないのでmodeからlinesを削除してもいいかも。ちょっと短くなるけど。)

・xref, yref, axref, ayrefにて、矢印の描画に使う座標軸を指定しています。今回はx軸とy軸は1つずつなので”x”, “y”しか選択肢がないですが、第2軸があって、第2軸を指定したい時は”x2”, “y2″としてやります

・arrowheadで矢印の先端の種類を[0-8]から選択します。矢印じゃないのもある。。。

・その他の各パラメータの説明はコード中のコメントをご参照ください

で、コードを実行すると、

plotly_arrowhead_on_line02

無事描画できました。

矢印をいくつも描画したいとき

矢印にしたい線がいくつもある時は、それぞれの線に対して作った矢印のリストを作って、fig.update_layout()に渡してやればいいです。

こんな感じで。

import plotly.graph_objects as go

#coordinate lists (arrow head and arrow tail)
headpoint_list = [(3,3), (4,3), (5,3), (3,4), (5,4), (3,5), (4,5), (5,5)]
tailpoint_list = [(2,2), (4,2), (6,2), (2,4), (6,4), (2,6), (4,6), (6,6)]

#put go.Scatter() for each coordinate into a list 
arrowhead_traces = [go.Scatter(
    x=[i[0]], 
    y=[i[1]], 
    mode="lines+markers"
    ) 
    for i in headpoint_list
    ]

arrowtail_traces = [go.Scatter(
    x=[i[0]],
    y=[i[1]], 
    mode="markers" 
    ) 
    for i in tailpoint_list
    ]

#merge
total_traces = arrowhead_traces + arrowtail_traces

#make a list of go.layout.Annotation() for each pair of arrow head and tail
arrows = [go.layout.Annotation(dict(
    x= head[0], #x position of arrowhead
    y= head[1], #y position of arrowhead
    showarrow=True,    
    xref = "x", #reference axis of arrow head coordinate_x 
    yref = "y",#reference axis of arrow head coordinate_y 
    arrowcolor="#636efa", #color of arrow
    arrowsize = 1.5, #size of arrow head
    arrowwidth = 2, #width of arrow line
    ax = tail[0], #arrow tail coordinate_x 
    ay = tail[1], #arrow tail coordinate_y
    axref= "x", #reference axis of arrow tail coordinate_x 
    ayref= "y", #reference axis of arrow tail coordinate_y 
    arrowhead=i, #annotation arrow head style, from 0 to 8
    text = 'style #'+str(i),
    )) 
    for i, (head, tail) in enumerate(zip(headpoint_list, tailpoint_list))
    ]

#pass total_traces to go.Figure 
fig = go.Figure(total_traces)

#update_layout with annotations 
fig.update_layout(annotations=arrows)

fig.show()

コードの説明

・矢印を作るのに使う座標が取り出せれば基データはどんな形でもいいと思います

・それぞれの座標についてgo.Scatter()を作ってリスト化します。作ったリストをfig = go.Figure() のところで渡しています

・矢印のパラメータを記述したgo.layout.Annotaiton()を、それぞれの座標について作って、arrowsというリストに入れています。これをfig.update_layout(annotations=arrows)のところで渡しています

・今回、矢印9個分(矢印のスタイルの数)の座標ペアを使用したので、

 for i, (head, tail) in enumerate(zip(headpoint_list, tailpoint_list))

 のところで、zipで各座標リストからそれぞれの座標をheadとtailで取り出しています

 ついでにenumerateで座標ペア分の数字(矢印のスタイル指定用)も取得しています

・取り出した矢印のhead側とtail側の各座標をx, y と ax, ayに代入しています

・矢印の先端の種類を指定するために、取得した数字をarrowheadに代入しています

・text = “style #”+str(i) スタイルの説明テキストの表示用 

というわけで、ついでに矢印の種類も全部描画してみました。

plotly_arrowhead_on_line04

Markerを見えなくしたいときはmodeからmarkersを削除するか、markerのopacityを0に設定すればいいです。

改変部分を抜粋すると、

#coordinate lists (arrow head and arrow tail)
arrowhead_traces = [go.Scatter(
    x=[i[0]], 
    y=[i[1]], 
    mode="lines"
    ) 
    for i in headpoint_list
    ]

#put go.Scatter() for each coordinate into a list 
arrowtail_traces = [go.Scatter(
    x=[i[0]],
    y=[i[1]], 
    mode="lines+markers",
    marker={'opacity':0},
    ) 
    for i in tailpoint_list
    ]

で、こうなります。ちょっとすっきり。

plotly_arrowhead_on_line05

おわりに

PlotlyのScatter plotの線を矢印にする方法でした。

なんか無理やり感が強いので、他にいい方法があればいいんですけど。

実施環境

Plotly 4.14.3

Python 3.9.4

Mac OS Catalina version 10.15.7

参考にしたページ

今回も感謝

Plotly公式サイト

https://plotly.com/python/text-and-annotations/

https://plotly.com/python/reference/layout/annotations/

Stack Overflow

https://stackoverflow.com/questions/58095322/draw-multiple-arrows-using-plotly-python



https://business.xserver.ne.jp/

https://www.xdomain.ne.jp/

★LOLIPOP★

.tokyo

MuuMuu Domain!