4.1 簡介
qplot()的局限性在於它只能使用一個數據集和一組圖形屬性映射,解決這個問題的辦法就是使用圖層。每個圖層可以有自己的數據集和圖形屬性映射,附加的數據元素可通過圖層添加到圖形中。
一個圖層由五個部分組成:
- 數據:必須是一個數據框;
- 一組圖形屬性映射,用來設定數據集中的變量如何映射到該圖層的圖形屬性;
- 幾何對象,用來指定在圖層中用哪種幾何對象來繪圖;
- 統計變換,返回一個包含新變量的數據框;
- 位置調整,通過調整元素位置來避免圖形重合。
4.2 創建繪圖對象
當我們調用qplot()時,它其實為我們做了很多幕后工作:創建一個圖形對象,添加圖層並且展示結果。在整個過程中它使用了很多默認的繪圖參數。如果想要手動創建圖形對象,就用用到ggplot()函數。只有在新添加的圖層里設定了新參數時,默認值才會被修改。
4.3 圖層
layer(geom, geom_params, stat, stat_params, data, mapping, position)
p <- ggplot(diamonds, aes(x = carat)) p <- p + layer(geom = "bar", geom_params = list(fill = "steelblue"), stat = "bin", stat_params = list(binwidth = 2)) p
該代碼生成一個組距為2,鐵青色的直方圖。
下面的快捷函數生成與上述代碼完全相同的圖層。
geom_histogram(binwidth = 2, fill = "steelblue")
所有這類快捷函數都由相同的形式——以geom_或者stat_開頭:
- geom_XXX(mapping, data, ..., stat, position)
- stat_XXX(mapping, data, ..., geom, position)
它們的參數定義了圖層的各種組件:
- mapping(可選):一個圖形屬性映射,通過aes()函數來設定;
- data(可選):一個數據集,它會修改默認的數據集。大部分情況下該參數被省略,默認數據集被調用;
- ...:geom或者stat的參數,例如直方圖的組距(binwidth)或者loess光滑曲線的帶寬(bandwidth);
- geom/stat(可選):可以修改geom默認的stat值,或者stat默認的geom值,它們是一組字符串,包含了將要使用的幾何對象或統計變換的名稱,使用默認值將會得到標准的圖形,修改默認值會得到一些新奇的圖形;
- position(可選):選擇一種調整對象重合的方式。
sleep:這是哺乳動物睡眠數據集的更新和擴展版本。
> head(msleep) # A tibble: 6 x 11 name genus vore order conservation sleep_total sleep_rem sleep_cycle <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> 1 Chee~ Acin~ carni Carn~ lc 12.1 NA NA 2 Owl ~ Aotus omni Prim~ NA 17 1.8 NA 3 Moun~ Aplo~ herbi Rode~ nt 14.4 2.4 NA 4 Grea~ Blar~ omni Sori~ lc 14.9 2.3 0.133 5 Cow Bos herbi Arti~ domesticated 4 0.7 0.667 6 Thre~ Brad~ herbi Pilo~ NA 14.4 2.2 0.767 # ... with 3 more variables: awake <dbl>, brainwt <dbl>, bodywt <dbl>
- name:常用名
- genus,vore:食肉動物、雜食動物還是食草動物?
- order,conservation:動物的保護狀況
- sleep_total:睡眠總量,以小時為單位
- sleep_rem:快速眼動睡眠,以小時為單位
- sleep_cycle:睡眠周期的長度,以小時為單位
- awake:醒着的時間,以小時為單位
- brainwt:腦重量(公斤)
- bodywt:體重(公斤)
圖層被添加到用ggplot()或qplot()創建的圖形對象上。
## 在用ggplot創建的圖形對象上添加圖層 ggplot(msleep, aes(sleep_rem/sleep_total, awake)) + geom_point() # 等價於 qplot(sleep_rem/sleep_total, awake, data = msleep)
# 也可以給qplot添加圖層 qplot(sleep_rem/sleep_total, awake, data = msleep) + geom_smooth() # 等價於 qplot(sleep_rem/sleep_total, awake, data = msleep, geom = c("point", "smooth")) # 或 ggplot(msleep, aes(sleep_rem/sleep_total, awake)) + geom_point() + geom_smooth()
圖層是普通的R對象,所以可以存儲到變量里去,這有利於代碼避繁就簡。例如,一組圖形可以先用不同的數據進行初始化,然后加上相同的圖層。下面的例子創建了一個帶有半透明深藍色回歸線的圖層。
bestfit <- geom_smooth(method = "lm", se = F, colour = alpha("steelblue", 0.5), size = 2) qplot(sleep_rem, sleep_total, data = msleep) + bestfit qplot(bodywt, brainwt, data = msleep, log = "xy") + bestfit
4.4 數據
ggplot2對數據的要求很簡單:必須是一個數據框。
要使用相同的代碼,不同的數據集繪圖,只需改變數據集即可。下面的例子用%+%來添加新的數據集以代替原來的數據集。
p <- ggplot(mtcars, aes(mpg, wt, colour = cyl)) + geom_point() p mtcars <- transform(mtcars, mpg = mpg^2) p %+% mtcars
數據是以副本而不是引用的形式存儲到圖形對象中的。
4.5 圖形屬性映射
aes(x = weight, y = height, colour = age)
aes()函數用來將數據變量映射到圖形中,從而使變量成為可以被感知的圖形屬性。
注意最好不要指定數據集以外的變量(例如diamonds$carat),因為這樣無法將繪圖所用的數據都封裝到一個對象里。每個aes()函數里的變量都包含於默認數據集或者圖層數據集中,是保證ggplot2對象都是自含型的重要方式之一,這樣方便存儲和重復使用。但可以使用變量的函數值作為參數。
4.5.1 圖和圖層
默認的圖形屬性可以在圖形對象初始化時設定,或者過后用+修改。如下例,圖形對象p中默認的映射可以在新圖層里擴充或修改。
## 使用默認的參數映射來添加圖層 p <- ggplot(mtcars, aes(x = mpg, y = wt)) p + geom_point()
p + geom_point(aes(colour = factor(cyl)))
p + geom_point(aes(y = disp))
4.5.2 設定和映射
除了可以將一個圖形屬性映射到一個變量,也可以在圖層的參數里將其設定為單一值。圖形屬性可以根據觀測的不同而變化,但是參數則不行。
p <- ggplot(mtcars, aes(mpg, wt)) p + geom_point(colour = "darkblue")
# 注意這里將顏色映射到'darkblue'與上面將顏色設定給'darkblue'的區別 p + geom_point(aes(colour = "darkblue"))
上面的圖將點的顏色設定為深藍色,下面的圖將colour映射到"darkblue"顏色,實際上是先創建了一個只含有"darkblue"字符的變量,然后將colour映射到這個新變量。
在使用qplot()函數的時候,可以將某個值放到I()里來實現映射,例如colour = I("darkblue")。
4.5.3 分組
圖中所有離散型變量的交互作用被設定為分組的默認值,通常情況下這樣可以正確地給數據分組,但是如果沒能正確分組或者圖中沒有離散型變量,那么就需要自定義分組結構,即將group映射到一個在不同組有不同取值的變量。當現有的單個變量不能夠正確地分組,而兩個變量的組合可以正確分組時,可以使用interaction()函數。
> library(nlme) > head(Oxboys) Grouped Data: height ~ age | Subject Subject age height Occasion 1 1 -1.0000 140.5 1 2 1 -0.7479 143.4 2 3 1 -0.4630 144.8 3 4 1 -0.1643 147.1 4 5 1 -0.0027 147.7 5 6 1 0.2466 150.2 6
牛津男孩的身高:
- Subject:為實驗中每個男孩提供唯一標識符的有序因子
- age:給出標准年齡的數字向量(無量綱)
- height:一個數字向量,給出男孩的身高(厘米)
- Occasion:把年齡從一個連續的變量轉換成一個計數的結果而得到的一個有序因素,這些輕微不平穩的數據可以被認為是平穩的。
多個分組與單個圖形屬性
將數據分為若干組,並用相同的方式對每個組進行渲染。當從總體上來查看數據時,我們通常希望區分每個個體(group)而不是識別他們(colour)。這在含有多個個體的縱向數據中是很常見的,而這類圖形也常被稱為“細面圖”。
p <- ggplot(Oxboys, aes(age, height, group = Subject)) p + geom_line()
不同圖層上的不同分組
希望在所有男孩的年齡和身高圖中添加一條光滑線條,新圖層需要一個不同的分組圖形屬性,group=1,這樣繪制出的線條就是基於整體的。
p <- ggplot(Oxboys, aes(age, height, group = Subject)) p + geom_line() + geom_smooth(aes(group = 1), method = "lm", size = 1.5, se = F) # 或 qplot(age, height, data = Oxboys, group = Subject, geom = "line") + geom_smooth(aes(group = 1), method = "lm", size = 1.5, se = F)
修改默認分組
如果圖像中含有離散型變量,而我們希望繪制連接所有分組的線條,那么可以采取繪制交互作用圖、輪廓圖以及平行坐標圖所用的策略。這里繪制各個測量時期身高的箱線圖並添加個體軌跡。
ggplot(Oxboys, aes(Occasion, height)) + geom_boxplot() + geom_line(aes(group = Subject), colour = "#3366FF")
4.5.4 匹配圖形屬性和圖像屬性
線條和路徑遵循差一原則:觀測點比線段數目多一,第一條線段使用第一個觀測的圖形屬性,第二條線段使用第二個觀測點的圖形屬性,以此類推,這意味着最后一個觀測的圖形屬性將不會被線段用到。
df <- data.frame(x = 1:3, y = 1:3, colour = c(1, 3, 5)) qplot(x, y, data = df, colour = factor(colour), size = I(5)) + geom_line(aes(group = 1), size = 2) qplot(x, y, data = df, colour = colour, size = I(5)) + geom_line(size = 2)
線段平穩地從一種圖形屬性變換到另一種圖形屬性。
## 用線性插值法做顏色漸變線條 xgrid <- with(df, seq(min(x), max(x), length = 50)) interp <- data.frame(x = xgrid, y = approx(df$x, df$y, xout = xgrid)$y, colour = approx(df$x, df$colour, xout = xgrid)$y) qplot(x, y, data = df, colour = colour, size = I(5)) + geom_line(data = interp, size = 2)
多邊形可用fill參數進行填充,這是整體對象的一個屬性。
## 圖4.7 一個條形圖(左)按組分解后得到的疊加條形圖(右),兩者輪廓相同。 qplot(color, data = diamonds) qplot(color, data = diamonds, fill = cut)
若不用fill而用colour,則會對邊界上色,效果遠不如上圖明顯。
qplot(color, data = diamonds, colour = cut)
4.6 幾何對象
幾何圖形對象,簡稱為geom,他執行着圖層的實際渲染,控制着生成的圖形類型。例如,用電幾何對象將會生成散點圖,而用線幾何對象就會生成折線圖。下表列出了ggplot2中所有可用的幾何對象。
每個幾何對象都有一組它能識別的圖形屬性和一組繪圖所需的值。例如,一個點含有顏色、大小和形狀等圖形屬性,以及x和y位置坐標。一個條形含有高度、條寬、邊界顏色和填充顏色等圖形屬性。下表中列出了所有幾何對象的圖形屬性。
每個幾何對象都有一個默認的統計變換,並且每一個統計變換都有一個默認的幾何對象。這些默認值如下表所示。
名稱 | 描述 | 默認的統計變換 | 圖形屬性 |
abline | 線,由斜率和截距決定 | abline | colour,linetype,size |
area | 面積圖 | identity | colour,fill,linetype,size,x,y |
bar | 條形圖,以x軸為底的矩形 | bin | colour,fill,linetype,size,weight,x |
bin2d | 2維熱圖 | bin2d | colour,fill,linetype,size,weight,xmax,xmin,ymax,ymin |
blank | 空白,什么也不畫 | identity | |
boxplot | 箱線圖 | boxplot | colour,fill,lower,middle,size,upper,weight,x,ymax,ymin |
contour | 等高線圖 | contour | colour,linetype,size,weight,x,y |
crossbar | 帶有水平中心線的盒子圖 | identity | colour,fill,linetype,size,x,y,ymax,ymin |
density | 光滑密度曲線圖 | density | colour,fill,linetype,size,weight,x,y |
density2d | 二維密度等高線圖 | density2d | colour,linetype,size,weight,x,y |
dotplot | “點直方圖”,用電來表示觀測值的個數 | bindot | colour,fill,x,y |
errorbar | 誤差棒 | indetity | colour,linetype,size,width,x,ymax,ymin |
errorbarh | 水平的誤差棒 | identity | colour,linetype,size,width,y,ymax,ymin |
freqpoly | 頻率多邊形圖 | bin | colour,linetype,size |
hex | 用六邊形表示的2維熱圖 | binhex | colour,fill,size,x,y |
histogram | 直方圖 | bin | colour,fill,linetype,size,weight,x |
hline | 水平線 | hline | colour,linetype,size |
jitter | 給點添加擾動,減輕圖形重疊問題 | identity | colour,fill,shape,size,x,y |
line | 按照x坐標的大小順序依次連接各個觀測值 | identity | colour,linetype,size,x,y |
linerange | 一條代表一個區間的豎直線 | identity | colour,linetype,size,x,ymax,ymin |
map | 基准地圖里的多邊形 | identity | colour,fill,linetype,size,x,y,map_id |
path | 按數據的原始順序連接各個觀測值 | identity | colour,linetype,size,x,y |
point | 點,用來繪制散點圖 | identity | colour,fill,shape,size,x,y |
pointrange | 用一條中間帶點的豎直線代表一個區間 | identity | colour,fill,linetype,shape,size,x,y,ymax,ymin |
polygon | 多邊形,相當於一個有填充的路徑 | identity | colour,fill,linetype,size,x,y |
quantile | 添加分位數回歸線 | quantile | colour,linetype,size,weight,x,y |
raster | 高效的矩形瓦片圖 | identity | colour,fill,linetype,size,x,y |
rect | 2維的矩形圖 | identity | colour,fill,linetype,size,xmax,xmin,ymax,ymin |
ribbon | 色帶圖,連續的x值所對應的y的范圍 | identity | colour,fill,linetype,size,x,ymax,ymin |
rug | 邊際地毯圖 | identity | colour,linetype,size |
segment | 添加線段或箭頭 | identity | colour,linetype,size,x,xend,y,yend |
smooth | 添加光滑的條件均值線 | smooth | alpha,colour,fill,linetype,size,weight,x,y |
step | 以階梯形式連接各個觀測值 | identity | colour,linetype,size,x,y |
text | 文本注釋 | identity | angle,colour,hjust,label,size,vjust,x,y |
tile | 瓦片圖 | identity | colour,fill,linetype,size,x,y |
violin | 小提琴圖 | ydensity | weight,colour,fill,size,linetype,x,y |
vline | 豎直線 | vline | colour,linetype,size |
4.7 統計變換
統計變換,簡稱stat,通常以某種方式對數據信息進行匯總。例如,平滑是一個很有用的統計變換,它能在一些限制條件的約束下計算給定x值時y的平均值。下表中列出了可用的統計變換。為了闡明在圖形中的意義,一個統計變換必須是一個位置尺度不變量,即$f(x + a) = f(x) + a$並且$f(b \cdot x) = b \cdot f(x)$,這樣才能保證當改變數據的標度時,數據變換的展現形式不變。
名稱 | 描述 |
bin | 計算封箱數據 |
bin2d | 計算矩形封箱內的觀測值個數 |
bindot | 計算“點直方圖”的封箱數據 |
binhex | 計算六邊形熱圖的封箱數據 |
boxplot | 計算組成箱線圖的各種元素值 |
contour | 三維數據的等高線 |
density | 一維密度估計 |
density2d | 二維密度估計 |
function | 添加新函數 |
identity | 不對數據進行統計變換 |
計算qq圖的相關值 | |
quantile | 計算連續的分位數 |
smooth | 添加光滑曲線 |
spoke | 將角度和半徑轉換成xend和yend |
sum | 計算每一個單一值的頻數,有助於解決散點圖的圖形重疊問題 |
summary | 對每個x所對應的y值做統計描述 |
summary2d | 對2維矩形封箱設定函數 |
summaryhex | 對2維六邊形封箱設定函數 |
unique | 刪除重復值 |
ydensity | 小提琴圖,計算1維y軸方向的核密度函數估計值 |
4.8 位置調整
所謂位置調整,就是對該層中的元素的位置進行微調,位置調整一般多見於處理離散型數據。下表中列出了可用的位置調整函數。
名稱 | 描述 |
dodge | 避免重疊,並排放置 |
fill | 堆疊圖形元素並將高度標准化為1 |
identity | 不做任何調整 |
jitter | 給點添加擾動避免重合 |
stack | 將圖形元素堆疊起來 |
在條形圖中可以很好地解釋不同類型的位置調整。
## 應用於條形圖的三種位置調整。從左到右依次是:堆疊(stacking),填充 ## (filling)和並列(dodging) dplot <- ggplot(diamonds, aes(clarity, fill = cut)) dplot + geom_bar(position = "stack") dplot + geom_bar(position = "fill") dplot + geom_bar(position = "dodge")
4.9 整合
4.9.1 結合幾何對象和統計變化
## 直方圖的三種變體。頻率多邊形(frequency ## polygon)(左);散點圖,點的大小和 ## 高度都映射給了頻率(中);熱圖(heatmap)用顏色來表示頻率。 d <- ggplot(diamonds, aes(carat)) d + stat_bin(aes(ymax = ..count..), binwidth = 0.1, geom = "area") d + stat_bin(aes(size = ..density..), binwidth = 0.1, geom = "point", position = "identity") d + stat_bin2d(aes(y = 1, fill = ..count..), binwidth = 0.1, geom = "tile", position = "identity")
4.9.2 顯示已計算過的統計量
如果已有匯總過的數據,並且希望直接使用它,而不進行其他的統計變換,可以使用stat_identity(),然后將合適的變量映射到相應的圖形屬性中。
4.9.3 改變圖形屬性和數據集
個體擬合預測,並添加預測線。
model <- lme(height ~ age, data = Oxboys, random = ~1 + age | Subject) oplot <- ggplot(Oxboys, aes(age, height, group = Subject)) + geom_line() age_grid <- seq(-1, 1, length = 10) subjects <- unique(Oxboys$Subject) preds <- expand.grid(age = age_grid, Subject = subjects) preds$height <- predict(model, preds) oplot + geom_line(data = preds, colour = "#3366FF", size = 0.4)
繪制殘差圖,后修改模型為帶二次項的擬合模型(效果較好)。
Oxboys$fitted <- predict(model) Oxboys$resid <- with(Oxboys, fitted - height) oplot %+% Oxboys + aes(y = resid) + geom_smooth(aes(group = 1)) model2 <- update(model, height ~ age + I(age^2)) Oxboys$fitted2 <- predict(model2) Oxboys$resid2 <- with(Oxboys, fitted2 - height) oplot %+% Oxboys + aes(y = resid2) + geom_smooth(aes(group = 1))
我們對圖形的修改是非常容易的,更新數據並重新作圖時並不需要重新運行oplot,這正是ggplot2所秉承的理念:使得反復擬合和評估模型變得輕松而自然。