plotnine で python から ggplot2 ライクなグラフ描画を行う

この頃 ggplot に入門してみて、特定のデータをハイライトするなどちょっとこみいったチャートを作成したいシーンで使い勝手が良いなと感じました。EDA の時には慣れている matplotlib や seaborn  でささっと描画して、レポーティングの時に見栄えの良いチャートを作るなどの際には ggplot を使いたいと思いました。
python から(見た目だけではなく文法含め) ggplot ライクなグラフ描画ができるパッケージとして plotnine というものがあり、そちらを使ってきれいなチャートを描く方法についてメモしておこうと思います。

import numpy as np
import pandas as pd

from plotnine import *
from plotnine.dataset import mpg

# データの確認
mpg.head()

"""
manufacturer	model	displ	year	cyl	trans	drv	cty	hwy	fl	class
0	audi	a4	1.8	1999	4	auto(l5)	f	18	29	p	compact
1	audi	a4	1.8	1999	4	manual(m5)	f	21	29	p	compact
2	audi	a4	2.0	2008	4	manual(m6)	f	20	31	p	compact
3	audi	a4	2.0	2008	4	auto(av)	f	21	30	p	compact
4	audi	a4	2.8	1999	6	auto(l5)	f	16	26	p	compact
"""

plotnine でのグラフ描画

plotnine(ggplot2) では次のように様々なパーツを重ねていくような書き方をします。

① ggplot でキャンパスをセットする

ggplot() を実行すると白紙のキャンパスが表示されます。ここをベースレイヤーとしてこの上に様々な要素を重ねていく形になります。

グラフに使用するデータについては pandas のデータフレームで用意してこの段階で引数として渡します。(ここでデータを渡さずに②のタイミングで渡す事もできます。)

ggplot(data=mpg)

② geom_xxx でチャートを追加していく
様々なチャート描画のためのメソッドが用意されているので、描画したいものにあわせて選択します。この際に x軸にどのデータを使用するのか等は、mapping に渡します。mappting に渡す情報は aes() でくるんであげる必要があります。① でデータフレームを渡している場合には、カラム名を文字列で指定することができます。

(
    ggplot(data=mpg)
    + geom_histogram(mapping=aes(x="displ"), bins=12)
)

任意の変数を直接渡す事もできます。上図は次の様にも書けます。

displ = mpg.displ.to_list()

(
    ggplot()
    + geom_histogram(mapping=aes(x=displ), bins=12)
)

seaborn の hue 引数のようなイメージでもう一つカテゴリ変数を追加することもできます。 fill を指定すると塗りつぶし、color を指定すると枠線に色付してくれます。指定する変数は category 型である必要があり、データフレームに対して `mpg.xxx.astype("category")` と元データのデータ型を変換したり、あるいは`"factor(xxx)"` と行った形で引数に値を渡す際に変換をかけることもできます。

(
    ggplot(data=mpg)
    + geom_histogram(
        aes(x="displ", fill="factor(cyl)"),
        bins=12,
        alpha=.6
    )
)

要素をどんどん積み重ねていく書き方ですので、例えば箱ひげ図にプラスしてデータポイントを散らばせて表現したい、みたいな複雑な描画をしたい際にも、
- geom_point() で点を描画 ※ position 引数で散らばりを表現
- geom_boxplot() でその上に箱ひげ図を描画
と要素を分解して追記していくことで実現できます。

(
    ggplot(data=mpg, mapping=aes(x="class", y="displ"))
    + geom_point(
        aes(color="class"), 
        position=position_jitterdodge(jitter_width=1.0),
        alpha =.5
    )
    + geom_boxplot( 
        aes(fill="class"),
        alpha=.5
    )
)

③ 細かい装飾を設定する

細かい装飾のオプションがたくさんあり例えば下記を追加できます。

  • labs: タイトルや軸の名前の変更

  • theme_xxx: グラフのデザインテーマの変更。一覧

  • theme(figure_size=()): グラフのサイズの指定 

(
    ggplot(data=mpg)
    + geom_histogram(
        aes(x="displ", fill="factor(cyl)"),
        bins=12,
        alpha=.6
    )
    + labs(title="title", x="new xlabel", y="new ylabel")
    + theme_minimal()
    + theme(figure_size=(8, 4))
)


Tips

実際に使ってみてはじめに知っておきたかった内容をメモしておきます。 

複数のチャートを並べたい

matplotlib では、plt.add_subplots() などで m 行 n 列にチャートを並べられます。plotnine でそれをやる場合には(調べたところ)状況に応じて次の 2 通りの方法がありました。

① 任意のカテゴリカル変数の値別に並べる場合

例えば次のような散布図を "fl" 変数ごとに分けて表示する事を考えます。

(
    ggplot(data=mpg, mapping=aes(x="displ", y="hwy", color="factor(fl)"))
    + geom_point()
)

このようにある一つのカテゴリ変数の値ごとに描画したい場合は、facet_wrap という便利なメソッドが用意されています。

(
    ggplot(data=mpg, mapping=aes(x="displ", y="hwy", color="factor(fl)"))
    + geom_point()
    + facet_wrap("~fl", ncol=2)
)

ncol 引数で列数を指定でき、指定した値に応じて自動で n 行 m 列に分割して表示してくれます。

② 完全に独立したチャートを並べたい(subplots したい)場合

①と異なり、ggplot() + ~~ のチャートそのものがたくさんあり、それを並べたい場合は別のパッケージと組み合わせる必要があります。例えば使いやすそうなパッケージとしては下記の patchworklib があります。

https://zenn.dev/ponnhide/articles/40ac7dbe0aa4ab

なお subplots をどうやるか問題は下記の issue が立っています。

https://github.com/has2k1/plotnine/issues/46


任意の legend を設定したい

matplotlib や seaborn を使っていると、次のようにあるデータについてプロットしたときに label を指定して、plt.legend() で表示する、といったことをよくやります。

a = np.random.randn(30) + 2
b = np.random.randn(30) + 1
x = np.arange(1, 31, 1)

plt.figure(figsize=(10, 4))
plt.plot(a, label="a")
plt.plot(b, label="b")
plt.legend()

これと似たようなことを plotnine で行うには次のようにします。

(
    ggplot(aes(x=x))
    + geom_line(aes(y=a, color="'a'"))
    + geom_line(aes(y=b, color="'b'"))
    + scale_colour_manual(
        name="label",
        values=["#F8766D","#00BFC4"],
        # labels=("a","b")
    )
    + theme_minimal()
    + theme(figure_size=(10, 4))
)

`aes()` の中で color や fill でラベル名を指定してあげると色わけして表示してくれます。初めてやったときに沼にハマったのですが、普通に `color="a"` と指定すると "a" を文字列ではなく変数(データフレームを渡している場合は、データフレームのカラム)として処理されてしまうようなので、ラベル名(文字列)であることを教えてあげるために `color="'a'"` と二重にクオーテーションで囲って上げる必要がある点に注意です。
([4] に助けていただきました…)

`scale_colour_manual` を使うと、ラベルの見出しをつけたり、それぞれの色を直接指定する事もできます。


特定の要素をハイライトしたい

チャートで効果的にストーリーを伝えるために、強調したい箇所だけ色をつけるようなことがやりやすいです。下記では displ > 6 の要素だけ強調したい時の記述例です。

(
    ggplot(data=mpg, mapping=aes(x="displ", y="cty"))
    + geom_point()
    + geom_point(data=mpg.query("displ > 6"), colour="red")
)

ぱっと見て目をひかれる図は注意を引けて結果的に説得力は増すように思うので、こちらもマスターしたいものです。

Reference 

[1] plotnine documentation
[2] https://www.jaysong.net/tutorial/%C2%A0%20%C2%A0
[3] Data Visualization with PlotNine
[4] https://ill-identified.hatenablog.com/entry/2021/08/06/200859


最後まで目を通していただきありがとうございました。もし内容に誤りを見つけていただいた場合はご指摘いただけますと幸いです🙇‍♂️

この記事が気に入ったらサポートをしてみませんか?