HOWTO · R
在 R 中建立分組箱線圖
本文介紹如何在 R 中建立分組箱線圖
本頁內容
在 ggplot2 中,分組箱線圖的關鍵只有一個決定:要用哪個變數把資料切分成您想比較的群組。您可以將該變數對映到 fill 美學屬性,讓 geom_boxplot() 在每個 x 位置內繪製並排的箱子;也可以使用 facet_wrap() 把資料拆分成各自獨立的面板。下列範例使用 gapminder 資料集搭配 dplyr 套件,每種方法都能產生您可以套用在自己欄位上的分組箱線圖。
使用 geom_boxplot() 建立分組箱線圖
ggplot 函式通常會搭配 geom_boxplot 來建構箱線圖物件。ggplot 的第一個引數是資料集,第二個引數是以 aes 建立的美學對映清單。在下列對映中,x 和 y 分別指向 gapminder 的 continent 與 lifeExp 欄位,並以 dplyr 函式篩選出三個選定年份。本節範例的程式碼與圖中標籤仍為英文。
if (requireNamespace("ggplot2", quietly = TRUE) &&
requireNamespace("gapminder", quietly = TRUE) &&
requireNamespace("dplyr", quietly = TRUE)) {
library(ggplot2)
library(gapminder)
library(dplyr)
dat <- gapminder %>%
filter(year %in% c(1972, 1992, 2007))
print(ggplot(dat, aes(x = continent, y = lifeExp)) +
geom_boxplot() +
scale_y_continuous(name = "Average Life Expectancy") +
scale_x_discrete(labels = abbreviate, name = "Continent"))
}
這個呼叫會為每個洲別各繪製一個箱子。它已經依 x 變數分組,但每個洲別內的年份仍會合併在一起。下一步是加入第二個分組變數。requireNamespace 防護可讓腳本在未安裝繪圖套件的環境中正常結束;此時會跳過繪圖,因此請將這段程式碼視為語法示範,並以本文稍後呈現的圖形為準。
使用 fill 美學屬性為箱子分組
將欄位對映到 fill,會告訴 geom_boxplot 在每個 x 位置內為該變數的各個群組分別繪製一個箱子。但有一個要注意的地方:fill 只有在被對映的變數為離散(discrete)時,才會產生不同的群組。gapminder 中的 year 欄位是數值型,因此 fill = year 會被視為連續的色彩漸層,圖形不會拆分成各年份的箱子。把它包進 factor 可將數字轉換為類別層級,進而產生預期的並排箱子。您可以直接在 R 中看出這個差別(程式碼與輸出仍為英文):
year <- c(1972, 1992, 2007)
print(class(year))
print(class(factor(year)))
print(levels(factor(year)))
[1] "numeric"
[1] "factor"
[1] "1972" "1992" "2007"
數值型的 year 仍是連續值,而 factor(year) 則帶有具名層級——fill 只會對第二種形式展開成各自獨立的箱子。在 ggplot2 中,同樣的區別會決定圖形的版面配置(下列程式碼的座標軸標籤仍為英文):
if (requireNamespace("ggplot2", quietly = TRUE) &&
requireNamespace("gridExtra", quietly = TRUE) &&
requireNamespace("gapminder", quietly = TRUE) &&
requireNamespace("dplyr", quietly = TRUE)) {
library(ggplot2)
library(gridExtra)
library(gapminder)
library(dplyr)
dat <- gapminder %>%
filter(year %in% c(1972, 1992, 2007))
p1 <- ggplot(dat, aes(x = continent, y = lifeExp, fill = year)) +
geom_boxplot() +
scale_y_continuous(name = "Average Life Expectancy") +
scale_x_discrete(labels = abbreviate, name = "Continent")
p2 <- ggplot(dat, aes(x = continent, y = lifeExp, fill = factor(year))) +
geom_boxplot() +
scale_y_continuous(name = "Average Life Expectancy") +
scale_x_discrete(labels = abbreviate, name = "Continent")
grid.arrange(p1, p2, nrow = 2)
}
上方面板保留 year 的數值型態,因此維持未分組的狀態;下方面板使用 factor(year),每個洲別顯示三個箱子,分別對應一個選定年份。
使用 facet_wrap() 將群組拆分成多個面板
facet_wrap 函式是另一種依指定變數繪製分組箱線圖的方式,這裡它會為每個年份各建立一個面板。facet_wrap 不需要 fill 對映也能運作,但保留 fill = factor(year) 會為箱子上色,讓各面板更容易區分。下列程式碼的座標軸標籤仍為英文。
if (requireNamespace("ggplot2", quietly = TRUE) &&
requireNamespace("gapminder", quietly = TRUE) &&
requireNamespace("dplyr", quietly = TRUE)) {
library(ggplot2)
library(gapminder)
library(dplyr)
dat <- gapminder %>%
filter(year %in% c(1972, 1992, 2007))
p3 <- ggplot(dat, aes(x = continent, y = lifeExp, fill = factor(year))) +
geom_boxplot() +
facet_wrap(~year) +
scale_y_continuous(name = "Average Life Expectancy") +
scale_x_discrete(labels = abbreviate, name = "Continent")
print(p3)
}
預設情況下,facet_wrap 產生的每個面板都共用相同的刻度,讓數值可在各面板之間直接比較。傳入 scales = "free" 會讓每個面板依自身包含的資料調整座標軸;這種做法能在資料範圍較窄的面板中呈現更多細節,但也會使跨面板比較產生誤導,因為相同的高度不再代表相同的數值。下列程式碼的座標軸標籤仍為英文。
if (requireNamespace("ggplot2", quietly = TRUE) &&
requireNamespace("gapminder", quietly = TRUE) &&
requireNamespace("dplyr", quietly = TRUE)) {
library(ggplot2)
library(gapminder)
library(dplyr)
dat <- gapminder %>%
filter(year %in% c(1972, 1992, 2007))
p4 <- ggplot(dat, aes(x = continent, y = lifeExp, fill = factor(year))) +
geom_boxplot() +
facet_wrap(~year, scales = "free") +
scale_y_continuous(name = "Average Life Expectancy") +
scale_x_discrete(labels = abbreviate, name = "Continent")
print(p4)
}
選擇版面配置並檢查結果
當群組數量不多、且希望在每個 x 類別內直接比較時,請使用 fill 分組;當群組很多、或每個子集都值得擁有獨立面板時,請使用 facet_wrap。您也可以交換兩者的角色,讓某個變數負責 fill 分組,另一個變數負責分面。下面的範例回到未經篩選的 gapminder 資料集,將 continent 對映到 fill,並讓 facet_wrap 處理所有可用的年份。下列程式碼的座標軸標籤仍為英文。
if (requireNamespace("ggplot2", quietly = TRUE) &&
requireNamespace("gapminder", quietly = TRUE)) {
library(ggplot2)
library(gapminder)
p5 <- ggplot(gapminder, aes(x = continent, y = lifeExp, fill = continent)) +
facet_wrap(~year) +
geom_boxplot() +
scale_y_continuous(name = "Average Life Expectancy") +
scale_x_discrete(labels = abbreviate, name = "Continent")
print(p5)
}
無論選擇哪種版面配置,都請檢查繪製出的圖形,確認每個群組具有預期數量的箱子或面板。如果數值欄位產生的是平滑的色彩圖例而非各自獨立的箱子,請先用 factor 轉換;如果各面板的形狀看起來相同但位置不同,請先確認刻度是共用的,再從圖表上讀取數值。