前回はPlolyによる箱ひげ図(Box Plot)を見ました。
箱ひげ図は中央値や四分位点、外れ値などが視覚化でき、非常に便利なグラフです。
そして、さらに便利なのが今回ご紹介するバイオリン・プロットもしくはバイオリン図(Violin Plot)です。
こちらは、直接密度をプロットしますので、箱ひげ図よりも情報量は多いと言えます。
もちろん箱ひげ図の方が見やすい、伝わりやすい場合も多いので、バイオリン・プロットと使い分けていただければと思います。
では、plotlyを使ったバイオリン・プロット見ていきましょう。
公式HPの解説はこちらです。
Plotly Expressを使ったバイオリン・プロット(violin plot)の作成
基本的なプロット方法
Plotly Expressを使うと、Pandasのデータフレームを使って簡単に作成することが可能です。
箱ひげ図と同じデータセットを使います。
以下のような月ごとの株価リターンのデータがあるとします。
month スノーピーク_リターン アルペン_リターン 楽天_リターン ANA_リターン 1 -0.029491 -0.014067 -0.016043 -0.02828 1 0.020892 0.011414 0.015217 0.014128 1 -0.023256 -0.018062 -0.017131 -0.010588 1 0.040955 0.022997 0.006536 0.015487 1 0.004569 -0.011802 -0.004329 -0.004992 ... ... ... ... ... 12 0.011617 -0.003908 0.011 0.01299 12 0.004376 -0.001746 -0.013848 -0.001603 12 0.000543 -0.009168 -0.026078 -0.01445 12 0.007395 0.014673 0.037075 0.047242 12 0.012507 0.035933 -0.01291 0.012
まず、plotly.expressをインポートします。
import plotly.express as px
以下のようにpx.violinを呼び出すだけでバイオリオン・プロットを描くことができます。
箱ひげ図のpx.boxをpx.violinに変えるだけで大丈夫です。
px.violin(df, y='スノーピーク_リターン')
描画するためのデータフレームdfを第一引数として渡し、yで描画したい列を指定しています。

箱ひげ図のときと同様に、カーソルを当てると、中央値や四分位点などの数値も見ることができます。
グループごとにプロットする
ここで見たのは、1年間のリターンのバイオリン・プロットですが、例えば月ごとに見たいという場合は、xでグループに分割する列を指定します。
この場合だとxはmonthという月を表す列になります。
px.violin(df, y='スノーピーク_リターン', x='month')

各月の分布がよくわかりますね。月によっては6月や12月のように(微妙ではありますが)ピークが2つあるような月も存在するので、そういった場合は箱ひげ図のよりもバイオリン・プロットの方が分布をしっかりと理解することができます。
元データの分布もあわせてプロットする
元のデータの分布を直接見たい場合もあります。
その場合は、1次元ですが、散布図を付けることも可能です。
points='all'を付け加えるだけです。
px.violin(df, y='スノーピーク_リターン', x='month', points='all')

複数データをグルーピングしてプロットする
他にもcolorに色分けする列を指定することにより、その列の値でグルーピングしてくれます。
例えば以下のようなスノーピークと楽天のリターンのデータフレームがあるとします。
month return name 1 -0.029491 スノーピーク 1 0.020892 スノーピーク -0.023256 スノーピーク 1 0.040955 スノーピーク 1 0.004569 スノーピーク ... ... ... 12 0.011000 楽天 12 -0.013848 楽天 12 -0.026078 楽天 12 0.037075 楽天 12 -0.012910 楽天
px.violinでcolor="name"を指定します。
px.violin(df_vertical, y='return', x='month', color='name')
(ここでは、1月-6月にデータを絞っています)

箱ひげ図を追加する
バイオリン・プロットの中に箱ひげ図を入れることも可能です。
その場合は、box=Trueとします。
fig = px.violin(df_vertical, y='return', color='name', box=True, points='all') fig.show()
それほど差はないですが、スノーピークの方が分散(標準偏差)が大きいことはわかりますね。あと、5%程度のリターンの日がも多かったようです。

箱ひげ図でもそうでしたが、皆さまは用途に応じてキレイに整えていただければと思います。
その他、タイトルや色の設定もできますが、ここでは省略します。
詳細はこちらの記事をご参照ください。
plotly graph_objectsを使ったviolin plotの作成
基本的なプロット
では、plotly.expressではなく、よりきめ細やかな設定のできるplotly.graph_objectsを使った棒グラフの作成方法を見ていきましょう。
plotly.expressは非常にシンプルですが、plotly.graph_objectsもそれほど大変なわけではないので、個人的にはこちらをオススメします。
こちらも箱ひげ図の場合とほぼ同じです。
まず、plotly.graph_objectsをインポートします。
import plotly.graph_objects as go
そして、go.Violin()を呼び出します。
引数は最低限yにデータを渡してやるだけです。
あとはnameなどで凡例を設定します。
fig = go.Figure() fig.add_trace(go.Violin(y=df['スノーピーク_リターン'], name='スノーピーク' )) fig.add_trace(go.Violin(y=df['楽天_リターン'], name='楽天'))
これだけでシンプルな箱ひげ図が作成できます。

グループごとにプロットする
月ごとのリターンを見たい場合は、xに分けたいグループを指定します。
この場合だと、データフレームのmonthという列に月の情報が入っているので、x=df["month"]とすることで、month列で分けることができます。
fig = go.Figure() fig.add_trace(go.Violin(y=df['スノーピーク_リターン'], x=df['month'], name='スノーピーク' ))

複数データをグルーピングしてプロットする
続いて、スノーピークと楽天の月次リターンを比較したい場合です。
その場合もxに月を表す列を指定して、楽天のデータも渡すだけです。
fig = go.Figure() fig.add_trace(go.Violin(y=df.query('month<=6')['スノーピーク_リターン'], x=df.query('month<=6')['month'], name='スノーピーク' )) fig.add_trace(go.Violin(y=df.query('month<=6')['楽天_リターン'], x=df.query('month<=6')['month'], name='楽天'))
すると以下のように、スノーピークと楽天のバイオリン・プロットが重なった状態になります。

これはこれで比較はしやすいですが、別々に表示したい場合はレイアウトを設定する必要があります。
箱ひげ図では、update_layoutでboxmode="group"としましが、バイオリン・プロットではviolinmode="group"とします。

元データや箱ひげ図、平均もあわせてプロットする
plotly expressのときと同様に、散布図を横に描きたい場合は、points="all"を指定します。
箱ひげ図を中に入れるにはbox_visible=Trueとします。
またmeanline_visible=Trueとすることで、中央値だけでなく平均も表示されます。
ここではjitter=0.3として散布図の幅(広がり方)を指定しています。
fig = go.Figure() fig.add_trace(go.Violin(y=df.query('month==7')['スノーピーク_リターン'], name='スノーピーク', )) fig.add_trace(go.Violin(y=df.query('month==7')['ANA_リターン'], name='ANA', )) fig.add_trace(go.Violin(y=df.query('month==7')['楽天_リターン'], name='楽天', )) fig.update_traces(points='all', jitter=0.3, box_visible=True, meanline_visible=True )

2種類のグループのデータを左右に表示する
2種類のグループを同時にプロットすることが可能です。
そのためには、sideにpositiveもしくはnegativeを設定します。
positiveであれば右半分をnegativeであれば左半分を表示します。
散布図も同様に作成したければpoints='all'を指定すればいいのですが、点を左右にするために、pointposを指定します。
pointposは-2から2の範囲を取り、0が真ん中、正であれば右、負であれば左に表示されます。
ここでは左右それぞれに対して、-1.5と1.5を指定しています。
fig = go.Figure() df_sub = df.query('month in [6, 7, 8]') fig.add_trace(go.Violin(y=df_sub['スノーピーク_リターン'], x=df_sub['month'], name='スノーピーク', side='negative', pointpos=-1.5 )) fig.add_trace(go.Violin(y=df_sub['楽天_リターン'], x=df_sub['month'], name='楽天', side='positive', pointpos=1.5 )) fig.update_traces(box_visible=True, meanline_visible=True, points='all' )
すると以下のようなグラフが出来上がります。

この例ではそれほど見るところがありませんが、例えば、金融のモデルであれば、左をデフォルト(倒産した)サンプル、右をデフォルトしていないサンプルとして、各指標を比較することなどが考えられます。
デフォルトしたサンプルとデフォルトしていないサンプルで、各指標についてどういった差があるかを見ることが可能になります。
水平にプロットする
棒グラフのように水平方向にするには、traceでorientaion="h"とします。
また、xとyは入れ替えないといけません。
ここでは、xだけ指定したいと思います。
また、update_tracesでまとめてorientaion="h"と設定します。
fig = go.Figure() fig.add_trace(go.Violin(x=df.query('month==7')['スノーピーク_リターン'], name='スノーピーク', )) fig.add_trace(go.Violin(x=df.query('month==7')['ANA_リターン'], name='ANA', )) fig.add_trace(go.Violin(x=df.query('month==7')['楽天_リターン'], name='楽天', )) fig.update_traces(points='all', jitter=0.3, box_visible=True, meanline_visible=True ) fig.update_traces(orientation='h')

(おまけ) レイアウトを整える
箱ひげ図と同様に、最後に少しだけレイアウトを整えましょう。
以下のコードで不要なものは除いて見やすくします。
レイアウトの整え方についてはこちらの記事ご参照ださい。
fig = go.Figure() fig.add_trace(go.Violin(y=df.query('month==7')['スノーピーク_リターン'], name='<b>スノーピーク', )) fig.add_trace(go.Violin(y=df.query('month==7')['ANA_リターン'], name='<b>ANA', marker_color='lightblue' )) fig.add_trace(go.Violin(y=df.query('month==7')['楽天_リターン'], name='<b>楽天', marker_color='grey' )) fig.update_traces(points='all', jitter=0.3, box_visible=True, ) fig.update_yaxes(title=dict(text='リターン(%)'), tickformat='%', showline=True, linewidth=1, linecolor='lightgrey', color='grey') fig.update_xaxes(showline=True, linewidth=1, linecolor='lightgrey', color='grey', tickfont={'size': 17}) fig.update_layout(plot_bgcolor='white', title=dict(text='<b>2020年7月の各銘柄のリターン', font_color='grey', font_size=22, y=0.9 ), showlegend=False, )
箱ひげ図のときと同じデータなので、あまりぱっとしませんが、スノーピークのリターンを少し強調しています。

皆さまがお客さんや上司の方へのプレゼンをする際は、うまく伝えたいところを強調していただければと思います。
ちなみに、伝わるデータ可視化技術については以下の本が超オススメです。実務で使える目から鱗の一冊です。
Storytelling with Data: A Data Visualization Guide for Business Professionals (English Edition)まとめ
今回はplotlyを使ったバイオリン・プロット(violin plot)使い方を紹介しました。
グラフをよりわかりやすいものにするには、レイアウト設定を行う必要がありますが、それについては以下の記事で細かく解説していますので、参考にしていただければと思います。
またWaterfall図などをご紹介したいと思います。
では!