Python

Python seaborn チュートリアル APIの概要 プロット機能(2)

原文のドキュメントはこちらから。

図形レベルと軸レベルの関数

seabornの関数には、「軸レベル」、「図形レベル」という分類がある。

軸レベルの関数(ヒストグラムやカーネル密度推定など)は、戻り値が単一のmatplotlib.pyplot.Axes オブジェクトにデータをプロットする。

図レベル関数は、図を管理する。普通はFacetGridを使用する。モジュールには単一の図レベル関数が用意され、軸レベル関数へインタフェースを提供する。

  • relplot(relational) – scatterplot – lineplot
  • displot(distributions) – histplot – kdeplot – ecdfplot – rugplot
  • catplot(categorical) – stripplot – swarmplot – boxplot – violinplot – pointplot – barplot

displot()は分布モジュールの図レベルの関数。デフォルト動作はヒストグラムを描画(内部的にはhistplot()と同じコードを使用)する。カーネル密度推定を描画するには(内部的にはkdeplot() )kind パラメータを用いる。

import seaborn as sns
import matplotlib.pyplot as plt

penguins = sns.load_dataset("penguins")
# ヒストグラムを表示
sns.displot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack")
# カーネル密度推定を描画(kdeplot())
sns.displot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack", kind="kde")
plt.show()

図レベルのプロットは、軸レベルと同じように見えるが、凡例がプロットの外側に配置され、形状も若干異なっている(詳しくは別記事を作成予定)。

図レベル関数が提供する最も便利な機能は、複数のサブプロットを持つ図を簡単に作成できる事だ。ペンギンの種別を3つの分布を同じ軸に重ねるのではなく、それぞれの分布を図の列にまたがってプロットし、それらの分布を切り分けることができる。

import seaborn as sns
import matplotlib.pyplot as plt

penguins = sns.load_dataset("penguins")
sns.displot(data=penguins, x="flipper_length_mm", hue="species", col="species")
plt.show()

図レベルの関数は、軸レベルで対応するものをラップし、その種類ごとに持つキーワード引数を基礎となる関数に渡す。

軸レベル関数は、自己完結型のプロットを作成

軸レベル関数は、軸ラベルと凡例を自動的に追加する。予測可能な任意に複雑なmatplotlib図に構成することができる。内部的にmatplotlib.pyplot.gca()を呼び出すが、matplotlibにフックして、アクティブな軸上にプロットを描画する(各プロットをどこに描画するか指定できる)。

import seaborn as sns
import matplotlib.pyplot as plt

penguins = sns.load_dataset("penguins")
f, axs = plt.subplots(1, 2, figsize=(8, 4), gridspec_kw=dict(width_ratios=[4, 3]))
sns.scatterplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species", ax=axs[0])
sns.histplot(data=penguins, x="species", hue="species", shrink=.8, alpha=.8, legend=False, ax=axs[1])
f.tight_layout()
plt.show()

図形レベルの関数と図形の所有

図形レベル関数は、簡単には他のプロットと一緒に構成できない。(図レベル関数が返すオブジェクトのmatplotlibの軸にアクセスして、プロットに他の要素を追加することはできる)

import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")
g = sns.relplot(data=tips, x="total_bill", y="tip")
g.ax.axline(xy1=(10, 2), slope=.2, color="b", dashes=(5, 2))
plt.show()

図形レベルの関数からプロットをカスタマイズ

図レベルの関数はFacetGridのインスタンスを返し、このインスタンスを使用して、サブプロットの構成、プロットの属性をカスタマイズできる。(1行で外部軸のラベルを変更ができる。)

import seaborn as sns
import matplotlib.pyplot as plt

penguins = sns.load_dataset("penguins")
g = sns.relplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", col="sex")
g.set_axis_labels("Flipper length (mm)", "Bill length (mm)")
plt.show()

※ set_axis_labels() はmatplotlib APIの一部ではなく、図形レベルの関数を使用している場合にのみ存在する

図形サイズの指定

matplotlibプロットの図全体の幅と高さは、rcParamsで、プロットを設定している間か、図オブジェクト上のメソッドで設定する。seabornで軸レベル関数を使用する場合、同じルールが適用される。プロットのサイズは、プロットに含まれる図のサイズと軸レイアウトによって決定される。

図レベル関数を使用する場合、いくつかの重要な違いがある。最も重要なことは、パラメータは全体の図のサイズではなく、各サブプロットのサイズに対応していること。また、関数自体が図のサイズを制御するためのパラメータを持っており、高さとアスペクトは、matplotlibの幅と高さのパラメータ(seabornのパラメータを使用して、幅 = 高さ * apsect)とは異なるサイズである。

アプローチの違いを説明するために、1つのサブプロットを持つmatplotlib.pyplot.subplots()のデフォルト出力を示す。

import seaborn as sns
import matplotlib.pyplot as plt

penguins = sns.load_dataset("penguins")
f, ax = plt.subplots()
plt.show()

複数の列を持つ図形は全体の大きさは同じだが、軸は空間に収まるように縦を狭めて配置する。

import seaborn as sns
import matplotlib.pyplot as plt

penguins = sns.load_dataset("penguins")
f, ax = plt.subplots(1, 2, sharey=True)
plt.show()

図レベルの関数で作成されたプロットは以下のように正方形になる。確認のために、FacetGridを直接使って空のプロットを設定する。これは、relplot()、displot()、catplot()のような関数の裏で実行される。

import seaborn as sns
import matplotlib.pyplot as plt

penguins = sns.load_dataset("penguins")
g = sns.FacetGrid(penguins)
plt.show()

列が追加されると、図の幅が広くなり、サブプロットが同じ大きさと形状を持つようになる。

import seaborn as sns
import matplotlib.pyplot as plt

penguins = sns.load_dataset("penguins")
g = sns.FacetGrid(penguins, col="sex")
plt.show()

以下のように行と列の合計数を考慮することなく、各サブプロットのサイズと形状を調整できる。

import seaborn as sns
import matplotlib.pyplot as plt

penguins = sns.load_dataset("penguins")
g = sns.FacetGrid(penguins, col="sex", height=3.5, aspect=.75)
plt.show()

図の全体のサイズの調整方法を考えることなく、変数を割り当てることができるようになった。
※図のサイズを変更したいときに、matplotlibとは少し違った挙動をする。

図形レベル関数の相対的なメリット

賛否両論をまとめる。

メリットデメリット
データ変数による簡単化関数シグネチャにない多くのパラメータ
デフォルトではプロット外のレジェンドより大きなmatplotlib図の一部にできない
図形レベルのカスタマイズが容易matplotlibとは異なるAPI
異なる図形サイズのパラメータ化異なる図形サイズのパラメータ化

バランス的に、図レベル関数は、初心者にとっては混乱を招くような複雑さを追加している。チュートリアルのドキュメントでは、レベル関数の方がよりきれいなプロットができるため、ほとんどの場合、図レベル関数を使用している。複数の異なる種類のプロットを構成する複雑なスタンドアロンの図を作成する必要がある場合は図レベル関数の選択が良いとは言えない。