円グラフ#

準備#

Import#

Hide code cell content
# warningsモジュールのインポート
import warnings

# データ解析や機械学習のライブラリ使用時の警告を非表示にする目的で警告を無視
# 本書の文脈では、可視化の学習に議論を集中させるために選択した
# ただし、学習以外の場面で、警告を無視する設定は推奨しない
warnings.filterwarnings("ignore")
Hide code cell content
# pathlibモジュールのインポート
# ファイルシステムのパスを扱う
from pathlib import Path

# numpy:数値計算ライブラリのインポート
# npという名前で参照可能
import numpy as np

# pandas:データ解析ライブラリのインポート
# pdという名前で参照可能
import pandas as pd

# plotly.expressのインポート
# インタラクティブなグラフ作成のライブラリ
# pxという名前で参照可能
import plotly.express as px

# plotly.graph_objectsからFigureクラスのインポート
# 型ヒントの利用を主目的とする
from plotly.graph_objects import Figure

変数#

Hide code cell content
# マンガデータ保存ディレクトリのパス
DIR_CM = Path("../../../data/cm/input")
# アニメデータ保存ディレクトリのパス
DIR_AN = Path("../../../data/an/input")
# ゲームデータ保存ディレクトリのパス
DIR_GM = Path("../../../data/gm/input")

# マンガデータの分析結果の出力先ディレクトリのパス
DIR_OUT_CM = (
    DIR_CM.parent / "output" / Path.cwd().parts[-2] / Path.cwd().parts[-1] / "pie"
)
# アニメデータの分析結果の出力先ディレクトリのパス
DIR_OUT_AN = (
    DIR_AN.parent / "output" / Path.cwd().parts[-2] / Path.cwd().parts[-1] / "pie"
)
# ゲームデータの分析結果の出力先ディレクトリのパス
DIR_OUT_GM = (
    DIR_GM.parent / "output" / Path.cwd().parts[-2] / Path.cwd().parts[-1] / "pie"
)
Hide code cell content
# 読み込み対象ファイル名の定義

# マンガ作品とマンガ作者の対応関係に関するファイル
FN_CC_CRT = "cm_cc_crt.csv"

# マンガ各話に関するファイル
FN_CE = "cm_ce.csv"

# アニメ作品と原作者の対応関係に関するファイル
FN_AC_ACT = "an_ac_act.csv"

# アニメ各話に関するファイル
FN_AE = "an_ae.csv"

# ゲームパッケージとプラットフォームの対応関係に関するファイル
FN_PKG_PF = "gm_pkg_pf.csv"
Hide code cell content
# plotlyの描画設定の定義

# plotlyのグラフ描画用レンダラーの定義
# Jupyter Notebook環境のグラフ表示に適切なものを選択
RENDERER = "plotly_mimetype+notebook"
Hide code cell content
# 可視化に関する設定値の定義

# 「年代」の集計単位
UNIT_YEARS = 10
Hide code cell content
# pandasのweekday関数で取得できる曜日の数値と実際の曜日名を対応させる辞書を定義
# 0:月曜日, 1:火曜日, ... , 6:日曜日
WEEKDAY2YOBI = {
    0: "月",
    1: "火",
    2: "水",
    3: "木",
    4: "金",
    5: "土",
    6: "日",
}
Hide code cell content
# 質的変数の描画用のカラースケールの定義

# Okabe and Ito (2008)基準のカラーパレット
# 色の識別性が高く、多様な色覚の人々にも見やすい色組み合わせ
# 参考URL: https://jfly.uni-koeln.de/color/#pallet
OKABE_ITO = [
    "#000000",  # 黒 (Black)
    "#E69F00",  # 橙 (Orange)
    "#56B4E9",  # 薄青 (Sky Blue)
    "#009E73",  # 青緑 (Bluish Green)
    "#F0E442",  # 黄色 (Yellow)
    "#0072B2",  # 青 (Blue)
    "#D55E00",  # 赤紫 (Vermilion)
    "#CC79A7",  # 紫 (Reddish Purple)
]

関数#

Hide code cell source
def show_fig(fig: Figure) -> None:
    """
    所定のレンダラーを用いてplotlyの図を表示
    Jupyter Bookなどの環境での正確な表示を目的とする

    Parameters
    ----------
    fig : Figure
        表示対象のplotly図

    Returns
    -------
    None
    """

    # 図の周囲の余白を設定
    # t: 上余白
    # l: 左余白
    # r: 右余白
    # b: 下余白
    fig.update_layout(margin=dict(t=25, l=25, r=25, b=25))

    # 所定のレンダラーで図を表示
    fig.show(renderer=RENDERER)
Hide code cell content
def add_years_to_df(
    df: pd.DataFrame, unit_years: int = UNIT_YEARS, col_date: str = "date"
) -> pd.DataFrame:
    """
    データフレームにunit_years単位で区切った年数を示す新しい列を追加

    Parameters
    ----------
    df : pd.DataFrame
        入力データフレーム
    unit_years : int, optional
        年数を区切る単位、デフォルトはUNIT_YEARS
    col_date : str, optional
        日付を含むカラム名、デフォルトは "date"

    Returns
    -------
    pd.DataFrame
        新しい列が追加されたデータフレーム
    """

    # 入力データフレームをコピー
    df_new = df.copy()

    # unit_years単位で年数を区切り、新しい列として追加
    df_new["years"] = (
        pd.to_datetime(df_new[col_date]).dt.year // unit_years * unit_years
    )

    # 'years'列のデータ型を文字列に変更
    df_new["years"] = df_new["years"].astype(str)

    return df_new
Hide code cell content
def save_df_to_csv(df: pd.DataFrame, dir_save: Path, fn_save: str) -> None:
    """
    DataFrameをCSVファイルとして指定されたディレクトリに保存する関数

    Parameters
    ----------
    df : pd.DataFrame
        保存対象となるDataFrame
    dir_save : Path
        出力先ディレクトリのパス
    fn_save : str
        保存するCSVファイルの名前(拡張子は含めない)
    """
    # 出力先ディレクトリが存在しない場合は作成
    dir_save.mkdir(parents=True, exist_ok=True)

    # 出力先のパスを作成
    p_save = dir_save / f"{fn_save}.csv"

    # DataFrameをCSVファイルとして保存する
    df.to_csv(p_save, index=False, encoding="utf-8-sig")

    # 保存完了のメッセージを表示する
    print(f"DataFrame is saved as '{p_save}'.")

可視化例#

マンガデータ#

Hide code cell content
# pandasのread_csv関数でCSVファイルの読み込み
df_cc_crt = pd.read_csv(DIR_CM / FN_CC_CRT)
Hide code cell content
# crtidごとのmcid数を集計し、n_mc列として追加した中間DataFrame
df_tmp = df_cc_crt.groupby("crtid")["mcid"].nunique().reset_index(name="n_mc")
# 複数のマンガ雑誌に掲載経験のあるcrtid一覧をリスト化
crtids2drop = df_tmp[df_tmp["n_mc"] > 1]["crtid"].to_list()
# df_cc_crtからcrtid2dropに含まれる行を削除
df_cc_crt = df_cc_crt[~df_cc_crt["crtid"].isin(crtids2drop)].reset_index(drop=True)
Hide code cell content
# 各crtidに対して、関連するmcnameのユニークな数を数える
# groupbyでcrtidごとにグループ化し、nuniqueメソッドで各グループのmcnameのユニークな数を数える
# all関数を使って、nuniqueの結果が全て1であること(各crtidが1つのmcnameにのみ紐づいていること)を確認
assert all(df_cc_crt.groupby("crtid")["mcname"].nunique() == 1)
Hide code cell content
# マンガ雑誌ごとのユニークな原作者数を集計

# マンガ雑誌名(mcname)ごとに、ユニークな原作者名(crtname)の数を集計
# reset_index(name="n_crt")を使用して集計結果を新しいデータフレームに変換し、
# 集計された原作者数を "n_crt" という列名で保存
df_cm = df_cc_crt.groupby("mcname")["crtname"].nunique().reset_index(name="n_crt")

# 列名をわかりやすく変更
df_cm = df_cm.rename(columns={"mcname": "マンガ雑誌名", "n_crt": "作者数"})

# 円グラフの表示順序が、その割合が大きい順序になるように制御
orders_cm = {
    "マンガ雑誌名": df_cm.sort_values("作者数", ascending=False)[
        "マンガ雑誌名"
    ].tolist()
}
Hide code cell content
# 可視化対象のDataFrameを確認
df_cm.head()
マンガ雑誌名 作者数
0 週刊少年サンデー 593
1 週刊少年ジャンプ 849
2 週刊少年チャンピオン 755
3 週刊少年マガジン 670
Hide code cell content
# 可視化対象DataFrameを保存
save_df_to_csv(df_cm, DIR_OUT_CM, "cm")
DataFrame is saved as '../../../data/cm/output/vol2/03/pie/cm.csv'.
Hide code cell source
# マンガ雑誌ごとの原作者数を円グラフで可視化

# px.pie関数を使用して、各マンガ雑誌ごとの原作者数の円グラフを作成
# "作者数" を値として、"マンガ雑誌名" を名前として表示
# category_ordersにより各水準の表示順序を制御
# color_discrete_sequenceにはOKABE_ITOカラースケールを使用
fig = px.pie(
    df_cm,
    values="作者数",
    names="マンガ雑誌名",
    category_orders=orders_cm,
    color_discrete_sequence=OKABE_ITO,
)

# 凡例名を追加
fig.update_layout(legend_title_text="マンガ雑誌名")

# 作成した円グラフを表示
show_fig(fig)
Hide code cell content
# pandasのread_csv関数でCSVファイルの読み込み
df_ce = pd.read_csv(DIR_CM / FN_CE)
Hide code cell content
# df_ceに年代情報を追加するための関数add_years_to_dfを適用
# これにより、df_ceに年代情報が含まれるようになる
df_ce = add_years_to_df(df_ce)

# 年代(years)とccidのユニークな組み合わせを持つデータフレームを作成
# 重複するデータを削除し、必要なカラムのみを選択
# ignore_index=Trueで新しいインデックスを割り当てている
df_cc_years = df_ce.drop_duplicates(subset=["years", "ccid"], ignore_index=True)[
    ["ccid", "years"]
]
Hide code cell content
# df_cc_crtとdf_cc_yearsをccidを基準にして右結合(right join)し、df_mergeを作成
# これにより、df_cc_crtのデータにdf_cc_yearsの年代情報が組み合わされる
df_merge = pd.merge(df_cc_crt, df_cc_years, on="ccid", how="right")

# df_mergeをマンガ雑誌名と年代ごとにグループ化し、作者のユニーク数を集計
# nunique()を使用して各グループ内のユニークな作者数をカウント
df_cm2 = (
    df_merge.groupby(["mcname", "years"])["crtid"].nunique().reset_index(name="n_crt")
)

# 列名をわかりやすい名前に変更
# mcnameをマンガ雑誌名、yearsを年代、n_crtを作者数にリネーム
df_cm2 = df_cm2.rename(
    columns={"mcname": "マンガ雑誌名", "years": "年代", "n_crt": "作者数"}
)
Hide code cell content
# 年代別の合計作者数を念のため確認
df_cm2.groupby("年代")["作者数"].sum().reset_index()
年代 作者数
0 1970 493
1 1980 682
2 1990 650
3 2000 787
4 2010 881
Hide code cell content
# 可視化対象のDataFrameを確認
df_cm2.head()
マンガ雑誌名 年代 作者数
0 週刊少年サンデー 1970 95
1 週刊少年サンデー 1980 157
2 週刊少年サンデー 1990 147
3 週刊少年サンデー 2000 162
4 週刊少年サンデー 2010 179
Hide code cell content
# 可視化対象DataFrameを保存
save_df_to_csv(df_cm2, DIR_OUT_CM, "cm2")
DataFrame is saved as '../../../data/cm/output/vol2/03/pie/cm2.csv'.
Hide code cell source
# マンガ雑誌ごとの原作者数を円グラフで可視化
# valuesに作者数、namesにマンガ雑誌名を指定し、年代ごとにファセット分割
# category_ordersでカテゴリの順序を指定し、color_discrete_sequenceで色を設定
fig = px.pie(
    df_cm2,
    values="作者数",
    names="マンガ雑誌名",
    facet_col="年代",
    category_orders=orders_cm,
    color_discrete_sequence=OKABE_ITO,
)

# 円グラフの凡例のタイトルを「マンガ雑誌名」に設定
fig.update_layout(legend_title_text="マンガ雑誌名")

# ファセット(年代ごとの円グラフ)のタイトルを簡潔にする処理
# デフォルトではタイトルは「年代=xxx」という形式になっている
# この処理は「=」で文字列を分割して「xxx」の部分だけを取り出し、
# タイトルを「xxx」だけの簡潔な形に更新している
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))

# 作成した円グラフを表示
show_fig(fig)
Hide code cell content
# 週刊少年サンデーのデータを表示
df_cm2[df_cm2["マンガ雑誌名"] == "週刊少年サンデー"]
マンガ雑誌名 年代 作者数
0 週刊少年サンデー 1970 95
1 週刊少年サンデー 1980 157
2 週刊少年サンデー 1990 147
3 週刊少年サンデー 2000 162
4 週刊少年サンデー 2010 179

アニメデータ#

Hide code cell content
# pandasのread_csv関数でCSVファイルの読み込み
df_ac_act = pd.read_csv(DIR_AN / FN_AC_ACT)
Hide code cell content
# 各crtidに対して、関連するgenderのユニークな数を数える
# groupbyでcrtidごとにグループ化し、nuniqueメソッドで各グループのgenderのユニークな数を数える
# all関数を使って、nuniqueの結果が全て1であること(各crtidが1つのgenderにのみ紐づいていること)を確認
assert all(df_ac_act.groupby("actid")["gender"].nunique() == 1)
Hide code cell content
# 性別ごとの声優数を集計
# 性別を基準にグループ化して、ユニークな声優IDの数(声優数)をカウント
df_an = df_ac_act.groupby("gender")["actid"].nunique().reset_index(name="n_act")

# カラム名をわかりやすく変更("性別"、"声優数")
df_an = df_an.rename(columns={"gender": "性別", "n_act": "声優数"})

# 声優数が多い順に配置されるように修正
orders_an = {"性別": df_an.sort_values("声優数", ascending=False)["性別"].tolist()}
Hide code cell content
# 可視化対象のDataFrameを確認
df_an.head()
性別 声優数
0 female 1664
1 male 1334
Hide code cell content
# 可視化対象DataFrameを保存
save_df_to_csv(df_an, DIR_OUT_AN, "an")
DataFrame is saved as '../../../data/an/output/vol2/03/pie/an.csv'.
Hide code cell source
# 性別ごとの声優数を円グラフで可視化

# px.pie関数を使用して、性別ごとの声優数の割合を円グラフで表示
# valuesに声優数、namesに性別を指定してデータをマッピング
# category_ordersで配置順序を指定
# color_discrete_sequenceでOKABE_ITOスケールを指定
fig = px.pie(
    df_an,
    values="声優数",
    names="性別",
    category_orders=orders_an,
    color_discrete_sequence=OKABE_ITO,
)

# 凡例名を追加
fig.update_layout(legend_title_text="声優の性別")

# 作成した円グラフを表示
show_fig(fig)
Hide code cell content
# pandasのread_csv関数でCSVファイルの読み込み
df_ae = pd.read_csv(DIR_AN / FN_AE)
Hide code cell content
# df_aeに年代情報を追加するための関数add_years_to_dfを適用
# unit_years=5を指定することで、5年単位の年代情報を追加する
df_ae = add_years_to_df(df_ae, unit_years=5)

# acidとyears(5年単位の年代)のユニークな組み合わせを持つDataFrameを作成
# 重複するデータを削除し、必要なカラムのみを選択
# ignore_index=Trueで新しいインデックスを割り当てる
df_ac_years = df_ae.drop_duplicates(subset=["acid", "years"], ignore_index=True)[
    ["acid", "years"]
]
Hide code cell content
# df_ac_actとdf_ac_yearsをacidを基準にして左結合(left join)し、df_mergeを作成
# これにより、df_ac_actのデータにdf_ac_yearsの年代情報が組み合わされる
df_merge = pd.merge(df_ac_act, df_ac_years, on="acid", how="left")

# df_mergeを性別と年代ごとにグループ化し、声優のユニーク数を集計
# nunique()を使用して各グループ内のユニークな声優数をカウント
df_an2 = (
    df_merge.groupby(["gender", "years"])["acid"].nunique().reset_index(name="n_act")
)

# 列名をわかりやすい名前に変更
df_an2 = df_an2.rename(columns={"gender": "性別", "years": "年代", "n_act": "声優数"})
Hide code cell content
# 年代別の声優数を集計
df_an2.groupby("年代")["声優数"].sum().reset_index()
年代 声優数
0 1960 4
1 1965 4
2 1970 6
3 1975 4
4 1980 2
5 1990 8
6 1995 137
7 2000 1113
8 2005 1695
9 2010 1654
10 2015 1149
Hide code cell content
# 2000年代以降を可視化対象とする
df_an2 = df_an2[df_an2["年代"].astype(int) >= 2000].reset_index(drop=True)
Hide code cell content
# 可視化対象のDataFrameを確認
df_an2.head()
性別 年代 声優数
0 female 2000 561
1 female 2005 844
2 female 2010 856
3 female 2015 597
4 male 2000 552
Hide code cell content
# 可視化対象DataFrameを保存
save_df_to_csv(df_an2, DIR_OUT_AN, "an2")
DataFrame is saved as '../../../data/an/output/vol2/03/pie/an2.csv'.
Hide code cell source
# 性別ごとの声優数を円グラフで可視化

# px.pie関数を使用して、性別ごとの声優数の割合を円グラフで表示
# valuesに声優数、namesに性別を指定してデータをマッピング
# facet_colとして年代を指定することで、年代別の円グラフを表示
# category_ordersで配置順序を指定
# color_discrete_sequenceでOKABE_ITOスケールを指定
fig = px.pie(
    df_an2,
    values="声優数",
    names="性別",
    facet_col="年代",
    category_orders=orders_an,
    color_discrete_sequence=OKABE_ITO,
)

# 凡例名を追加
fig.update_layout(legend_title_text="声優の性別")

# ファセット(年代ごとの円グラフ)のタイトルを簡潔にする処理
# デフォルトではタイトルは「年代=xxx」という形式になっている
# この処理は「=」で文字列を分割して「xxx」の部分だけを取り出す
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))

# 作成した円グラフを表示
show_fig(fig)
Hide code cell source
# 性別ごとの声優数を円グラフで可視化

# px.pie関数を使用して、性別ごとの声優数の割合を円グラフで表示
# valuesに声優数、namesに性別を指定してデータをマッピング
# category_ordersで配置順序を指定
# color_discrete_sequenceでOKABE_ITOスケールを指定
fig = px.pie(
    df_an2,
    values="声優数",
    names="性別",
    facet_col="年代",
    # category_orders=orders_an,
    color_discrete_sequence=OKABE_ITO,
)

# 凡例名を追加
fig.update_layout(legend_title_text="声優の性別")

# ファセット(年代ごとの円グラフ)のタイトルを簡潔にする処理
# デフォルトではタイトルは「年代=xxx」という形式になっている
# この処理は「=」で文字列を分割して「xxx」の部分だけを取り出す
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))

# 作成した円グラフを表示
show_fig(fig)

ゲームデータ#

Hide code cell content
# pandasのread_csv関数でCSVファイルの読み込み
df_pkg_pf = pd.read_csv(DIR_GM / FN_PKG_PF)
Hide code cell content
# date列をdatetimeオブジェクトに変換して、曜日情報を新たな列としてdf_pkg_pfに追加
df_pkg_pf["weekday"] = pd.to_datetime(df_pkg_pf["date"]).dt.weekday
Hide code cell content
# 各pkgidに対して、関連するweekdayのユニークな数を数える
# groupbyでpkgidごとにグループ化し、nuniqueメソッドで各グループのweekdayのユニークな数を数える
# all関数を使って、nuniqueの結果が全て1であること(各pkgidが1つのweekdayにのみ紐づいていること)を確認
assert all(df_pkg_pf.groupby("pkgid")["weekday"].nunique() == 1)
Hide code cell content
# 曜日ごとのパッケージ数を集計するためのデータ前処理

# 曜日ごとにユニークなパッケージIDの数を集計
df_gm = df_pkg_pf.groupby("weekday")["pkgid"].nunique().reset_index(name="n_pkg")

# 数値で表されている曜日を文字列にマッピング
df_gm["yobi"] = df_gm["weekday"].apply(lambda x: WEEKDAY2YOBI.get(x, None))

# 列名をリネーム
df_gm = df_gm.rename(columns={"n_pkg": "パッケージ数", "yobi": "発売曜日"})

# パッケージ数順に表示されるように指定する場合
orders_gm_pkg = {
    "発売曜日": df_gm.sort_values("パッケージ数", ascending=False)["発売曜日"].tolist()
}
Hide code cell content
# 可視化対象のDataFrameを確認
df_gm.head()
weekday パッケージ数 発売曜日
0 0 281
1 1 1692
2 2 4515
3 3 22844
4 4 5331
Hide code cell content
# 可視化対象DataFrameを保存
save_df_to_csv(df_gm, DIR_OUT_GM, "gm")
DataFrame is saved as '../../../data/gm/output/vol2/03/pie/gm.csv'.
Hide code cell source
# 曜日ごとのパッケージ数を円グラフで可視化

# px.pieを使用して、曜日ごとのパッケージ数の円グラフを作成
# df_gmデータフレームを使用し、'発売曜日'列を名前、'パッケージ数'列を値としてプロット
# 表示順序が割合の降順になるよう、category_ordersで指定
# OKABE_ITOカラースキームを使用
fig = px.pie(
    df_gm,
    values="パッケージ数",
    names="発売曜日",
    category_orders=orders_gm_pkg,
    color_discrete_sequence=OKABE_ITO,
)

# 凡例名を追加
fig.update_layout(legend_title_text="発売曜日")

# 作成した円グラフを表示
show_fig(fig)
Hide code cell source
# 曜日順に表示されるように指定する場合
orders_gm_weekday = {"発売曜日": df_gm.sort_values("weekday")["発売曜日"].tolist()}

# px.pieを使用して、曜日ごとのパッケージ数の円グラフを作成
# df_gmデータフレームを使用し、'発売曜日'列を名前、'パッケージ数'列を値としてプロット
# 表示順序が曜日順になるよう、category_ordersで指定
# OKABE_ITOカラースキームを使用
fig = px.pie(
    df_gm,
    values="パッケージ数",
    names="発売曜日",
    category_orders=orders_gm_weekday,
    color_discrete_sequence=OKABE_ITO,
)

# 凡例名を追加
fig.update_layout(legend_title_text="発売曜日")

# 作成した円グラフを表示
show_fig(fig)
Hide code cell content
# df_pkg_pfに年代(5年刻み)を追加
df_pkg_pf = add_years_to_df(df_pkg_pf, unit_years=5)
Hide code cell content
# years、weekdayごとにpkgidのユニーク数を集計し、n_pkg列として追加
df_gm2 = (
    df_pkg_pf.groupby(["years", "weekday"])["pkgid"].nunique().reset_index(name="n_pkg")
)

# 数値で表されている曜日を文字列にマッピング
df_gm2["yobi"] = df_gm2["weekday"].apply(lambda x: WEEKDAY2YOBI.get(x, None))

# 可視化用に列名をリネーム
df_gm2 = df_gm2.rename(
    columns={"years": "年代", "yobi": "発売曜日", "n_pkg": "パッケージ数"}
)
Hide code cell content
# 年代別の合計ゲームパッケージ数を集計
df_gm2.groupby("年代")["パッケージ数"].sum().reset_index()
年代 パッケージ数
0 1980 53
1 1985 453
2 1990 2074
3 1995 4590
4 2000 5587
5 2005 7637
6 2010 9344
7 2015 5900
Hide code cell content
# 1990年代以降を抽出して、可視化対象とする
df_gm2 = df_gm2[df_gm2["年代"].astype(int) >= 1990].reset_index(drop=True)
Hide code cell content
# 可視化対象のDataFrameを確認
df_gm2.head()
年代 weekday パッケージ数 発売曜日
0 1990 0 34
1 1990 1 81
2 1990 2 66
3 1990 3 122
4 1990 4 1559
Hide code cell content
# 可視化対象DataFrameを保存
save_df_to_csv(df_gm2, DIR_OUT_GM, "gm2")
DataFrame is saved as '../../../data/gm/output/vol2/03/pie/gm2.csv'.
Hide code cell source
# 曜日ごとのパッケージ数を円グラフで可視化

# px.pieを使用して、曜日ごとのパッケージ数の円グラフを作成
# df_gm2データフレームを使用し、'発売曜日'列を名前、'パッケージ数'列を値としてプロット
# facet_colとして年代を指定することで、年代別の円グラフを表示
# 表示順序が曜日順になるよう、category_ordersで指定
# OKABE_ITOカラースキームを使用
fig = px.pie(
    df_gm2,
    values="パッケージ数",
    names="発売曜日",
    facet_col="年代",
    category_orders=orders_gm_weekday,
    color_discrete_sequence=OKABE_ITO,
)

# textinfo: パーセントのみ表示(ラベル名を含めると長くなりすぎるため)
# textposition: グラフの内側に配置
fig.update_traces(textposition="inside", textinfo="percent")

# 凡例名を追加し、フォントサイズが12px未満になる場合はラベルを隠す設定
fig.update_layout(
    legend_title_text="発売曜日", uniformtext_minsize=12, uniformtext_mode="hide"
)

# ファセット(年代ごとの円グラフ)のタイトルを簡潔にする処理
# デフォルトではタイトルは「年代=xxx」という形式になっている
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[-1]))

# 作成した円グラフを表示
show_fig(fig)