seaborn.objects 介面#

在版本 0.12 中推出了 seaborn.objects 名稱空間,作為繪製 seaborn 繪圖的全新介面。它提供更一致且彈性的 API,包含一系列可組合的類別,用於轉換和繪製資料。與現有的 seaborn 函數相反,新的介面旨在支援端對端繪圖規格和自訂,而不需要使用 matplotlib(但如有需要,仍可執行此操作)。

注意

目前,物件介面仍為實驗性質,且尚未完善。雖然已穩定到足以正式使用,但肯定還有一些粗糙的邊緣和遺漏的功能。

指定繪圖並對應資料#

應該使用以下慣例匯入物件介面

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),並將其中兩欄指定為此圖形的 xy 座標。但這裡我們沒有從圖形類型開始,再增加一些資料指定,而是從資料指定開始,再加入圖形元素。

設定屬性#

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 相比,一個重要的差異在於,使用與將屬性直接設為相同的參數名稱來對應屬性(而不是使用 hue 相對於 color 等)。重要的是屬性定義在哪裡:在初始化 Dot 時傳入值會直接設定屬性,而在設定 Plot 時指定變數則會對應相對應的資料。

除了此差異之外,物件介面也允許對應更廣泛的記號屬性

(
    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 函數一樣,物件介面支援統計轉換。這些轉換乃由 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())。物件介面更乾脆地將呈現和轉換分離,讓你得以組成 MarkStat 物件

(
    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」分配後自動「跳動」長條圖。物件介面具有較不複雜的預設行為。預設情況下,代表多個群組的長條圖會重疊

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

儘管如此,也可將 Bar 標記與 Agg 統計和第二個由 Dodge 實作的轉換結合

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

類別 DodgeMove 變換的一個範例,這就像一個 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 需要 xy 都已經定義,但也可透過統計變換「建立」變數。例如,狀態 Hist 僅需要定義 x y 其中一個,透過計算觀測數會建立另一個

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

狀態 Hist 也會建立新的 x 值(透過分組),當給予數字資料時

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

注意我們使用 Bars,而不是 Bar 來繪製具有連續 x 軸的圖形。這兩個標記相關,但 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

調整標記和轉換#

在聚合、逃避和繪製棒線時,會以不同的方式處理 xy 變數。每個操作都有 方向 的概念。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

有時候,正確的方向很含糊,例如當 xy 變數都是數字時。在這些情況下,您可以透過將 orient 參數傳遞給 Plot.add() 來明確說明

(
    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

建立並顯示圖形#

到目前為止,大多數範例都只產生一個子圖形,且僅包含一種標記。但 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

綜上所述,有以下三種方法可以指定標記屬性的值:(1)在所有圖層中對應變數,(2)在特定圖層中對應變數,以及(3)直接設定屬性

../_images/objects_interface_48_0.svg

分面和小圖形配對#

與 seaborn 的圖形層級函式(seaborn.displot()seaborn.catplot() 等)一樣,Plot 介面也可以產生包含多個「分面」(facets)或包含資料子集的小圖形的圖形。這是透過 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

建立子圖的另一種方法是 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,並提供 Plot 應使用 Plot.on() 方法使用的 Matplotlib 物件。此物件可以是 matplotlib.axes.Axesmatplotlib.figure.Figurematplotlib.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 的範例中未看見任何內容,這是因為我們將 Plot 指定給 p,而不是讓它傳回 REPL。

若要於筆記本中看見繪圖,請從儲存格的最後一列傳回繪圖,或於物件中呼叫 Jupyter 內建的 display 函數。筆記本整合完全略過 matplotlib.pyplot,但您可以在其他脈絡中透過呼叫 Plot.show() 來使用 figure 顯示機制。

您也可以透過呼叫 Plot.save(),將繪圖儲存為檔案(或緩衝區)。

自訂外觀#

新介面旨在透過 Plot,支援大量自訂化功能,讓您無需轉換模式就能直接使用 matplotlib 功能。(但請耐心等待,畢竟尚非所有達成此目標所需的元素都已準備就緒!)

量表參數化#

所有數據相關屬性都由 Scale 概念和 Plot.scale() 方法控制。此方法接受多種不同類型的引數。其中一種可能性最接近於 matplotlib 中使用的比例,即傳遞函數名稱,讓函數能轉換座標

(
    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)