seaborn.objects インターフェース#
seaborn.objects
名前空間は、バージョン 0.12 で seaborn プロットを作成するための全く新しいインターフェースとして導入されました。これは、データを処理およびプロットするための、構成可能なクラスの集合からなる、より一貫性があり柔軟な API を提供します。既存の seaborn
関数とは対照的に、新しいインターフェースは、matplotlib に切り替えることなく(必要であれば引き続き可能です)、エンドツーエンドのプロットの仕様とカスタマイズをサポートすることを目的としています。
注記
objects インターフェースは現在実験的であり、不完全です。本格的な使用には十分安定していますが、いくつかの粗い部分や欠けている機能が確かにあります。
プロットの指定とデータのマッピング#
objects インターフェースは、次の規則に従ってインポートする必要があります。
import seaborn.objects as so
seaborn.objects
名前空間は、関連するすべてのクラスへのアクセスを提供します。最も重要なのは Plot
です。 Plot
オブジェクトをインスタンス化し、そのメソッドを呼び出すことでプロットを指定します。簡単な例を見てみましょう。
(
so.Plot(penguins, x="bill_length_mm", y="bill_depth_mm")
.add(so.Dot())
)

このコードは散布図を作成しますが、かなり馴染みのあるものに見えるはずです。seaborn.scatterplot()
を使用する場合と同様に、整然としたデータフレーム(penguins
)を渡し、その2つの列をプロットの x
座標と y
座標に割り当てました。しかし、チャートの種類から始めてデータの割り当てを追加するのではなく、ここではデータの割り当てから始めてグラフィック要素を追加しました。
プロパティの設定#
Dot
クラスは Mark
の例です。データ値をグラフィカルに表現するオブジェクトです。各マークには、外観を変更するために設定できる多くのプロパティがあります。
(
so.Plot(penguins, x="bill_length_mm", y="bill_depth_mm")
.add(so.Dot(color="g", pointsize=4))
)

プロパティのマッピング#
seaborn の関数と同様に、データ値をさまざまなグラフィカルプロパティにマッピングすることも可能です。
(
so.Plot(
penguins, x="bill_length_mm", y="bill_depth_mm",
color="species", pointsize="body_mass_g",
)
.add(so.Dot())
)

この基本的な機能は目新しいものではありませんが、関数 API との重要な違いは、プロパティが、直接設定する場合と同じパラメータ名を使用してマッピングされることです(hue
対 color
などではなく)。重要なのはプロパティがどこで定義されているかです。Dot
を初期化するときに値を渡すと直接設定され、Plot
を設定するときに変数を割り当てると、対応するデータがマッピングされます。
この違い以外にも、objects インターフェースでは、はるかに幅広いマークプロパティをマッピングできます。
(
so.Plot(
penguins, x="bill_length_mm", y="bill_depth_mm",
edgecolor="sex", edgewidth="body_mass_g",
)
.add(so.Dot(color=".8"))
)

グループの定義#
Dot
マークは各データポイントを個別に表現するため、変数をプロパティに割り当てても、各ドットの外観を変更する効果しかありません。Line
など、観測値をグループ化または接続するマークの場合、個別のグラフィック要素の数も決定します。
(
so.Plot(healthexp, x="Year", y="Life_Expectancy", color="Country")
.add(so.Line())
)

group
を使用して、視覚的なプロパティを変更せずにグループを定義することも可能です。
(
so.Plot(healthexp, x="Year", y="Life_Expectancy", group="Country")
.add(so.Line())
)

プロット前のデータ変換#
統計的変換#
多くの seaborn 関数と同様に、objects インターフェースは統計的変換をサポートしています。これらは Stat
オブジェクト(Agg
など)によって実行されます。
(
so.Plot(penguins, x="species", y="body_mass_g")
.add(so.Bar(), so.Agg())
)

関数インターフェースでは、一部の視覚表現(例:seaborn.barplot()
)では統計的変換が可能ですが、他の表現(例:seaborn.scatterplot()
)では不可能です。objects インターフェースは表現と変換をより明確に分離し、Mark
オブジェクトと Stat
オブジェクトを合成することができます。
(
so.Plot(penguins, x="species", y="body_mass_g")
.add(so.Dot(pointsize=10), so.Agg())
)

プロパティをマッピングしてグループを形成する場合、Stat
変換は各グループに個別に適用されます。
(
so.Plot(penguins, x="species", y="body_mass_g", color="sex")
.add(so.Dot(pointsize=10), so.Agg())
)

オーバープロットの解決#
一部の seaborn 関数には、seaborn.barplot()
が hue
が割り当てられるとバーを「ずらす」場合など、オーバープロットを自動的に解決するメカニズムもあります。objects インターフェースのデフォルトの動作はそれほど複雑ではありません。複数のグループを表すバーは、デフォルトでは重なります。
(
so.Plot(penguins, x="species", y="body_mass_g", color="sex")
.add(so.Bar(), so.Agg())
)

それにもかかわらず、Bar
マークを Agg
stat と、Dodge
によって実装された2番目の変換と合成することができます。
(
so.Plot(penguins, x="species", y="body_mass_g", color="sex")
.add(so.Bar(), so.Agg(), so.Dodge())
)

Dodge
クラスは Move
変換の例であり、Stat
と似ていますが、x
と y
座標のみを調整します。Move
クラスは任意のマークに適用でき、最初に Stat
を使用する必要はありません。
(
so.Plot(penguins, x="species", y="body_mass_g", color="sex")
.add(so.Dot(), so.Dodge())
)

複数の Move
操作を連続して適用することも可能です。
(
so.Plot(penguins, x="species", y="body_mass_g", color="sex")
.add(so.Dot(), so.Dodge(), so.Jitter(.3))
)

変換による変数の作成#
Agg
stat は x
と y
の両方が既に定義されている必要がありますが、変数は統計的変換によって作成することもできます。たとえば、Hist
stat は x
または y
のいずれか一方のみを定義する必要があり、観測値をカウントすることでもう一方を作成します。
(
so.Plot(penguins, x="species")
.add(so.Bar(), so.Hist())
)

Hist
stat は、数値データが与えられると新しい x
値(ビン分割による)も作成します。
(
so.Plot(penguins, x="flipper_length_mm")
.add(so.Bars(), so.Hist())
)

連続的なx
軸を持つプロットでは、Bar
ではなくBars
を使用したことに注目してください。これら2つのマークは関連していますが、Bars
はデフォルト値が異なり、連続ヒストグラムにはより適しています。また、より効率的なmatplotlibアーティストも生成します。単数形/複数形のマークのパターンは他にも見られます。複数形バージョンは通常、より多くのマークがある場合に最適化されています。
一部の変換はx
とy
の両方を受け付けますが、各座標に区間データを追加します。これは、集計後の誤差バーのプロットに特に関連しています。
(
so.Plot(penguins, x="body_mass_g", y="species", color="sex")
.add(so.Range(), so.Est(errorbar="sd"), so.Dodge())
.add(so.Dot(), so.Agg(), so.Dodge())
)

マークと変換の向き#
集計、回避、バーの描画を行う場合、x
変数とy
変数は異なる方法で扱われます。各操作には向きという概念があります。Plot
は、変数のデータ型に基づいて向きを自動的に決定しようとします。たとえば、species
とbody_mass_g
の割り当てを入れ替えると、同じプロットが水平方向に表示されます。
(
so.Plot(penguins, x="body_mass_g", y="species", color="sex")
.add(so.Bar(), so.Agg(), so.Dodge())
)

x
変数とy
変数の両方が数値の場合など、正しい向きが不明確になる場合があります。このような場合は、Plot.add()
にorient
パラメーターを渡して明示的に指定できます。
(
so.Plot(tips, x="total_bill", y="size", color="time")
.add(so.Bar(), so.Agg(), so.Dodge(), orient="y")
)

プロットの作成と表示#
これまでのほとんどの例では、1種類のマークのみを含む単一サブプロットが生成されました。しかし、Plot
はこれに限定されません。
複数のレイヤーの追加#
より複雑な単一サブプロットのグラフィックは、Plot.add()
を繰り返し呼び出すことで作成できます。呼び出されるたびに、プロットにレイヤーが定義されます。たとえば、散布図(ここではDots
を使用)と回帰適合を追加する場合があります。
(
so.Plot(tips, x="total_bill", y="tip")
.add(so.Dots())
.add(so.Line(), so.PolyFit())
)

Plot
コンストラクターで定義された変数のマッピングは、すべてのレイヤーで使用されます。
(
so.Plot(tips, x="total_bill", y="tip", color="time")
.add(so.Dots())
.add(so.Line(), so.PolyFit())
)

レイヤー固有のマッピング#
特定のレイヤーでのみ使用されるマッピングを定義することもできます。これは、関連するレイヤーのPlot.add
への呼び出し内でマッピングを定義することで実現できます。
(
so.Plot(tips, x="total_bill", y="tip")
.add(so.Dots(), color="time")
.add(so.Line(color=".2"), so.PolyFit())
)

あるいは、プロット全体に対してレイヤーを定義しますが、変数をNone
に設定することで、特定のレイヤーから削除します。
(
so.Plot(tips, x="total_bill", y="tip", color="time")
.add(so.Dots())
.add(so.Line(color=".2"), so.PolyFit(), color=None)
)

要約すると、マークのプロパティの値を指定する方法は3つあります。(1)すべてのレイヤーで変数をマッピングする、(2)特定のレイヤーで変数をマッピングする、(3)プロパティを直接設定する。
ファセットとサブプロットのペアリング#
seabornのfigureレベル関数(seaborn.displot()
、seaborn.catplot()
など)と同様に、Plot
インターフェースは、データのサブセットを含む複数の「ファセット」またはサブプロットを持つ図も生成できます。これは、Plot.facet()
メソッドを使用して行われます。
(
so.Plot(penguins, x="flipper_length_mm")
.facet("species")
.add(so.Bars(), so.Hist())
)

Plot.facet()
を、プロットの列と/または行を定義するために使用する変数で呼び出します。
(
so.Plot(penguins, x="flipper_length_mm")
.facet(col="species", row="sex")
.add(so.Bars(), so.Hist())
)

レベル数の多い変数を使用してファセットを作成するには、もう一方の次元で「ラップ」します。
(
so.Plot(healthexp, x="Year", y="Life_Expectancy")
.facet(col="Country", wrap=3)
.add(so.Line())
)

明示的に除外しない限り、すべてのレイヤーがファセット化されます。これは、各サブプロットにさらなるコンテキストを提供する場合に役立ちます。
(
so.Plot(healthexp, x="Year", y="Life_Expectancy")
.facet("Country", wrap=3)
.add(so.Line(alpha=.3), group="Country", col=None)
.add(so.Line(linewidth=3))
)

サブプロットを作成するもう1つの方法は、Plot.pair()
です。seaborn.PairGrid
と同様に、これはx座標と/またはy座標に異なる変数を使用して、各サブプロットにすべてのデータを描き込みます。
(
so.Plot(penguins, y="body_mass_g", color="species")
.pair(x=["bill_length_mm", "bill_depth_mm"])
.add(so.Dots())
)

操作が反対の次元にサブプロットを追加する限り、ファセットとペアリングを組み合わせることができます。
(
so.Plot(penguins, y="body_mass_g", color="species")
.pair(x=["bill_length_mm", "bill_depth_mm"])
.facet(row="sex")
.add(so.Dots())
)

matplotlibとの統合#
Plot.facet()
またはPlot.pair()
で提供できるものよりも複雑な構造を持つ図に複数のサブプロットを表示させたい場合があります。現在の解決策は、図の設定をmatplotlibに委任し、Plot
がPlot.on()
メソッドで使用すべきmatplotlibオブジェクトを提供することです。このオブジェクトは、matplotlib.axes.Axes
、matplotlib.figure.Figure
、またはmatplotlib.figure.SubFigure
のいずれかになります。後者は、特注のサブプロットレイアウトを作成するのに最も役立ちます。
f = mpl.figure.Figure(figsize=(8, 4))
sf1, sf2 = f.subfigures(1, 2)
(
so.Plot(penguins, x="body_mass_g", y="flipper_length_mm")
.add(so.Dots())
.on(sf1)
.plot()
)
(
so.Plot(penguins, x="body_mass_g")
.facet(row="sex")
.add(so.Bars(), so.Hist())
.on(sf2)
.plot()
)

プロットの作成と表示#
重要なのは、Plot
メソッドは、呼び出されたオブジェクトを複製し、オブジェクトをその場で更新するのではなく、そのクローンを返すことです。つまり、共通のプロット仕様を定義し、そこからいくつかのバリエーションを作成できます。
したがって、この基本的な仕様を考えてみましょう。
p = so.Plot(healthexp, "Year", "Spending_USD", color="Country")
これを使用して、線プロットを描画できます。
p.add(so.Line())

あるいは、積み重ねられた面積図を描画することもできます。
p.add(so.Area(), so.Stack())

Plot
メソッドは完全に宣言型です。これらを呼び出すとプロット仕様が更新されますが、実際にはプロットは行われません。その結果、メソッドは任意の順序で呼び出すことができ、多くのメソッドを複数回呼び出すことができます。
プロットはいつ実際にレンダリングされますか?Plot
は、ノートブック環境での使用に最適化されています。レンダリングは、Plot
がJupyter REPLに表示されると自動的にトリガーされます。そのため、上記の例では何も表示されませんでした。ここでPlot
を定義しましたが、REPLに返すのではなくp
に割り当てました。
ノートブックでプロットを表示するには、セルの最後の行から返すか、オブジェクトに対してJupyterの組み込みdisplay
関数を呼び出します。ノートブックの統合はmatplotlib.pyplot
を完全にバイパスしますが、Plot.show()
を呼び出すことで、他のコンテキストではその図の表示機構を使用できます。
Plot.save()
を呼び出すことで、プロットをファイル(またはバッファー)に保存することもできます。
外観のカスタマイズ#
新しいインターフェースは、Plot
を介して高度なカスタマイズをサポートすることを目指しており、ギアを切り替えてmatplotlibの機能を直接使用する必要性を軽減します。(ただし、辛抱強くお待ちください。この目標を達成するために必要な機能のすべてが実装されているわけではありません!)
スケールのパラメーター化#
データ依存のプロパティはすべて、Scale
とPlot.scale()
メソッドという概念によって制御されます。このメソッドは、いくつかの異なるタイプの引数を受け入れます。matplotlibでのスケールの使用に最も近い可能性の1つは、座標を変換する関数の名前を渡すことです。
(
so.Plot(diamonds, x="carat", y="price")
.add(so.Dots())
.scale(y="log")
)

Plot.scale()
は、color
のような意味的プロパティのマッピングも制御できます。Seaborn の関数インターフェースの palette
パラメータに渡す引数を、直接渡すことができます。
(
so.Plot(diamonds, x="carat", y="price", color="clarity")
.add(so.Dots())
.scale(color="flare")
)

別の方法として、(min, max)
値のタプルを指定して、スケールがマッピングする範囲を制御することもできます。これは、数値プロパティと色の両方で機能します。
(
so.Plot(diamonds, x="carat", y="price", color="clarity", pointsize="carat")
.add(so.Dots())
.scale(color=("#88c", "#555"), pointsize=(2, 10))
)

より詳細な制御を行うには、Scale
オブジェクトを渡すことができます。いくつかの異なるタイプの Scale
があり、それぞれ適切なパラメータを持っています。例えば、Continuous
を使用すると、入力ドメイン(norm
)、出力範囲(values
)、およびそれらの間をマッピングする関数(trans
)を定義できます。一方、Nominal
を使用すると、順序を指定できます。
(
so.Plot(diamonds, x="carat", y="price", color="carat", marker="cut")
.add(so.Dots())
.scale(
color=so.Continuous("crest", norm=(0, 3), trans="sqrt"),
marker=so.Nominal(["o", "+", "x"], order=["Ideal", "Premium", "Good"]),
)
)

凡例と目盛りのカスタマイズ#
Scale
オブジェクトは、目盛りのラベル/凡例に表示する値と、それらの表示方法を指定する方法でもあります。例えば、Continuous.tick()
メソッドを使用すると、目盛りの密度または位置を制御できます。また、Continuous.label()
メソッドを使用すると、書式を変更できます。
(
so.Plot(diamonds, x="carat", y="price", color="carat")
.add(so.Dots())
.scale(
x=so.Continuous().tick(every=0.5),
y=so.Continuous().label(like="${x:.0f}"),
color=so.Continuous().tick(at=[1, 2, 3, 4]),
)
)

制限値、ラベル、タイトルのカスタマイズ#
Plot
には、Plot.label()
、Plot.limit()
、Plot.share()
を含む、簡単なカスタマイズのための多くのメソッドがあります。
(
so.Plot(penguins, x="body_mass_g", y="species", color="island")
.facet(col="sex")
.add(so.Dot(), so.Jitter(.5))
.share(x=False)
.limit(y=(2.5, -.5))
.label(
x="Body mass (g)", y="",
color=str.capitalize,
title="{} penguins".format,
)
)

テーマのカスタマイズ#
最後に、Plot
は、Plot.theme
メソッドを通じて、データに依存しないテーマ設定をサポートしています。現在、このメソッドは Matplotlib の rc パラメータの辞書を受け入れます。それらを直接設定したり、Seaborn のテーマ設定関数のパラメータパッケージを渡したりできます。
from seaborn import axes_style
theme_dict = {**axes_style("whitegrid"), "grid.linestyle": ":"}
so.Plot().theme(theme_dict)

すべての Plot
インスタンスのテーマを変更するには、Plot.config
の設定を更新します。
so.Plot.config.theme.update(theme_dict)