📊

Pythonでレーダーチャート(Radar Chart)を簡単にプロットする

2024/04/02に公開

個人開発しているmatplotlibベースの円形データプロットライブラリ「pyCirclize」に新しく実装したレーダーチャートのプロット機能をこの記事では紹介します。

https://github.com/moshi4/pyCirclize

↓こんな感じのレーダーチャートが簡単に作成できます。
radar_chart_concat.png

開発目的

レーダーチャートはそれほど多く使う図ではないですが、学生時代に模試の平均点と自分の点数の比較などで見る機会はそれなりにあったので、ほとんどの人に馴染みがある図だと思います。よく知られている図なのでPythonでも既にレーダーチャートをプロットするライブラリもいくつかあると思いましたが、いい感じのライブラリは見当たらなかったです。そこで、個人開発しているpyCirclizeに新しくレーダーチャートのプロット機能を追加してみた。

pyCirclizeのインストール方法

pyCirclizeはPyPIconda-forgeにそれぞれパッケージを登録しているので、pipまたはcondaコマンドでインストール可能です。

PyPIパッケージ

pip install pycirclize

Condaパッケージ

conda install -c conda-forge pycirclize

レーダーチャートプロット例

pyCirclizeではCircos.radar_chart()メソッドでpandasデータフレームから簡単にレーダーチャートをプロットできます。以下にRPGでよく見かける職業ごとのパラメータを表現するレーダーチャートのプロットコード例をいくつか挙げます。

1. 標準プロット例

from pycirclize import Circos
import pandas as pd

# Create RPG jobs parameter dataframe (3 jobs, 6 parameters)
df = pd.DataFrame(
    data=[
        [80, 80, 80, 80, 80, 80],
        [90, 95, 95, 30, 30, 80],
        [60, 20, 20, 100, 90, 50],
    ],
    index=["Hero", "Warrior", "Wizard"],
    columns=["HP", "ATK", "DEF", "SP.ATK", "SP.DEF", "SPD"],
)
print(df)

# Initialize Circos instance for radar chart plot
circos = Circos.radar_chart(
    df,
    vmax=100,
    grid_interval_ratio=0.2,
)

# Plot figure
circos.savefig("example01.png")
         HP  ATK  DEF  SP.ATK  SP.DEF  SPD
Hero     80   80   80      80      80   80
Warrior  90   95   95      30      30   80
Wizard   60   20   20     100      90   50

example01.png

2. マーカー・凡例付プロット例

from pycirclize import Circos
import pandas as pd

# Create RPG jobs parameter dataframe (3 jobs, 7 parameters)
df = pd.DataFrame(
    data=[
        [80, 80, 80, 80, 80, 80, 80],
        [90, 20, 95, 95, 30, 30, 80],
        [60, 90, 20, 20, 100, 90, 50],
    ],
    index=["Hero", "Warrior", "Wizard"],
    columns=["HP", "MP", "ATK", "DEF", "SP.ATK", "SP.DEF", "SPD"],
)
print(df)

# Initialize Circos instance for radar chart plot
circos = Circos.radar_chart(
    df,
    vmax=100,
    marker_size=6,
    circular=True,
    cmap="Set2",
    grid_interval_ratio=0.25,
)

# Plot figure & set legend on upper right
fig = circos.plotfig()
_ = circos.ax.legend(loc="upper right")
fig.savefig("example02.png")
         HP  MP  ATK  DEF  SP.ATK  SP.DEF  SPD
Hero     80  80   80   80      80      80   80
Warrior  90  20   95   95      30      30   80
Wizard   60  90   20   20     100      90   50

example02.png

3. 詳細な設定をしたプロット例

from pycirclize import Circos
import pandas as pd

# Create RPG jobs parameter dataframe (4 jobs, 8 parameters)
df = pd.DataFrame(
    data=[
        [80, 80, 80, 80, 80, 80, 80, 80],
        [90, 20, 95, 95, 30, 30, 80, 70],
        [60, 90, 20, 20, 100, 90, 50, 70],
        [70, 50, 60, 40, 60, 40, 100, 60],
    ],
    index=["Hero", "Warrior", "Wizard", "Assassin"],
    columns=["HP", "MP", "ATK", "DEF", "SP.ATK", "SP.DEF", "SPD", "LUK"],
)
print(df)

# Initialize Circos instance for radar chart plot
circos = Circos.radar_chart(
    df,
    vmax=100,
    fill=False,
    marker_size=6,
    bg_color=None,
    cmap=dict(Hero="salmon", Warrior="skyblue", Wizard="lime", Assassin="magenta"),
    grid_interval_ratio=0.1,
    grid_label_formatter=lambda v: f"{v:.1f}pt",
    label_kws_handler=lambda _: dict(style="italic"),
    line_kws_handler=lambda _: dict(lw=2, ls="solid"),
    marker_kws_handler=lambda _: dict(marker="s", ec="grey", lw=0.5),
)
circos.text("RPG Jobs Radar Chart", r=125, size=15, weight="bold")

# Plot figure & set legend on upper right
fig = circos.plotfig()
_ = circos.ax.legend(
    loc="upper right",
    bbox_to_anchor=(1.05, 1.05),
    fontsize=10,
    title="RPG Jobs",
)
fig.savefig("example03.png")
          HP  MP  ATK  DEF  SP.ATK  SP.DEF  SPD  LUK
Hero      80  80   80   80      80      80   80   80
Warrior   90  20   95   95      30      30   80   70
Wizard    60  90   20   20     100      90   50   70
Assassin  70  50   60   40      60      40  100   60

example03.png

4. Subplotsへの複数プロット例

from pycirclize import Circos
import pandas as pd
import matplotlib.pyplot as plt

# Create RPG jobs parameter dataframe (4 jobs, 8 parameters)
df = pd.DataFrame(
    data=[
        [80, 80, 80, 80, 80, 80, 80, 80],
        [90, 20, 95, 95, 30, 30, 80, 70],
        [60, 90, 20, 20, 100, 90, 50, 70],
        [70, 50, 60, 40, 60, 40, 100, 60],
    ],
    index=["Hero", "Warrior", "Wizard", "Assassin"],
    columns=["HP", "MP", "ATK", "DEF", "SP.ATK", "SP.DEF", "SPD", "LUK"],
)
print(df)

# Create 2 x 2 subplots
fig = plt.figure(figsize=(16, 16), dpi=100)
fig.subplots(2, 2, subplot_kw=dict(polar=True))
fig.subplots_adjust(wspace=0.15, hspace=0.15)

# Plot radar chart into subplots for each target
for target_name, ax in zip(df.index, fig.axes):
    target_df = df.loc[[target_name]]

    # Initialize Circos instance for radar chart plot
    circos = Circos.radar_chart(
        target_df,
        vmax=100,
        marker_size=6,
        cmap=dict(Hero="salmon", Warrior="skyblue", Wizard="lime", Assassin="magenta"),
        grid_interval_ratio=0.2,
        line_kws_handler=lambda _: dict(lw=2, ls="solid"),
        marker_kws_handler=lambda _: dict(marker="D", ec="grey", lw=0.5),
    )

    # Plot figure & set legend on upper right
    circos.plotfig(ax=ax)
    circos.ax.legend(loc="upper right", fontsize=10)

fig.savefig("example04.png")
          HP  MP  ATK  DEF  SP.ATK  SP.DEF  SPD  LUK
Hero      80  80   80   80      80      80   80   80
Warrior   90  20   95   95      30      30   80   70
Wizard    60  90   20   20     100      90   50   70
Assassin  70  50   60   40      60      40  100   60

example04.png

その他の既存ライブラリ

  • matplotlib
    公式ドキュメントにレーダーチャートをプロットする例が紹介されていますが、少し複雑なコード例なので改変して自分の思い通りのものを作ろうとするとそれなりに苦労すると思う。

  • plotly
    こちらも公式ドキュメントにプロット例が紹介されていますが、デフォルトのデザインがいまいちに感じました。私自身がplotlyを十分に習熟していないのも一因ですが、より良いデザインを求めて改変しようとすると苦労しそう。インタラクティブな操作ができる点では優れているという印象です。

最後に

レーダーチャートくらいに有名なグラフであれば、扱いやすいPythonライブラリを誰かが作っているだろうと思いましたが、めぼしいものがなかったので自分で作ってみた。pyCirclizeはテーブルデータから簡単にレーダーチャートを作成できるので、Pythonでレーダーチャートをプロットしたいという方がいれば試してみてください。

Discussion