カテゴリー

px

【px&facet】plotly.expressでsubplotsを描く

2021年10月28日

こんな人にオススメ

plotly.expresspxsubplotsを作成したいけどどうしたらいい?

make_subplotsとか使わないといけない?

ということで、今回はplotly.expresspxを使ってsubplotsを作成する方法を解説する。以下の記事ではplotly.graph_objectsgosubplotsを解説した。

【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く

続きを見る

今回はpx版のsubplotsgoだと色々と設定が必要だったが、pxだと使用するデータフレームが規定の形になっていたら一種で完成する。

python環境は以下。

  • Python 3.9.7
  • plotly 5.3.1
  • plotly-orca 3.4.2
スポンサーリンク
スポンサーリンク

運営者のメガネです。TwitterInstagramも運営してます。

自己紹介はこちらから、お問い合わせはこちら。

運営者メガネ

作成したコード全文

下準備

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」にあるギャップマインダーのデータ。

ギャップマインダーのデータを使用した記事には以下のものもある。こちらは自分でバブルチャートを作成したもの。

bubblechart2017_pop
【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を使用する。

【plotly&kwargs】グラフ作成時の設定を後から追加できるように

続きを見る

hover_dataでマウスオーバーした際に表示する内容を設定している。今回はデータフレーム内の情報を全て表示するように設定した。

あとはconfigNoneと設定しグラフ表示時に使用、先程の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_colfacet_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_spacingfacet_col_spacingの同時設定も可能


facet_row_spacingfacet_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つでできる。

その分、自由度は下がるが、元データを希望通りになるように設定できていれば一瞬。個人的には行数指定もできるようにすればもっと楽だと感じたが。

本記事でpxsubplotsが使えるようになったので、これからグラフの自由度がもっと上がるだろう。

関連記事

【plt&subplot】pythonのpltで複数グラフを1つの画像に描く

続きを見る

【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く

続きを見る

【plotly&table+scatter】subplotsで表と散布図を同時にグラフ化

続きを見る

【plotly&円グラフ】subplotsやボタンの適用

続きを見る

関連コンテンツ

スポンサーリンク

Amazonのお買い物で損したない人へ

1回のチャージ金額通常会員プライム会員
¥90,000〜2.0%2.5%
¥40,000〜1.5%2.0%
¥20,000〜1.0%1.5%
¥5,000〜0.5%1.0%

Amazonギフト券にチャージすることでお得にお買い物できる。通常のAmazon会員なら最大2.0%、プライム会員なら2.5%還元なのでバカにならない。

ゲットしたポイントは通常のAmazonでのお買い物に使えるからお得だ。一度チャージしてしまえば、好きなタイミングでお買いものできる。

なお、有効期限は10年だから安心だ。いつでも気軽にAmazonでお買い物できる。

Amazonチャージはここから出来るで

もっとお得なAmazon Prime会員はこちらから

30日間無料登録

執筆者も便利に使わせてもらってる

スポンサーリンク

  • この記事を書いた人

メガネ

独学でpythonを学び天文学系の大学院を修了。 ガジェット好きでMac×Android使い。色んなスマホやイヤホンを購入したいけどお金がなさすぎて困窮中。 元々、人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂ったために今も人生めちゃくちゃ楽しい。 pythonとガジェットをメインにブログを書いていますので、興味を持たれましたらちょこちょこ訪問してくだされば幸いです🥰。 自己紹介→変わって楽しいの繰り返し

-px
-, , ,