こんな人にオススメ
plotly.express
、px
でsubplots
を作成したいけどどうしたらいい?
make_subplots
とか使わないといけない?
ということで、今回はplotly.express
、px
を使ってsubplots
を作成する方法を解説する。以下の記事ではplotly.graph_objects
、go
のsubplots
を解説した。
-
-
【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く
続きを見る
今回はpx
版のsubplots
。go
だと色々と設定が必要だったが、px
だと使用するデータフレームが規定の形になっていたら一種で完成する。
python環境は以下。
- Python 3.9.7
- plotly 5.3.1
- plotly-orca 3.4.2
作成したコード全文
下準備
import sys import plotly.express as px import plotly.io as pio # sys.path.append('../../') # import plotly_layout_template as template
まずは下準備としてのimport関連。pioはグラフ保存用で使用。
-
-
【随時更新 備忘録】plotlyのグラフ即席作成コード
続きを見る
使用するデータ
df = px.data.gapminder() print(df) # country continent year ... gdpPercap iso_alpha iso_num # 0 Afghanistan Asia 1952 ... 779.445314 AFG 4 # 1 Afghanistan Asia 1957 ... 820.853030 AFG 4 # 2 Afghanistan Asia 1962 ... 853.100710 AFG 4 # 3 Afghanistan Asia 1967 ... 836.197138 AFG 4 # 4 Afghanistan Asia 1972 ... 739.981106 AFG 4 # ... ... ... ... ... ... ... ... # 1699 Zimbabwe Africa 1987 ... 706.157306 ZWE 716 # 1700 Zimbabwe Africa 1992 ... 693.420786 ZWE 716 # 1701 Zimbabwe Africa 1997 ... 792.449960 ZWE 716 # 1702 Zimbabwe Africa 2002 ... 672.038623 ZWE 716 # 1703 Zimbabwe Africa 2007 ... 469.709298 ZWE 716 # [1704 rows x 8 columns]
今回使用するデータはplotlyの「Facet and Trellis Plots in Python」の「Wrapping Column Facets」にあるギャップマインダーのデータ。
ギャップマインダーのデータを使用した記事には以下のものもある。こちらは自分でバブルチャートを作成したもの。
-
-
【plotly&バブルチャート】plotlyで各国の収入と平均寿命をバブルチャートで描く
続きを見る
-
-
【plotly&バブルチャート】plotlyで各国の収入と平均寿命の時代変化をバブルチャートで描く
続きを見る
全ヘッダの内容を取り出してみる
for header in df.columns: values = df[header].unique() print(f"{header}: {values}\\n")
普通にデータフレームを抽出しても途中のヘッダが省略されて「...」表記になってしまう。ということで、全ヘッダを取り出してみる。
取り出し方はシンプルにしたいが、国名を示す「country」の列で同じ国名が続いているので.unique
で一意な値を抽出。
平均寿命とかおかしなことになるけど、値の種類がめちゃくちゃあって「...」になるから放置。結果は以下。
# country: ['Afghanistan' 'Albania' 'Algeria' 'Angola' 'Argentina' 'Australia' # 'Austria' 'Bahrain' 'Bangladesh' 'Belgium' 'Benin' 'Bolivia' # 'Bosnia and Herzegovina' 'Botswana' 'Brazil' 'Bulgaria' 'Burkina Faso' # 'Burundi' 'Cambodia' 'Cameroon' 'Canada' 'Central African Republic' # 'Chad' 'Chile' 'China' 'Colombia' 'Comoros' 'Congo, Dem. Rep.' # 'Congo, Rep.' 'Costa Rica' "Cote d'Ivoire" 'Croatia' 'Cuba' # 'Czech Republic' 'Denmark' 'Djibouti' 'Dominican Republic' 'Ecuador' # 'Egypt' 'El Salvador' 'Equatorial Guinea' 'Eritrea' 'Ethiopia' 'Finland' # 'France' 'Gabon' 'Gambia' 'Germany' 'Ghana' 'Greece' 'Guatemala' 'Guinea' # 'Guinea-Bissau' 'Haiti' 'Honduras' 'Hong Kong, China' 'Hungary' 'Iceland' # 'India' 'Indonesia' 'Iran' 'Iraq' 'Ireland' 'Israel' 'Italy' 'Jamaica' # 'Japan' 'Jordan' 'Kenya' 'Korea, Dem. Rep.' 'Korea, Rep.' 'Kuwait' # 'Lebanon' 'Lesotho' 'Liberia' 'Libya' 'Madagascar' 'Malawi' 'Malaysia' # 'Mali' 'Mauritania' 'Mauritius' 'Mexico' 'Mongolia' 'Montenegro' # 'Morocco' 'Mozambique' 'Myanmar' 'Namibia' 'Nepal' 'Netherlands' # 'New Zealand' 'Nicaragua' 'Niger' 'Nigeria' 'Norway' 'Oman' 'Pakistan' # 'Panama' 'Paraguay' 'Peru' 'Philippines' 'Poland' 'Portugal' # 'Puerto Rico' 'Reunion' 'Romania' 'Rwanda' 'Sao Tome and Principe' # 'Saudi Arabia' 'Senegal' 'Serbia' 'Sierra Leone' 'Singapore' # 'Slovak Republic' 'Slovenia' 'Somalia' 'South Africa' 'Spain' 'Sri Lanka' # 'Sudan' 'Swaziland' 'Sweden' 'Switzerland' 'Syria' 'Taiwan' 'Tanzania' # 'Thailand' 'Togo' 'Trinidad and Tobago' 'Tunisia' 'Turkey' 'Uganda' # 'United Kingdom' 'United States' 'Uruguay' 'Venezuela' 'Vietnam' # 'West Bank and Gaza' 'Yemen, Rep.' 'Zambia' 'Zimbabwe'] # continent: ['Asia' 'Europe' 'Africa' 'Americas' 'Oceania'] # year: [1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 2002 2007] # lifeExp: [28.801 30.332 31.997 ... 46.809 39.989 43.487] # pop: [ 8425333 9240934 10267083 ... 11404948 11926563 12311143] # gdpPercap: [779.4453145 820.8530296 853.10071 ... 792.4499603 672.0386227 # 469.7092981] # iso_alpha: ['AFG' 'ALB' 'DZA' 'AGO' 'ARG' 'AUS' 'AUT' 'BHR' 'BGD' 'BEL' 'BEN' 'BOL' # 'BIH' 'BWA' 'BRA' 'BGR' 'BFA' 'BDI' 'KHM' 'CMR' 'CAN' 'CAF' 'TCD' 'CHL' # 'CHN' 'COL' 'COM' 'COD' 'COG' 'CRI' 'CIV' 'HRV' 'CUB' 'CZE' 'DNK' 'DJI' # 'DOM' 'ECU' 'EGY' 'SLV' 'GNQ' 'ERI' 'ETH' 'FIN' 'FRA' 'GAB' 'GMB' 'DEU' # 'GHA' 'GRC' 'GTM' 'GIN' 'GNB' 'HTI' 'HND' 'HKG' 'HUN' 'ISL' 'IND' 'IDN' # 'IRN' 'IRQ' 'IRL' 'ISR' 'ITA' 'JAM' 'JPN' 'JOR' 'KEN' 'KOR' 'KWT' 'LBN' # 'LSO' 'LBR' 'LBY' 'MDG' 'MWI' 'MYS' 'MLI' 'MRT' 'MUS' 'MEX' 'MNG' 'MNE' # 'MAR' 'MOZ' 'MMR' 'NAM' 'NPL' 'NLD' 'NZL' 'NIC' 'NER' 'NGA' 'NOR' 'OMN' # 'PAK' 'PAN' 'PRY' 'PER' 'PHL' 'POL' 'PRT' 'PRI' 'REU' 'ROU' 'RWA' 'STP' # 'SAU' 'SEN' 'SRB' 'SLE' 'SGP' 'SVK' 'SVN' 'SOM' 'ZAF' 'ESP' 'LKA' 'SDN' # 'SWZ' 'SWE' 'CHE' 'SYR' 'TWN' 'TZA' 'THA' 'TGO' 'TTO' 'TUN' 'TUR' 'UGA' # 'GBR' 'USA' 'URY' 'VEN' 'VNM' 'PSE' 'YEM' 'ZMB' 'ZWE'] # iso_num: [ 4 8 12 24 32 36 40 48 50 56 204 68 70 72 76 100 854 108 # 116 120 124 140 148 152 156 170 174 180 178 188 384 191 192 203 208 262 # 214 218 818 222 226 232 231 246 250 266 270 276 288 300 320 324 624 332 # 340 344 348 352 356 360 364 368 372 376 380 388 392 400 404 410 414 422 # 426 430 434 450 454 458 466 478 480 484 496 499 504 508 104 516 524 528 # 554 558 562 566 578 512 586 591 600 604 608 616 620 630 638 642 646 678 # 682 686 688 694 702 703 705 706 710 724 144 736 748 752 756 760 158 834 # 764 768 780 788 792 800 826 840 858 862 704 275 887 894 716]
内容は以下。
- country: 国名
- continent: 大陸
- year: データの年
- lifeExp: 平均寿命
- pop: 人口
- gdpPercap: 一人当たりGDP
- iso_alpha: ISOの国名表記
- iso_num: ISOの国の番号
グラフ保存用の関数とプロット用の関数
ここではメインとなるグラフを描くための関数を定義する。といってもpxなのでちょっと設定すれば勝手にグラフを描いてくれる。
グラフ保存用の関数
# グラフ保存用の関数 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
はグラフの右上に存在するツールバーのようなもの。
config
を設定しておくとこのツールバーの内容を追加したり削除したりできる。htmlで使用することで、保存後のhtmlファイルでもツールバーの設定が引き継がれる。
-
-
【plotly&config】グラフのツールバーを編集する
続きを見る
今回はconfig=Noneとすることでconfigの設定はデフォルトにする。上記記事だとカスタムしすぎてfacetの複数グラフだとごちゃごちゃしがち。
プロット用の関数
def scatter(save_name, **kwargs): fig = px.scatter( df, x='gdpPercap', y='lifeExp', hover_data=df, **kwargs # 追加設定 ) # fig.show() prefix = 'plotly-px-subplot' save(fig=fig, config=None, save_name=f"{prefix}_{save_name}")
scatter
関数はpx
で散布図を作成するための関数。とりあえず最低限の引数のみを指定した。追加したい場合は可変長キーワード引数kwargs
を使用する。
hover_data
でマウスオーバーした際に表示する内容を設定している。今回はデータフレーム内の情報を全て表示するように設定した。
あとはconfigをNoneと設定しグラフ表示時に使用、先程のsave関数を使用してグラフを保存している。
簡単なグラフとアニメーション
ということでグラフ化だが、まずはsubplots
ではなくシンプルなグラフやアニメーションのグラフを作成。アニメーションについては以下で詳しく解説している。
-
-
【px&animation】plotly.expressでアニメーションのグラフを作成
続きを見る
デフォルトでグラフ化
まずはscatter
関数の設定そのままでグラフ化。この場合だと何が何だかわからん。GDPに関してはかなり左に寄っているためバランスが悪いし全プロットが同じ色だ。
ということで色分けやバブルサイズの変更を行う。
# デフォルトのグラフ scatter(save_name='default')
大陸ごとに色分け+バブルサイズ変更
とりあえず大陸ごとに色分けをして人口でバブルサイズを変更した。多少傾向は見やすくなったが、しかしこれだとまだ左寄りすぎている。
なお、グラフ右の方のプロットが消えているように感じるかもしれないが、単純にバブルが小さくなりすぎているだけ。
# 大陸ごとに色分け+人口でバブルサイズ変更 scatter(save_name='continent', color='continent', size='pop', )
横軸をlogに
見づらかったので横軸をlogに変更。これだけでも大体の傾向がわかりやすくなる。こうみるとアジア・アフリカは左寄り、ヨーロッパ・アメリカは右寄り、オセアニアは人口が少なすぎて見えない。
ここら辺のレイアウトに関しては適宜変更すればいいだろう。
# 横軸をlogに scatter(save_name='log_x', color='continent', size='pop', log_x=True)
アニメーションを作ってみる
ということで、遊び心の1つのアニメーションを作成。作らなくてもよかったけど簡単にできるから作ってやった。これで各年の傾向がわかりやすくなる。
ただし、欠点として全ての年を一覧することはできない、すなわち一覧性に欠ける。なのでsubplots
を作成する。
# アニメーションを追加 scatter( save_name='animation', color='continent', size='pop', log_x=True, animation_frame='year', range_x=(2e2, 2e5), range_y=(0, 100), )
subplots
を作成
本題のsubplots
。引数が複数あるのでそれぞれ作成してみる。主にsubplots
で使用する引数はfacet_col
とfacet_row
。
facet_col
だけだと1行表示
まずはfacet_col
で年を設定する。そうすると全てのプロットが横並びになってしまう。これはこれで面白いけどあまりにも縦長すぎる。
# yearを基準に列方向にsubplotで作成 # 1行で作成される scatter( save_name='facet_col', color='continent', size='pop', log_x=True, facet_col='year', )
facet_col_wrap
で列数指定
じゃあどうすればいいのかってことだけど、引数facet_col_wrap
を使用することで列数を指定できる。今回だと全部で12年分あるので3行4列で作成する。
これだとかなり見やすくなる。目立つもので言えば中国とインドだが、これら2国は時代とともに人口が増えていき、平均寿命もGDPも増加傾向にある。
# 4列のsubplotで作成 scatter( save_name='facet_col_wrap', color='continent', size='pop', log_x=True, facet_col='year', facet_col_wrap=4, )
facet_row
は行方向
先程はfacet_col
だったが、facet_row
にすると行方向での設定となる。もちろん行方向だけの設定になるので1列表示になってしまいかなり見づらい。
# yearを基準に行方向にsubplotで作成 # 1列で作成される scatter( save_name='facet_row', color='continent', size='pop', log_x=True, facet_row='year', )
facet_row_wrap
はない
# facet_row_wrapはない scatter( save_name='facet_row_wrap', color='continent', size='pop', log_x=True, facet_row='year', facet_row_wrap=4, ) # TypeError: scatter() got an unexpected keyword argument 'facet_row_wrap'
じゃあ行方向でもfacet_row_wrap
で行数を指定すればいいじゃないかってことになるけど、facet_row_wrap
は引数として存在しない。
要するに行と列の数を指定したい場合は列基準で数を指定しないといけないってこと。
facet_row
を指定するとfacet_col_wrap
は無視される
facet_row
を指定しつつfacet_col_wrap
を指定するとどうなるのか。facet_row
が優先されてfacet_col_wrap
は無視される。
今回だと列数は4に設定したのに1列のまま。
# facet_rowを指定するとfacet_col_wrapは無視される scatter( save_name='facet_row_col_wrap', color='continent', size='pop', log_x=True, facet_row='year', facet_col_wrap=4 )
列数が約数じゃなくても大丈夫
今回だと年が12種類なので5列にするとエラーになるかといえばそうではない。勝手に調節してくれる。便利。
# 5列のsubplotで作成 scatter( save_name='facet_col_wrap5', color='continent', size='pop', log_x=True, facet_col='year', facet_col_wrap=5, )
subplots
の間隔を設定
基本的なsubplots
は以上で終わり。ここではレイアウト関連として各グラフ間の間隔を設定する。
facet_row_spacing
は行の間隔
まずはfacet_row_spacing
。これは行の間隔を設定する。今回だと0.2に設定してみた。値は0から1の間だが、0.5とかに設定するとグラフが潰れる危険があるので注意。
# facet_row_spacingで行の間隔を設定 scatter( save_name='facet_row_spacing', color='continent', size='pop', log_x=True, facet_col='year', facet_col_wrap=4, facet_row_spacing=0.2 )
facet_col_spacing
は列の間隔
facet_col_spacing
は逆に列の間隔を設定する。
# facet_col_spacingで列の間隔を設定 scatter( save_name='facet_col_spacing', color='continent', size='pop', log_x=True, facet_col='year', facet_col_wrap=4, facet_col_spacing=0.2 )
facet_row_spacing
、facet_col_spacing
の同時設定も可能
facet_row_spacing
、facet_col_spacing
については同時に設定することも可能。ここではどちらも0.2に設定した。かなり間隔が大きくなる。
# facet_row_spacing, facet_col_spacingで行・列の間隔を設定 scatter( save_name='facet_row_col_spacing', color='continent', size='pop', log_x=True, facet_col='year', facet_col_wrap=4, facet_col_spacing=0.2, facet_row_spacing=0.2, )
category_orders
も試す
最後にpx.scatter
の引数category_orders
を試しておく。ただし、本記事で使用したギャップマインダーのデータだと使いにくいから別のデータを使用。
category_orders
はカテゴリーごとにsubplots
を設定できるというもの。今回だと曜日と時間でsubplots
を整理している。
ここでのグラフは公式の「Histogram Facet Grids」に書かれているものを改行などを加えたりしたもの。
df = px.data.tips() print(df) # total_bill tip sex smoker day time size # 0 16.99 1.01 Female No Sun Dinner 2 # 1 10.34 1.66 Male No Sun Dinner 3 # 2 21.01 3.50 Male No Sun Dinner 3 # 3 23.68 3.31 Male No Sun Dinner 2 # 4 24.59 3.61 Female No Sun Dinner 4 # .. ... ... ... ... ... ... ... # 239 29.03 5.92 Male No Sat Dinner 3 # 240 27.18 2.00 Female Yes Sat Dinner 2 # 241 22.67 2.00 Male Yes Sat Dinner 2 # 242 17.82 1.75 Male No Sat Dinner 2 # 243 18.78 3.00 Female No Thur Dinner 2 # [244 rows x 7 columns] fig = px.histogram( df, x="total_bill", y="tip", color="sex", facet_row="time", facet_col="day", category_orders={ "day": ["Thur", "Fri", "Sat", "Sun"], # 列の設定 "time": ["Lunch", "Dinner"] # 行の設定 } ) fig.show() prefix = 'plotly-px-subplot' save(fig=fig, config=None, save_name=f"{prefix}_category_orders")
一瞬でsubplots
ができる
今回はpx
を使用してsubplots
を作成した。go
を使用した時はライブラリを別でimport
してごちゃごちゃ設定してたけど、px
の場合は引数1つないしは2つでできる。
その分、自由度は下がるが、元データを希望通りになるように設定できていれば一瞬。個人的には行数指定もできるようにすればもっと楽だと感じたが。
本記事でpx
のsubplots
が使えるようになったので、これからグラフの自由度がもっと上がるだろう。
関連記事
-
-
【plt&subplot】pythonのpltで複数グラフを1つの画像に描く
続きを見る
-
-
【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く
続きを見る
-
-
【plotly&table+scatter】subplotsで表と散布図を同時にグラフ化
続きを見る
-
-
【plotly&円グラフ】subplotsやボタンの適用
続きを見る