這裡介紹如何使用 R 的 ggplot2 繪圖套件,輕鬆畫出高品質的各種統計圖形。
ggplot 是以繪圖文法概念為基礎所發展出來的一套 R 繪圖系統,可繪製各種高品質圖形的 R 繪圖套件,是一套相當受歡迎的繪圖工具。
在使用上,ggplot 這種繪圖系統不同於以往傳統式的 R 繪圖方式,使用者不會受限於既有的固定功能,可以依照自己的需求組合各種圖形組件、任意調整圖形,產生各種變化,相當富有彈性,而其畫出來的圖形品質也非常好,通常使用預設的設定值就可以很容易地產生出版品水準的圖形。
在傳統的 R 繪圖系統之下,一般的繪圖工具所提供的只是一些固定的繪圖功能,頂多加上一些微調參數讓使用者做些微的調整,若要自行設計新的圖形,使用者能使用的繪圖組件只有資料點、線條等低階的元素,既有的高階圖形完全無法重複使用。
在 ggplot 系統之下,繪圖文法的設計可以讓使用者以更高階的思維模式來設計繪圖,有許多既有的高階圖形元素可以使用,例如資料的呈現以及統計上的資料轉換方式等,使用者只要運用這些 ggplot 系統下的元件,就可以快速組裝出適用於自己的圖形。
以下我們將從 ggplot 繪圖系統的基礎概念開始介紹,敘述如何使用這個系統中的各種繪圖組件創建各式各樣的圖形,同時也會提供各種常用圖形的範例,縱使在不熟悉 ggplot 的繪圖概念之下,也可以畫出自己需要的圖形。
ggplot 繪圖文法
繪圖文法(grammar of graphics)的概念最早是由 Wilkinson 所提出來的,其主要用來解釋統計圖形(statistical graphic)的概念,而後來 Wickham 則是以 Wilkinson 的繪圖文法理論為基礎,做了一些調整後應用於 R 語言中,發展出了 ggplot 這個繪圖系統。
ggplot 系統將圖形視為一個從資料開始的一連串轉換,轉換的過程中間有很多道程序,包含幾何圖案、顏色與大小等美學屬性、統計量的計算與座標系統等,一個圖形就是原始資料結合這些轉換之後的結果。

- 資料來源(data):指定原始資料來源的 data frame。
- 美學對應(aesthetic):指定原始資料與圖形之間的對應關係,例如哪一個變數要當作 x 座標變數,而哪一個要當作 y 座標變數,還有資料繪圖時的樣式等。
- 幾何圖案(geometry):要用什麼幾何圖形繪製資料,例如點、線條、多邊形等。
- 繪圖面(facet):指定如何將資料分散在多張子圖形中繪製,以利互相比較。
- 統計轉換(statistical transformation):指定如何以將資料轉換為各種統計量,例如將連續型資料轉為離散型的類別。
- 座標系統(coordinate system):指定繪圖時所使用的座標系統,除了常見的笛卡兒直角座標系統,也可以使用極坐標或地圖投影(map projection)。
- 主題(theme):控制資料以外的繪圖組件,例如座標軸、說明文字等。
簡單的圖形只要有資料來源、美學對應以及幾何圖案的設定就可以畫出來了,而其餘轉換則是可以依照需求自行增減,以下我們將陸續介紹各種轉換的組件與使用方式。
安裝 ggplot2 套件
在使用 ggplot 繪圖之前,請先安裝 ggplot2 這個 R 套件,這個套件在 R 官方的套件庫中就有收錄,使用 install.packages 安裝即可:
install.packages("ggplot2")
接著載入 ggplot2 套件:
library(ggplot2)
qplot 函數
qplot 函數是 ggplot 系統中最基本的一個繪圖函數,它的設計類似傳統 plot 函數,方便讓初次使用 ggplot 的使用者快速上手,繪製一些基本的圖形。
這裡我們以一組 R 內建的 diamonds 資料集來示範 qplot 的用法,這組資料預設就內建在 ggplot2 套件中,在 ggplot2 套件安裝後即可使用。
diamonds資料集中包含了大約五萬多顆鑽石的資料,其中包含鑽石的 4C 品質指標,亦即顏色(color)、淨度(clarity)、切磨(Cut)與克拉(carat),另外還包含了幾個鑽石的尺寸資訊,詳細說明可參考diamonds的線上手冊。
首先我們用 head 稍微看一下 diamonds 資料集中的實際資料:
head(diamonds)
由於 diamonds 資料集的資料有五萬多筆,有些圖形會需要使用較少量的資料做示範,所以我們另外抽樣出 100 筆,儲存在 diamonds.subset 這個 data frame 中:
set.seed(5)
diamonds.subset <- diamonds[sample(nrow(diamonds), 100), ]
基本用法
qplot 的用法與傳統的 plot 類似,前兩個參數分別是 x 軸與 y 軸的座標資料:
qplot(diamonds$carat, diamonds$price)

另外也可以使用 data 參數指定資料來源的 data frame,這種方式會讓指令比較簡潔:
qplot(carat, price, data = diamonds)

這張圖顯示了鑽石的價格(price)與它的克拉數(carat)有明顯的關係,不過這個關係看起來不是線性的,我們可以將變數透過對數(log)轉換一下:
qplot(log(carat), log(price), data = diamonds)

經過對數轉換之後,看起來就呈現比較好的線性關係。
我們也可以使用多個變數進行運算之後,作為 x 軸或 y 軸的座標值:
qplot(carat, x * y * z, data = diamonds)

這裡我們拿 diamonds 的 x、y、z 三個變數相乘,當作 y 軸的座標,畫出與 carat 的關係圖,看起來大部分鑽石的體積都跟重量成正比,也就是密度都差不多,不過也有許多 outliers。
圖形樣式
qplot 有提供一些參數可以讓使用者更改資料點的顏色、大小等樣式,而且在使用上會比傳統的 plot 函數更方便,在使用 plot 更改資料點的樣式時,使用者必須自行將類別型的資料轉換為 plot 可以接受的數值或名稱(例如 red、blue 等),而 qplot 則是可以自動處理這些繁瑣的動作,並且在圖形上加入圖示說明(legend)。
假設我們想要依據 diamonds 中的 color 變數來替資料點著色,區別不同顏色的鑽石,可以使用 color 參數:
qplot(carat, price, data = diamonds.subset, color = color)

若要以資料點的形狀區分資料,可使用 shape:
qplot(carat, price, data = diamonds.subset, shape = cut)

資料點的顏色與形狀都是屬於 ggplot 系統上的美學對應。
這裡的鑽石資料總共有五萬多筆,一次畫在同一張圖形上會造成大量的資料點重疊問題,看不出實際的資料分佈,我們將資料點加上透明度的參數,這樣可以比較容易辨識實際的資料分佈情況:
qplot(carat, price, data = diamonds, alpha = I(1/10))

qplot(carat, price, data = diamonds, alpha = I(1/100))

隨著資料類型的不同,適合的資料呈現方式也不同,例如類別型的資料就適合使用顏色、形狀來區隔,而若是連續型的資料則可以使用資料點的大小來表示;至於資料量的多寡也會有影響,資料量多的時候,除了使用透明度的技巧,也可以考慮以繪圖面(facet)繪出多張圖形的方式,避免過多的資料擠在同一張圖上難以區分。
各式圖形
qplot 可繪製的圖形並不限於簡單的散佈圖,其 geom 參數可以設定用來呈現資料所使用的幾何圖形,我們可以藉著調整此參數來做出各種變化,有些 geom 參數還會伴隨一些統計量的計算(例如顯示直方圖的同時也需要計算每個 bin 的統計量),以下是一些在呈現二維資料時比較常用的 geom 選項:
"point":使用點的方式呈現資料,這也是qplot在繪製二維資料時預設的方式。"smooth":以 smoother 配適資料,畫出平滑曲線與標準誤差(standard error)。"boxplot":以箱形圖呈現資料分佈情形。"path":以線段連接每一個資料點。"line":類似"path",但"line"只能產生由左至右的線段圖形。
對於一維的資料,常用的 "geom" 參數如下:
"histogram":直方圖,適用於連續型的資料,在資料是一維的情況下,預設會使用此方式。"freqpoly":類似直方圖,但以折線表示。"density":密度函數圖,適用於連續型的資料。"bar":長條圖,適用於離散型的類別資料。
在圖形上加入 Smoother
在資料點很多的二維圖形上,有時候不容易看出整體的資料趨勢,這時候可以加上一條平滑曲線幫助判讀。若要同時指定多種 geom,可以使用 c 函數來指定:
qplot(carat, price, data = diamonds.subset,
geom = c("point", "smooth"))
qplot 在解讀 geom 參數時,若遇到多個幾何圖形,就會依照這裡指定的順序依序畫在圖形上:

qplot(carat, price, data = diamonds,
geom = c("point", "smooth"))

箱形圖與 Jitter 資料點
當一組資料同時含有一個類別型的變數以及一個或多個連續型的變數時,資料分析者通常都會想要比較各類別中各連續型變數的分佈狀況,而最常用的圖形就是箱形圖:
qplot(color, price / carat, data = diamonds, geom = "boxplot")

箱形圖僅帶有 five numbers 的資訊,若想要看到更細部的資訊,可以使用 jitter 的方式繪製資料點,它可以將每一個資料點都畫出來:
qplot(color, price / carat, data = diamonds, geom = "jitter")

在資料量太多的時候,可以使用透明度的技巧:
qplot(color, price / carat, data = diamonds,
geom = "jitter", alpha = I(1 / 5))

qplot(color, price / carat, data = diamonds, geom = "jitter",
alpha = I(1 / 50))

qplot(color, price / carat, data = diamonds, geom = "jitter",
alpha = I(1 / 200))

以 jitter 的方式繪圖可以顯示資料分佈的細部資訊,彌補箱形圖的不足。以這個例子而言,雖然不同透明度的 jitter 資料點可以顯示資料集中的區域,不過箱形圖可能還是比較容易辨識。
以 jitter 繪製資料點時,可以同時配合散佈圖中的各種參數來調整圖形,例如:size、color 與 shape,而箱形圖也可以使用 color、fill 與 size 來調整顏色與線條粗細。
練習
# 以資料點的形狀區分 cut 變數
qplot(color, price / carat, data = diamonds, geom = "jitter",
alpha = I(1 / 5), shape = cut)
# 以資料點的顏色區分 color 變數
qplot(color, price / carat, data = diamonds, geom = "jitter",
alpha = I(1 / 5), color = color)
# 以資料點的顏色區分 cut 變數
qplot(color, price / carat, data = diamonds, geom = "jitter",
alpha = I(1 / 5), color = cut)
# 以箱形圖的外框顏色區分 color 變數
qplot(color, price / carat, data = diamonds, geom = "boxplot",
color = color)
# 以箱形圖的內部顏色區分 color 變數
qplot(color, price / carat, data = diamonds, geom = "boxplot",
fill = color)
# 調整箱形圖的外框粗細
qplot(color, price / carat, data = diamonds, geom = "boxplot",
size = I(2))
直方圖與密度函數
直方圖可以顯示一維資料的分佈狀況:
qplot(carat, data = diamonds, geom = "histogram")

直方圖的 bin 寬度可以使用 binwidth 參數調整:
qplot(carat, data = diamonds, geom = "histogram",
binwidth = 0.5, xlim = c(0, 3))

某些資料的細部資訊可能要在 bin 的寬度非常小的情況下才會顯現出來:
qplot(carat, data = diamonds, geom = "histogram",
binwidth = 0.01, xlim = c(0, 3))

若要同時呈現多組資料、相互比較時,可以加上美學對應:
qplot(carat, data = diamonds, geom = "histogram",
fill = color)

當美學對應指定為一個類別型的變數時,會讓資料以此類別變數為依據區分為多個群組,所以 qplot 在這種狀況下就會用不同顏色畫出不同的鑽石顏色的資料,產生堆疊式的直方圖。
密度函數圖的作用也跟直方圖類似:
qplot(carat, data = diamonds, geom = "density")

使用 adjust 參數調整密度函數圖的平滑程度,這個值越大則曲線越平滑,其效果類似直方圖的 bin 寬度:
qplot(carat, data = diamonds, geom = "density", adjust = 3)

密度函數圖可以同時呈現多組資料的分佈:
qplot(carat, data = diamonds, geom = "density",
color = color)

在比較多組資料時,密度函數圖可能比較容易閱讀,不過這種圖形是假設資料本身是屬於無界(unbounded)的連續型資料,若資料的類型不屬於這種就要注意。
如果要將直方圖密度函數圖畫在一起,可將 y 軸的單位指定為密度,再畫出兩種圖形:
qplot(carat, ..density.., data = diamonds,
geom = c("histogram", "density"))

長條圖
長條圖跟直方圖類似,用於表示類別型的離散資料,在 ggplot 系統下使用 bar 這個 geom 繪製長條圖時,它會自動計算每個類別的數量,不需要像傳統的 barchart 一樣自行計算列聯表。如果您已經把列聯表算出來了,或是想要自行指定列聯表的計算方式,可以使用 weight 這個參數指定數值的來源。
qplot(color, data = diamonds, geom = "bar")

qplot(color, data = diamonds, geom = "bar", weight = carat) +
ylab("carat")

時間序列與路徑
line 與 path 這兩種 geom 可以用來繪製時間序列與路徑類型的資料,path 會依據資料在 data frame 中的順序,將每一個點以線段連接起來,而 line 則是會將點的順序依據 x 軸座標來排序。通常 line 圖形的 x 軸是時間的資訊,用來呈現某個變數隨著時間的變化,而 path 則是用在比較兩個變數隨的時間變化的關係。
由於在 diamonds 資料集中沒有時間的資訊,我們改用 economics 這個資料集來示範,這個資料集包含了美國近 40 年的一些經濟發展相關數據。
unemploy 是失業人口,而 pop 則是人口總數,單位都是千人,相除之後即可計算失業率:
qplot(date, unemploy / pop, data = economics, geom = "line")

uempmed 是失業時間的中位數,單位為週:
qplot(date, uempmed, data = economics, geom = "line")

如果想要進一步細看失業率與失業時間之間的關係,最簡單的做法就是將這兩個數值用 x 與 y 的散佈圖畫出來,不過這樣的缺點就是無法看出時間上的資訊,另外一種畫法是使用 path 的方式依據時間把每個點連接起來:
qplot(unemploy / pop, uempmed, data = economics,
geom = c("point", "path"))

為了讓時間的資訊更容易辨識,可以再加上顏色來表示不同的年份:
year <- function(x) as.POSIXlt(x)$year + 1900
qplot(unemploy / pop, uempmed, data = economics,
geom = "path", colour = year(date))

從這樣的圖形上可以看得出來,失業率與失業時間之前呈現高度的正相關,而且最近幾年在相同失業率的狀況下,失業時間中位數比以往更高。
對於多個個體的時間序列資料,我們可能會需要在一張圖形上畫出好多條線,每一條線代表一個個體,以利互相比較,這種狀況則可使用 group 這個美學對應。
繪圖面(Facet)
善用 ggplot 的美學對應可以讓同一張圖形中的資料很方便的區分成不同的群組,而 ggplot 的繪圖面(facet)功能則提供另外一種資料區隔方式,它會先將資料分組後畫在表格式圖形中,而每一個子圖形的座標都相同,方便互相比較。
qplot 的繪圖面功能是使用 facets 參數配合公式的方式指定,公式的左邊是列(row)、右邊是行(column),例如:
qplot(carat, data = diamonds, facets = color ~ cut,
geom = "histogram", binwidth = 0.1, xlim = c(0, 3))

若只需要對一個變數分組,則將公式另一側指定為一個句點(.),例如:
qplot(carat, data = diamonds, facets = color ~ .,
geom = "histogram", binwidth = 0.1, xlim = c(0, 3))

其他用法
qplot 還有一些可以調整圖形的參數,這些用法與傳統的 plot 參數類似。
xlim、ylim:設定 x 軸與 y 軸的繪圖範圍。log:指定需要對數轉換的座標軸,例如log = "x"就是將 x 軸經過對數轉換,而log = "xy"則是讓 x 與 y 軸都經過對數轉換。main:指定圖形的標題,可指定為一般的字串或是以expression來表示的數學公式(詳細說明請參考plotmath的線上說明)。xlab、ylab:指定 x 軸與 y 軸的名稱,其與main一樣可以指定為字串或數學公式。
以下是一個使用範例:
qplot(
carat, price, data = diamonds.subset,
xlab = "Price ($)", ylab = "Weight (carats)",
main = "Price-weight relationship"
)

qplot(
carat, price/carat, data = diamonds.subset,
ylab = expression(frac(price,carat)),
xlab = "Weight (carats)",
main="Small diamonds",
xlim = c(.2, 1)
)

qplot(carat, price, data = diamonds.subset, log = "xy")

qplot 與 plot 的差異
以下是 qplot 的一些注意事項,以及與傳統 plot 之間的一些主要差異:
qplot並不是泛用型的函數,不像plot可以接受各種的 R 物件,對不同類別的物件畫出不同的圖形。而ggplot這個函數則是一個泛用型的函數,它可以建立一個繪圖的基礎物件,並配合其他函數產生各種圖形。- 一般來說美學對應都會指定為一個有興趣的變數,讓
ggplot自動依據該變數畫圖,如果只是單純要指定一個固定值,可以使用I函數,例如color = I("red")。 - 傳統
plot中的col、pch與cex等參數也可以用於qplot中,不過建議改用ggplot本身的參數名稱會比較好記,例如:color、shape與size。 - 在傳統的 R 繪圖系統上,若要在既有的圖形上增加元素,會使用
points、lines或text這些函數,而在ggplot系統中則會以增加圖層(layers)的方式處理。
qplot 範例
# 產生繪圖用的因子變數
mtcars$gear <- factor(mtcars$gear,levels=c(3, 4, 5),
labels=c("3gears", "4gears", "5gears"))
mtcars$am <- factor(mtcars$am,levels=c(0, 1),
labels=c("Automatic", "Manual"))
mtcars$cyl <- factor(mtcars$cyl,levels=c(4, 6, 8),
labels=c("4cyl", "6cyl", "8cyl"))
# 以 gear 分組畫出 mpg 密度函數圖
qplot(mpg, data = mtcars, geom = "density",
fill = gear, alpha = I(.5),
main="Distribution of Gas Milage",
xlab="Miles Per Gallon",
ylab="Density")

# 將資料以 gear 與 cylinder 分組,
# 畫出 mpg 與 hp 的散佈圖,
# 並且以資料點的顏色與形狀標示 am
qplot(hp, mpg, data = mtcars, shape = am, color = am,
facets = gear~cyl, size = I(3),
xlab = "Horsepower", ylab = "Miles per Gallon")

# 畫出箱形圖,
# 並且在上面用 jitter 資料點畫出實際資料的位置
qplot(gear, mpg, data = mtcars, geom = c("boxplot", "jitter"),
fill = gear, main = "Mileage by Gear Number",
xlab = "", ylab = "Miles per Gallon")

