こんな人にオススメ
plotly
で円グラフを作成するのは簡単だったけど、引数がどんな意味を持つのかがわからない。あと、円グラフのsubplots
もしてみたい。
ということで、今回は円グラフにsubplots
を適用したり、引数によるグラフの変化を調べてみる。使用するデータは以下の記事でも使用したスマホの出荷台数についてのデータ。
-
-
【plotly&円グラフ】世界のスマホ出荷台数をgo.Pieで円グラフ化
続きを見る
日本人が大好きなAppleは秋に新作を発売するだけだが、Androidは1年中発売している。なので、玉石混交になるだろうことが今回の円グラフで見えてくる。
python環境は以下。
- Python 3.9.6
- plotly 5.1.0
- plotly-orca 3.4.2
作成したコード全文
下準備
import sys import plotly.graph_objects as go import plotly.io as pio from plotly.subplots import make_subplots sys.path.append('../../') import plotly_layout_template as template
まずは下準備としてのimport
関連。今回はnumpy
などは使用せずdict
を使用してデータを整理する。plotly_layout_templateは自作のplotlyテンプレートで、これを使用することで簡単にキレイなグラフが作成可能。
-
-
【随時更新 備忘録】plotlyのグラフ即席作成コード
続きを見る
スマホの出荷台数のデータ
今回使用するデータのは以下の3種類。
- 2020年の世界のスマホ出荷台数(Q3, Q4)
- 2021年の世界のスマホ出荷台数(Q1, Q2)
以下でこれらのデータの具体的な値を見ていく。
# スマホの出荷台数(100万台単位) # <https://www.canalys.com/newsroom/canalys-worldwide-smartphone-market-q3-2020> # <https://www.canalys.com/newsroom/global-smartphone-shipment-Q4-2020> # <https://www.canalys.com/newsroom/canalys-worldwide-smartphone-market-Q1-2021> # <https://canalys.com/newsroom/worldwide-smartphone-market-q2-2021> shipment2020to2021 = { '2020Q3': { 'Samsung': 80.2, 'Huawei': 51.7, 'Xiaomi': 47.1, 'Apple': 43.2, 'Vivo': 31.8, 'Others': 94.0, }, '2020Q4': { 'Apple': 81.8, 'Samsung': 62.0, 'Xiaomi': 43.4, 'Oppo': 34.7, 'Vivo': 32.1, 'Others': 105.5, }, '2021Q1': { 'Samsung': 76.5, 'Apple': 52.4, 'Xiaomi': 49.0, 'Oppo': 37.6, 'Vivo': 36.0, 'Others': 95.9, }, '2021Q2': { 'Samsung': 58.0, 'Xiaomi': 52.8, 'Apple': 45.7, 'Oppo': 32.6, 'Vivo': 31.2, 'Others': 95.8, }, }
世界のスマホシェア1位は基本Samsungであるけど、AppleのiPhoneが発売される秋頃=Q4ではiPhoneが大量に売れていくので、この時期に関しては1位に輝いている。
一方で、次の年のQ1になると再びSamsungが1位となっている。Samsung強い。
各種ブランドのテーマカラー
color_dct = { 'Samsung': (3, 78, 162), 'Xiaomi': (255, 103, 0), 'Apple': (0, 0, 0), 'Oppo': (30, 163, 102), 'Vivo': (0, 114, 184), 'Huawei': (255, 0, 0), 'Others': (115, 66, 41), # 茶色にしてみた }
円グラフを作成する際にはわかりやすい色の方がいいだろう。線のグラフとは異なり、色がつく部分が多いからより適切な色にした方がいい。以前iPhoneの色別の人気ランキングをiPhoneの色に関係なく適当に配色した円グラフがあったがこれは見づらい。
今回はブランド名とロゴの色で検索して得たRGBの値を使用、これに透明度を後ほど追加して使用することにする。
円グラフ、レイアウト作成とグラフ保存の関数
まずは全グラフの共通事項として、円グラフの作成関数やレイアウト作成の関数、そして作成したグラフを保存するための関数を作成する。
円グラフ作成用の関数
# 円グラフ作成用の関数 def pie(labels, values, **kwargs): d = go.Pie( labels=labels, # データ名 values=values, # データ sort=False, # データを自動で降順に並び替えるのを無効に direction='clockwise', # セクターを時計回りに配置(初期はcounterclockwise) **kwargs ) return d
円グラフ作成用の関数。使用するのはgo.Pie
でデータ名はlabels
、円グラフのデータはvalues
で指定する。通常、使用されるgo.Scatter
のようにx
, y
といった表示でないので注意が必要。
また、デフォルトではvalues
が大きい順で勝手にソートされるのでsort=False
、デフォルトは反時計回りなのでdirection='clockwise'
で時計回りに変更。
**kwargs
を引数に設定することで、後から追加の引数を設定することができる。詳しくは以下。
レイアウト作成用の関数
# レイアウト作成用の関数 def layout_kwargs(**kwargs): layout = go.Layout( template=template.plotly_layout(), **kwargs, ) return layout
レイアウト作成の関数で自作のテンプレートを適用。ここでも**kwargs
とすることで後から追加の引数を設定できるようにする。
グラフ保存用の関数
# グラフ保存用の関数 def save(fig, config, save_name): pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html", config=config,) pio.write_image(fig, f"{save_name}.png")
最後は作成したグラフを保存するための関数。保存形式はhtmlとpngとした。また、configを設定することでグラフ上部のツールバーの機能を増やしたり減らしたりすることができる。が、円グラフの場合はほとんど役立たない。
円グラフの場合はホバーでの情報表示をするかどうかの選択のみできるようだ。go.Scatter
などの散布図の場合は図形を追加したりできる。
-
-
【plotly&config】グラフのツールバーを編集する
続きを見る
円グラフ作成の関数
本題の円グラフ作成用の関数を紹介。先程の準備の関数を使用しつつサッと円グラフを作成できるようにした。
各ブランドの色を対応づける関数
# 作成したい円グラフの各セクターの色を自動で取得 def sector_color(data): colors = [] for name in data: color = color_dct[name] + (0.5,) # 透明度の追加 rgba = f"rgba{color}" colors.append(rgba) return colors
まず初めは各ブランドの色をデータに対応づける関数。color_dct
で定義したブランド名と実際に使用する出荷台数のデータのブランドは完全一致しているわけではなく、順番も異なる。
したがって、この関数でデータにどのブランドが含まれているのかを抽出し、色を選択、透明度を付与したのちに配列に入れる。あとはこれを色として使用すればいい。
各セクターの色を作成
# 各セクターの色を作成 def set_marker(data): # 各ブランドの色を設定 colors = sector_color(data=data) # セクターの設定 marker = dict( colors=colors, # 各セクターの色 line=dict(color="black", width=1), # 円グラフの枠線の色と太さ ) return marker
色を対応づけられたら、その色を円グラフに適用できるように整形する。円グラフの各セクターの色は引数marker
で行うことができるので、ここでも変数はmarker
とした。
各ブランドの色だけだと円グラフがわかりにくいので、セクターは全て黒線で囲ってわかりやすくなるように調節している。
4つの出荷台数データを円グラフのsubplots
で作成
まずは4期間の出荷台数を円グラフのsubplots
で描画する方法について解説。subplotsを使用することで、パッと見でデータの内訳とその変化を表現することができる。
データを作成した時にわかった、Q4のAppleの快進撃もこの円グラフを見ることで簡単に確認することができる。また、2020年のQ3ではHuaweiがランクインしていたが、Q4では姿を消している。
Huaweiはアメリカの制裁によって、色々と生産などがうまくいっておらず、一番痛手だったのがGoogle系のサービスが使えないということ。最新のHuaweiスマホでは既に使えない。
subplots
のspecs
のtype
はdomain
def smartphone_subplots(data, save_name, title=None, **kwargs): fig = set_subplots( rows=2, cols=2, specs=[ [{'type': 'domain'}, {'type': 'domain'}], [{'type': 'domain'}, {'type': 'domain'}], ], subplot_titles=['2020Q3', '2020Q4', '2021Q1', '20201Q2'], shared_xaxes=False, shared_yaxes=False ) # 円グラフ for num, quarter in enumerate(data): model = data[quarter] labels, values = tuple(model), tuple(model.values()) sum_value = sum(values) # マーカーの設定 marker = set_marker(data=model) # ホバーで表示するcustomdataを作成 customdata = [f"{(x / sum_value) * 100:.2f}%" for x in values] # ホバーで表示する内容を自作 hovertemplate = ('%{label}<br>%{value}M shipments' '<br>%{customdata}<extra></extra>') d = pie( labels=labels, values=values, marker=marker, name=quarter, customdata=customdata, # customdataで割合を表示 hovertemplate=hovertemplate, # hovertemplateでホバーを設定 **kwargs, ) row, col = num // 2 + 1, num % 2 + 1 fig.append_trace(d, row, col) # 配置する行と列位置 # レイアウト # グラフタイトルの位置を中央に layout = layout_kwargs(title=dict(text=title, x=0.5, xanchor='center'),) fig['layout'].update(layout) # グラフの表示と保存 config = template.plotly_config() fig.show(config=config,) save(fig=fig, config=config, save_name=save_name)
ここで作成するグラフは円グラフのsubplots
。ということで、subplots
用にコードの書き方を変更している。plotly
のsubplots
の書き方については以下参照。
-
-
【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く
続きを見る
今回は2×2のグラフなので、うまくして行と列の配置順を作成する必要がある。これを解決したのが以下の部分で、切り捨てとあまりを使用することで、グリッド形式に並べている。
row, col = num // 2 + 1, num % 2 + 1 for num in range(4): row, col = num // 2 + 1, num % 2 + 1 print(f"row: {row}, col: {col}") # row: 1, col: 1 # row: 1, col: 2 # row: 2, col: 1 # row: 2, col: 2
また、今回は円グラフなので、subplots
のspecs
のtype
をdomain
に変更しないといけない。Referenceには以下のように書かれている。
’domain’: Subplot type for traces that are individually positioned. pie, parcoords, parcats, etc.
関数の起動は予め決めていた引数を設定することで実行可能だが、ここではtextinfo
で表示する内容をセクターのラベル、値、そして全体での割合にした。
あと、この円グラフを作成してから思ったのが、Googleはランクインしていないこと。日本ではそこそこ売れているような売れていないような感じだが、世界でも売れてなかった。
smartphone_subplots( data=shipment2020to2021, save_name='shipment2020to2021', title='Shipment 2020Q3 → 2021Q2 Global Shipment[million]', textinfo='label+value+percent', )
使えそうな引数をボタン・スライダーで試したい
plotlyは色々とカスタムする項目が多いのが特徴的だが、ここではgo.Pieの引数のうち使えそうなものをボタンを使用して試していく。使えそうな引数は以下。
# あるセクターを中心からどれくらいはみ出させるか pull = 0 # テキストをどの位置に設定するか textposition = ("inside", "outside", "auto", "none") # +もあり textinfo = ("label", "text", "value", "percent", "none") # +もあり hoverinfo = ( "label", "text", "value", "percent", "name", "all", "none", "skip", ) # ドーナツにする時のドーナツの穴の大きさ hole = 0 # テキストを配置する方法 insidetextorientation = ("horizontal", "radial", "tangential", "auto")
これらのうち、hole
とpull
は数値のデータなので後でスライダーを使用して作成することにする。
可変長キーワード引数kwargs
を展開して1つ1つ使用するための関数
# kwargsで指定したdict内の配列を1つずつ関数に適用するアイデア def unpack_kwargs(**kwargs): print(kwargs) # {'insidetextorientation': ('horizontal', 'radial', 'tangential', 'auto')} vals = kwargs.values() print(vals) # dict_values([('horizontal', 'radial', 'tangential', 'auto')]) # そのままlistにするとlistの中にtupleがあるネストになってしまう print(list(vals)) # [('horizontal', 'radial', 'tangential', 'auto')] # 展開して入れることで1次元listとして値を抽出可能 vals_lst = list(*vals) print(vals) # ['horizontal', 'radial', 'tangential', 'auto'] # kwargsで指定した配列を1つずつ取り出してgo.Pieに適用 for val in vals_lst: tmp = {} name = list(kwargs)[0] # kwargsで指定したdictのkeyを抽出 tmp[name] = val # 一時的にdictに保存 p = go.Pie(**tmp) # ここで展開 print(p)
例えば使えそうな引数の最後のinsidetextorientation = ("horizontal", "radial", "tangential", "auto")
を試そうとしてkwargs
にこれを代入しするとする。
そうすると、go.Pie
で展開する際には("horizontal", "radial", "tangential", "auto")
が代入されることになる。使える引数はこれらのうちの1つだけなのでこれはエラーになる。
かといって、関数内にinsidetextorientation
を使用してしまうと他の引数を指定する際にエラーとなってしまう。なのでkwargs
を使いたい。ということで上の関数。やってることは以下。
kwargs
のvalues
値を抽出- 抽出した
values
値でループを回す kwargs
のkey
と各ループでのvalues
を持つdict
を作成- これを
go.Pie
で展開
こうすることで、配列でkwargs
のvalues
を指定しても、その中の1つ1つを独立して使用することができる。この関数のアイデアを利用して、ボタン・スライダーに対応した関数を作成する。
使えそうな引数をボタン・スライダーで試すための関数
ということで、go.Pie
の使えそうな引数たちをボタン・スライダーで試してみる。まずはボタン・スライダーの設定で使用する関数を定義、その後、本体の関数を作成する。
ボタンやスライダーに関しては以下参照。
-
-
【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加
続きを見る
-
-
【plotly&スライダー】plotlyのslidersにスライダーを追加
続きを見る
プロットの表示・非表示を設定する関数
# プロットの表示・非表示を設定する関数 def set_visible(nums, labels): # 表示・非表示の内容を作成 visibles = [] for num, label in enumerate(labels): # 一旦全てのプロットを非表示に visible = [False] * nums visible[num] = True # ボタンに対応するプロットを表示 button = dict( label=f"{label}", method='update', args=[dict(visible=visible), dict(title=label), ] ) visibles.append(button) return visibles
まずはプロットの表示・非表示を設定する関数。plotly
のボタン・スライダーを使用してグラフを切り替えるというのは、予め作成したグラフを表示・非表示で切り替えること。
なので、予めどのプロットを表示・他のプロットを非表示にするというのを決めておかなくてはいけない。set_visible
関数では予め全プロットを非表示にし、その後にボタンに該当するプロットを表示に変更するという風にしている。
ボタンをレイアウトに配置するための関数
# ボタンをレイアウトに配置するための関数 def set_updatemenus(buttons, buttons_type, direction): updatemenus = [ dict( type=buttons_type, direction=direction, # ボタンのタイプと向き x=0.5, y=1.01, xanchor='center', yanchor='bottom', active=0, buttons=buttons, ) ] return updatemenus
お次は作成した表示・非表示の設定をグラフのレイアウトに配置するための関数。まずはボタンから。先程の表示・非表示を引数buttons
として使用。ボタンはプルダウン方式と常に表示されるボタン方式の2種類ある。また方向も決められる。
引数が増えると常にボタンを表示しているとグラフからはみ出してしまうので、その場合はプルダウン方式(type='dropdown'
)にする。少ないとボタン方式(type='buttons'
)にする。
また、次のボタンが並ぶ方向(direction
)については、プルダウンの場合は下(down
)に、ボタンの場合は右(right
)に配置する。
スライダーをレイアウトに配置するための関数
# スライダーをレイアウトに配置するための関数 def set_slider(steps, prefix): # スライダー全体の設定 sliders = [ dict( currentvalue=dict(prefix=f"{prefix}: "), active=0, pad={"t": 50}, steps=steps, ) ] return sliders
スライダーも基本は同じように作成する。スライダーは常に表示されているので、この部分については設定する必要ない。指定するのは表示・非表示の設定を表す引数steps
だけ。
また、今表示しているスライダーがどの値の時なのかを明示する文字に接頭辞(prefix
)も設定しておいた。
使えそうな引数を試すための関数
# go.Pieの引数をボタンで切り替えられるようにする関数 def smartphone_buttons(data, save_name, title, butoons_slider, buttons_type='buttons', direction='right', **kwargs): # マーカーの設定 marker = set_marker(data=data) labels = tuple(data) values = tuple(data.values()) # 円グラフ plot = [] vals = kwargs.values() vals_lst = list(*vals) # kwargsで指定した配列を1つずつ取り出してgo.Pieに適用 for num, val in enumerate(vals_lst): tmp = {} name = list(kwargs)[0] # kwargsで指定したdictのkeyを抽出 tmp[name] = val # 一時的にdictに保存 d = pie( labels=labels, values=values, marker=marker, text=tuple(color_dct.values()), # textには各ブランドの色を指定 **tmp, # ここでkwargsで指定した引数の値を1つずつ試す visible=num == 0, # 初めの引数を初期表示とする ) plot.append(d) # ボタン・スライダー作成 visibles = set_visible(nums=len(plot), labels=vals_lst) if butoons_slider == 'buttons': updatemenus = set_updatemenus( buttons=visibles, buttons_type=buttons_type, direction=direction, ) sliders = None # ボタンが設定された時はスライダーの設定はなし elif butoons_slider == 'sliders': updatemenus = None # スライダーが設定された時はボタンの設定はなし sliders = set_slider(steps=visibles, prefix=list(kwargs)[0]) else: raise ValueError('butoons_sliderはbuttonsかslidersから選択') # レイアウト # グラフタイトルの位置を中央に layout = layout_kwargs( title=dict(text=title, x=0.5, xanchor='center'), updatemenus=updatemenus, # ボタンの配置 sliders=sliders, # スライダーの配置 ) # グラフの表示と保存 config = template.plotly_config() fig = go.Figure(data=plot, layout=layout) fig.show(config=config,) save(fig=fig, config=config, save_name=save_name)
本命の試すための関数。流れは以下。
- マーカーの設定
kwargs
を適用した円グラフを作成- ボタン・スライダー作成
- グラフレイアウト
- グラフの表示と保存
これまでに作成した関数を組み合わせることで作成した。ボタンとスライダーを指定できるようにし、指定されていない方はNone
にして適用されないようにした。
使えそうな引数を試す
さて、ようやく試せる。引数の内容や引数の種類など詳しく知りたいならReferenceを参照すればいい。
textinfo
を試す
まずはtextinfo
を試す。textinfo
は円グラフに表示する項目をどうするかを決める引数。+を
使用することで組み合わせることもできるので、数は大量。
ボタンの数が多い時はdropdown
でプルダウン方式にすることで使う時だけ表示する。
# textinfoを試す # noneはnone単体でないとエラー textinfo = ( "label", "text", "value", "percent", "none", "label+text", "label+text+value", "label+text+value+percent", "label+value", "label+value+percent", "label+percent", "text+value", "text+value+percent", "text+percent", "value+percent", ) smartphone_buttons( data=shipment2020to2021['2021Q2'], title='Shipment 2021Q2 Global Shipment[million]', save_name='shipment2021Q2_textinfo', textinfo=textinfo, buttons_type='dropdown', direction='down', # ボタンタイプと向きを変更 butoons_slider='buttons', # ボタンを選択 )
hoverinfo
を試す
hoverinfo
も同じような感じ。こちらはホバーした時に何を表示するかの設定。hovertemplate
で自分でカスタムすることもできるが、サクッと決めたいならhoverinfo
で十分。
先ほど色んな組み合わせの表示を試したので、ここでは簡単なものだけにした。
# hoverinfoを試す # allはall単体でないとエラー hoverinfo = ( "label", "text", "value", "percent", "name", "all", "none", "skip", "label+text+value+percent+name", ) smartphone_buttons( data=shipment2020to2021['2021Q2'], title='Shipment 2021Q2 Global Shipment[million]', save_name='shipment2021Q2_hoverinfo', hoverinfo=hoverinfo, buttons_type='dropdown', direction='down', # ボタンタイプと向きを変更 butoons_slider='buttons', # ボタンを選択 )
textposition
を試す
textposition
は円グラフに表示する項目をどこに表示するのかという設定。
# textpositionを試す smartphone_buttons( data=shipment2020to2021['2021Q2'], title='Shipment 2021Q2 Global Shipment[million]', save_name='shipment2021Q2_textposition', textposition=textposition, butoons_slider='buttons', # ボタンを選択 )
insidetextorientation
を試す
insidetextorientation
は円グラフに表示する項目をどんな風に配置するのかという設定。全て水平だったり、円状に配置したり設定することができる。
# insidetextorientationを試す smartphone_buttons( data=shipment2020to2021['2021Q2'], title='Shipment 2021Q2 Global Shipment[million]', save_name='shipment2021Q2_insidetextorientation', insidetextorientation=insidetextorientation, butoons_slider='buttons', # ボタンを選択 )
hole
を試す
hole
は円グラフを中心からどれくらいの割合で穴を開けるかの設定。0だと穴が開かず、1にすると完全に円グラフが消滅する。イメージはドーナツをの穴。
hole = (0, 0.1, 0.2, 0.5, 1,) smartphone_buttons( data=shipment2020to2021['2021Q2'], title='Shipment 2021Q2 Global Shipment[million]', save_name='shipment2021Q2_hole', hole=hole, butoons_slider='sliders', # ボタンを選択 )
pull
を試す
最後はpull
。これを設定すると円グラフの各セクターを中心からズラすことができる。pull
は数値だけだと全てのセクターに、配列にするとその順番で適用される。
0にするとズレがなくなる、すなわちいつも通りの円グラフとなる。
# pullを試す pull = ( 0.1, # 数値だけだと全てのセクターに反映 (0.5,), # 1つにすると初めのデータのみ適用される (0.5, 0.2), # 順番に設定されていく (0.5, 0, 0.2), # 0にすると中心固定 (0, 0.1, 0, 0.2, 0, 0.3, 0), (0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6), ) smartphone_buttons( data=shipment2020to2021['2021Q2'], title='Shipment 2021Q2 Global Shipment[million]', save_name='shipment2021Q2_pull', pull=pull, butoons_slider='sliders', # ボタンを選択 )
カスタムし放題
今回は円グラフに色んなカスタムを施す方法について解説した。本記事で紹介した内容は使う頻度が少ないかもしれないが、何か効果的な表現がしたい時などに使用できるだろう。
いつ使うかは知らんけど、いつか使うだろう。備えあれば憂いなし。
関連記事
-
-
【plotly&ボタン】グラフに複数種のボタンを追加
続きを見る
-
-
【plotly&グラフ内グラフ】plotlyでグラフ内に別グラフやグラフの一部拡大を描画
続きを見る
-
-
【plotly&table+scatter】subplotsで表と散布図を同時にグラフ化
続きを見る
-
-
【plotly&棒グラフ】go.Barでバーチャートを作成
続きを見る