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())
)
../_images/objects_interface_4_0.png

このコードは散布図を作成しますが、かなり馴染みのあるものに見えるはずです。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))
)
../_images/objects_interface_6_0.png

プロパティのマッピング#

seaborn の関数と同様に、データ値をさまざまなグラフィカルプロパティにマッピングすることも可能です。

(
    so.Plot(
        penguins, x="bill_length_mm", y="bill_depth_mm",
        color="species", pointsize="body_mass_g",
    )
    .add(so.Dot())
)
../_images/objects_interface_8_0.png

この基本的な機能は目新しいものではありませんが、関数 API との重要な違いは、プロパティが、直接設定する場合と同じパラメータ名を使用してマッピングされることです(huecolor などではなく)。重要なのはプロパティがどこで定義されているかです。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"))
)
../_images/objects_interface_10_0.png

グループの定義#

Dot マークは各データポイントを個別に表現するため、変数をプロパティに割り当てても、各ドットの外観を変更する効果しかありません。Line など、観測値をグループ化または接続するマークの場合、個別のグラフィック要素の数も決定します。

(
    so.Plot(healthexp, x="Year", y="Life_Expectancy", color="Country")
    .add(so.Line())
)
../_images/objects_interface_12_0.png

group を使用して、視覚的なプロパティを変更せずにグループを定義することも可能です。

(
    so.Plot(healthexp, x="Year", y="Life_Expectancy", group="Country")
    .add(so.Line())
)
../_images/objects_interface_14_0.png

プロット前のデータ変換#

統計的変換#

多くの seaborn 関数と同様に、objects インターフェースは統計的変換をサポートしています。これらは Stat オブジェクト(Agg など)によって実行されます。

(
    so.Plot(penguins, x="species", y="body_mass_g")
    .add(so.Bar(), so.Agg())
)
../_images/objects_interface_16_0.png

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

(
    so.Plot(penguins, x="species", y="body_mass_g")
    .add(so.Dot(pointsize=10), so.Agg())
)
../_images/objects_interface_18_0.png

プロパティをマッピングしてグループを形成する場合、Stat 変換は各グループに個別に適用されます。

(
    so.Plot(penguins, x="species", y="body_mass_g", color="sex")
    .add(so.Dot(pointsize=10), so.Agg())
)
../_images/objects_interface_20_0.png

オーバープロットの解決#

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

(
    so.Plot(penguins, x="species", y="body_mass_g", color="sex")
    .add(so.Bar(), so.Agg())
)
../_images/objects_interface_22_0.png

それにもかかわらず、Bar マークを Agg stat と、Dodge によって実装された2番目の変換と合成することができます。

(
    so.Plot(penguins, x="species", y="body_mass_g", color="sex")
    .add(so.Bar(), so.Agg(), so.Dodge())
)
../_images/objects_interface_24_0.png

Dodge クラスは Move 変換の例であり、Stat と似ていますが、xy 座標のみを調整します。Move クラスは任意のマークに適用でき、最初に Stat を使用する必要はありません。

(
    so.Plot(penguins, x="species", y="body_mass_g", color="sex")
    .add(so.Dot(), so.Dodge())
)
../_images/objects_interface_26_0.png

複数の Move 操作を連続して適用することも可能です。

(
    so.Plot(penguins, x="species", y="body_mass_g", color="sex")
    .add(so.Dot(), so.Dodge(), so.Jitter(.3))
)
../_images/objects_interface_28_0.png

変換による変数の作成#

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

(
    so.Plot(penguins, x="species")
    .add(so.Bar(), so.Hist())
)
../_images/objects_interface_30_0.png

Hist stat は、数値データが与えられると新しい x 値(ビン分割による)も作成します。

(
    so.Plot(penguins, x="flipper_length_mm")
    .add(so.Bars(), so.Hist())
)
../_images/objects_interface_32_0.png

連続的なx軸を持つプロットでは、BarではなくBarsを使用したことに注目してください。これら2つのマークは関連していますが、Barsはデフォルト値が異なり、連続ヒストグラムにはより適しています。また、より効率的なmatplotlibアーティストも生成します。単数形/複数形のマークのパターンは他にも見られます。複数形バージョンは通常、より多くのマークがある場合に最適化されています。

一部の変換はxyの両方を受け付けますが、各座標に区間データを追加します。これは、集計後の誤差バーのプロットに特に関連しています。

(
    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())
)
../_images/objects_interface_34_0.png

マークと変換の向き#

集計、回避、バーの描画を行う場合、x変数とy変数は異なる方法で扱われます。各操作には向きという概念があります。Plotは、変数のデータ型に基づいて向きを自動的に決定しようとします。たとえば、speciesbody_mass_gの割り当てを入れ替えると、同じプロットが水平方向に表示されます。

(
    so.Plot(penguins, x="body_mass_g", y="species", color="sex")
    .add(so.Bar(), so.Agg(), so.Dodge())
)
../_images/objects_interface_36_0.png

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

(
    so.Plot(tips, x="total_bill", y="size", color="time")
    .add(so.Bar(), so.Agg(), so.Dodge(), orient="y")
)
../_images/objects_interface_38_0.png

プロットの作成と表示#

これまでのほとんどの例では、1種類のマークのみを含む単一サブプロットが生成されました。しかし、Plotはこれに限定されません。

複数のレイヤーの追加#

より複雑な単一サブプロットのグラフィックは、Plot.add()を繰り返し呼び出すことで作成できます。呼び出されるたびに、プロットにレイヤーが定義されます。たとえば、散布図(ここではDotsを使用)と回帰適合を追加する場合があります。

(
    so.Plot(tips, x="total_bill", y="tip")
    .add(so.Dots())
    .add(so.Line(), so.PolyFit())
)
../_images/objects_interface_40_0.png

Plotコンストラクターで定義された変数のマッピングは、すべてのレイヤーで使用されます。

(
    so.Plot(tips, x="total_bill", y="tip", color="time")
    .add(so.Dots())
    .add(so.Line(), so.PolyFit())
)
../_images/objects_interface_42_0.png

レイヤー固有のマッピング#

特定のレイヤーでのみ使用されるマッピングを定義することもできます。これは、関連するレイヤーのPlot.addへの呼び出し内でマッピングを定義することで実現できます。

(
    so.Plot(tips, x="total_bill", y="tip")
    .add(so.Dots(), color="time")
    .add(so.Line(color=".2"), so.PolyFit())
)
../_images/objects_interface_44_0.png

あるいは、プロット全体に対してレイヤーを定義しますが、変数をNoneに設定することで、特定のレイヤーから削除します。

(
    so.Plot(tips, x="total_bill", y="tip", color="time")
    .add(so.Dots())
    .add(so.Line(color=".2"), so.PolyFit(), color=None)
)
../_images/objects_interface_46_0.png

要約すると、マークのプロパティの値を指定する方法は3つあります。(1)すべてのレイヤーで変数をマッピングする、(2)特定のレイヤーで変数をマッピングする、(3)プロパティを直接設定する。

../_images/objects_interface_48_0.svg

ファセットとサブプロットのペアリング#

seabornのfigureレベル関数(seaborn.displot()seaborn.catplot()など)と同様に、Plotインターフェースは、データのサブセットを含む複数の「ファセット」またはサブプロットを持つ図も生成できます。これは、Plot.facet()メソッドを使用して行われます。

(
    so.Plot(penguins, x="flipper_length_mm")
    .facet("species")
    .add(so.Bars(), so.Hist())
)
../_images/objects_interface_50_0.png

Plot.facet()を、プロットの列と/または行を定義するために使用する変数で呼び出します。

(
    so.Plot(penguins, x="flipper_length_mm")
    .facet(col="species", row="sex")
    .add(so.Bars(), so.Hist())
)
../_images/objects_interface_52_0.png

レベル数の多い変数を使用してファセットを作成するには、もう一方の次元で「ラップ」します。

(
    so.Plot(healthexp, x="Year", y="Life_Expectancy")
    .facet(col="Country", wrap=3)
    .add(so.Line())
)
../_images/objects_interface_54_0.png

明示的に除外しない限り、すべてのレイヤーがファセット化されます。これは、各サブプロットにさらなるコンテキストを提供する場合に役立ちます。

(
    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))
)
../_images/objects_interface_56_0.png

サブプロットを作成するもう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())
)
../_images/objects_interface_58_0.png

操作が反対の次元にサブプロットを追加する限り、ファセットとペアリングを組み合わせることができます。

(
    so.Plot(penguins, y="body_mass_g", color="species")
    .pair(x=["bill_length_mm", "bill_depth_mm"])
    .facet(row="sex")
    .add(so.Dots())
)
../_images/objects_interface_60_0.png

matplotlibとの統合#

Plot.facet()またはPlot.pair()で提供できるものよりも複雑な構造を持つ図に複数のサブプロットを表示させたい場合があります。現在の解決策は、図の設定をmatplotlibに委任し、PlotPlot.on()メソッドで使用すべきmatplotlibオブジェクトを提供することです。このオブジェクトは、matplotlib.axes.Axesmatplotlib.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()
)
../_images/objects_interface_62_0.png

プロットの作成と表示#

重要なのは、Plotメソッドは、呼び出されたオブジェクトを複製し、オブジェクトをその場で更新するのではなく、そのクローンを返すことです。つまり、共通のプロット仕様を定義し、そこからいくつかのバリエーションを作成できます。

したがって、この基本的な仕様を考えてみましょう。

p = so.Plot(healthexp, "Year", "Spending_USD", color="Country")

これを使用して、線プロットを描画できます。

p.add(so.Line())
../_images/objects_interface_66_0.png

あるいは、積み重ねられた面積図を描画することもできます。

p.add(so.Area(), so.Stack())
../_images/objects_interface_68_0.png

Plotメソッドは完全に宣言型です。これらを呼び出すとプロット仕様が更新されますが、実際にはプロットは行われません。その結果、メソッドは任意の順序で呼び出すことができ、多くのメソッドを複数回呼び出すことができます。

プロットはいつ実際にレンダリングされますか?Plotは、ノートブック環境での使用に最適化されています。レンダリングは、PlotがJupyter REPLに表示されると自動的にトリガーされます。そのため、上記の例では何も表示されませんでした。ここでPlotを定義しましたが、REPLに返すのではなくpに割り当てました。

ノートブックでプロットを表示するには、セルの最後の行から返すか、オブジェクトに対してJupyterの組み込みdisplay関数を呼び出します。ノートブックの統合はmatplotlib.pyplotを完全にバイパスしますが、Plot.show()を呼び出すことで、他のコンテキストではその図の表示機構を使用できます。

Plot.save()を呼び出すことで、プロットをファイル(またはバッファー)に保存することもできます。

外観のカスタマイズ#

新しいインターフェースは、Plotを介して高度なカスタマイズをサポートすることを目指しており、ギアを切り替えてmatplotlibの機能を直接使用する必要性を軽減します。(ただし、辛抱強くお待ちください。この目標を達成するために必要な機能のすべてが実装されているわけではありません!)

スケールのパラメーター化#

データ依存のプロパティはすべて、ScalePlot.scale()メソッドという概念によって制御されます。このメソッドは、いくつかの異なるタイプの引数を受け入れます。matplotlibでのスケールの使用に最も近い可能性の1つは、座標を変換する関数の名前を渡すことです。

(
    so.Plot(diamonds, x="carat", y="price")
    .add(so.Dots())
    .scale(y="log")
)
../_images/objects_interface_71_0.png

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

(
    so.Plot(diamonds, x="carat", y="price", color="clarity")
    .add(so.Dots())
    .scale(color="flare")
)
../_images/objects_interface_73_0.png

別の方法として、(min, max) 値のタプルを指定して、スケールがマッピングする範囲を制御することもできます。これは、数値プロパティと色の両方で機能します。

(
    so.Plot(diamonds, x="carat", y="price", color="clarity", pointsize="carat")
    .add(so.Dots())
    .scale(color=("#88c", "#555"), pointsize=(2, 10))
)
../_images/objects_interface_75_0.png

より詳細な制御を行うには、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"]),
    )
)
../_images/objects_interface_77_0.png

凡例と目盛りのカスタマイズ#

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]),
    )
)
../_images/objects_interface_79_0.png

制限値、ラベル、タイトルのカスタマイズ#

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,
    )
)
../_images/objects_interface_81_0.png

テーマのカスタマイズ#

最後に、Plot は、Plot.theme メソッドを通じて、データに依存しないテーマ設定をサポートしています。現在、このメソッドは Matplotlib の rc パラメータの辞書を受け入れます。それらを直接設定したり、Seaborn のテーマ設定関数のパラメータパッケージを渡したりできます。

from seaborn import axes_style
theme_dict = {**axes_style("whitegrid"), "grid.linestyle": ":"}
so.Plot().theme(theme_dict)
../_images/objects_interface_83_0.png

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

so.Plot.config.theme.update(theme_dict)