文章和代碼已經歸檔至【Github倉庫:https://github.com/timerring/dive-into-AI 】或者公眾號【AIShareLab】回覆 R語言 也可獲取。
R 的基礎繪圖系統由 Ross Ihaka 編寫,功能非常強大,主要由 graphics 包和 grDevices 包組成,它們在啓動 R 時會自動加載。基礎繪圖系統中有兩類函數,一類是高水平作圖函數,另一類是低水平作圖函數。
所謂高水平作圖函數是用於直接產生圖形的函數,包括 plot( )、hist( )、boxplot( )和 pairs( )等。低水平作圖函數是用於在高水平作圖函數所繪圖形的基礎上添加新的圖形或元素的函數,包括 points( )、lines( )、text( )、title( )、legend( )和 axis( )等。
1. 函數 plot( )
函數 plot( ) 是一個泛型函數,對於不同類型的數據,它可以繪製出不同的圖形。例如,對於數值型數據,它可以繪製出散點圖;對於分類數據,它可以繪製出箱線圖;對於一些統計模型,它可以繪製出相應的圖形,比如對於生存分析,它可以繪製出生存曲線。因此,函數 plot( ) 的使用頻率非常高,建議可以打開它的幫助文檔查看其各種常用參數的用法。
下面創建一個示例數據,表示某病病人對 2 種藥物(drugA 和 drugB)、5 個劑量(dose)水平上的響應情況。
dose <- c(20, 30, 40, 45, 60)
drugA <- c(16, 20, 27, 40, 60)
drugB <- c(15, 18, 25, 31, 40)
用上面的數據繪製藥物 A 的劑量和響應關係的圖形:
plot(dose, drugA)
plot(dose, drugA, type = "b")
上面的命令創建了兩幅圖,函數 plot( ) 裏的參數 type 默認為“p”(代表點),所以得到的第一張圖是散點圖。在第二行命令裏,參數 type 改為了“b”(代表點和線),所以得到的第二張圖是點線圖。
函數 plot( ) 用於新建一幅圖形,我們還可以用低水平作圖函數,例如 lines( )、legend( )等,在一幅現有圖形上添加新的圖形元素。例如:
# 為了比較兩種藥物不同劑量下的響應情況,我們在一幅圖上展示兩個點線圖,並用不同類型的線(lty)和不同特徵的點(pch)加以區分。
plot(dose, drugA, type = "b", lty = 1, pch = 15)
lines(dose, drugB, type = "b", lty = 2, pch = 17)
# 為了增強可讀性,還添加了圖例(legend)。
# 需要注意的是,函數 legend( )裏面點和線的屬性必須與前面函數 plot( )和 lines( )中設置的屬性一致。
legend("topleft", title = "Drug Type",
legend = c("A", "B"),
lty = c(1, 2),
pch = c(15, 17))
2.直方圖和密度曲線圖
直方圖(histogram)是用於展示連續型變量分佈的最常用的工具,它本質上是對密度函數的一種估計。直方圖和密度曲線圖一般用於探索分佈,很少用於報告結果。函數 hist( )可用於繪製直方圖。
數據集 anorexia 位於 MASS 包中,來自一項關於年輕女性厭食症患者體重變化的研究。該數據集包含 72 例觀察對象、3 個變量,其中變量 Treat(治療方式)是一個包含 3 個水平的因子,變量 Prewt 和 Postwt 均為數值型,分別表示治療前後的體重(單位:lb)。下面繪製變量 Prewt 的直方圖,代碼如下:
library(MASS)
data(anorexia)
str(anorexia)
attach(anorexia)
hist(Prewt)
上圖給出了變量 Prewt 的頻數分佈,由於函數 hist( )中沒有設置任何參數,圖中使用了默認的組距、座標軸標籤和標題等。需要注意的是,直方圖的形狀受到組距的影響,有時我們需要嘗試設定參數 breaks 的不同的值以得到合適的圖形。函數 hist( )的輸出結果中包含一些計算返回值,這些值可用於進一步地作圖或者分析,例如為區間劃分端點、頻數(或密度)、區間中點等。
密度曲線為數據的分佈提供了一種更為平滑的描述,繪製密度曲線的方法為:
plot(density(Prewt))
從上圖可以看出,變量 Prewt 的分佈是單峯的,基本是對稱的。我們還可以在一幅直方圖上添加一條密度曲線和軸須圖。此時,需要在函數 hist( )裏面設定參數 freq 為 FALSE,即把縱座標換成頻率,否則將會幾乎看不到密度曲線。參數 las(或者 labels) 設為 1 是為了將縱軸的刻度標籤橫向顯示。
library("showtext") # R 數據分析鏡像的中文支持不太好,需要藉助 showtext 包
showtext_auto() # 自動支持中文
# 使用紅色填充了條形,添加了信息量更大的座標軸標籤和標題,還通過設置參數 las 為 1 把縱軸的刻度標籤換成了橫向顯示。
hist(Prewt, freq = FALSE, col = "red",
xlab = "體重(lbs)",
main = "治療前體重分佈直方圖",
las = 1)
# 然後使用函數 lines( )在直方圖上疊加了一條藍色的、兩倍於默認線條寬度的密度曲線。
lines(density(Prewt), col = "blue", lwd = 2)
# 最後使用函數 rug( )在橫軸上添加了軸須圖,以展示數據分佈的密集趨勢。
rug(Prewt)
detach(anorexia)
3.條形圖
條形圖(bar chart)在醫學科技論文中經常用到,它通過垂直的或水平的矩形展示分類變量的頻數分佈。函數 barplot( ) 可用於繪製條形圖。
下面以 vcd 包裏的 Arthritis 數據集為例介紹函數 barplot( )的用法。該數據集來自一項關於治療類風濕性關節炎新方法的成組對照雙盲臨牀試驗研究。其中的反應變量 Improved 記錄了每位接受藥物治療(Treated,41 例)或安慰劑(Placebo,43 例)的患者的治療效果,分為 3 個級別(None、Some、Marked)。
library(vcd)
data(Arthritis)
attach(Arthritis)
counts <- table(Improved)
counts
# Improved
# None Some Marked
# 42 14 28
函數 table( )用於生成分類變量的頻數統計表。從上面的輸出可以看到,有 28 位患者有了明顯改善、14 人有部分改善,而有 42 人沒有改善。條形圖可以用於展示這個頻數分佈,如下圖所示:
barplot(counts, xlab = "Improvement", ylab = "Freqency", las = 1)
函數 barplot( )還可以用於展示二維列聯表的數據。下圖繪製了一幅分組條形圖,並添加了顏色和圖例,代碼如下:
counts <- table(Improved, Treatment)
barplot(counts,
col = c("red", "yellow", "green"),
xlab = "Improvement",
ylab = "Freqency",
beside = TRUE, las = 1)
legend("top", legend = rownames(counts),
fill = c("red", "yellow", "green"))
條形圖有時候也可以用於展示不同分類下的均值、中位數、標準差、置信區間等。用基本包裏的函數可以實現這個功能,但是需要很多個步驟。而 epiDisplay 包裏的函數 aggregate.plot( )可以簡化這個過程。
下面的代碼以數據集 anorexia 為例繪製了不同治療方式下治療後體重的均值條形圖,結果如下圖所示。
library(epiDisplay)
aggregate.plot(anorexia$Postwt, by = list(anorexia$Treat),
error = "sd", legend = FALSE,
bar.col = c("red", "yellow", "green"),
ylim = c(0,100), las = 1,
main = "")
上面的誤差棒表示的是標準差,我們可以通過改變函數 aggregate.plot( )裏面的參數 error 設置顯示標準誤或置信區間。
4. 餅圖
餅圖(pie chart)可用於展示分類數據的佔比情況。例如,下面的代碼繪製的餅圖展示了某醫院一週內急診入院的疾病類型分佈。
percent <- c(5.8, 27.0, 0.5, 20.8, 12.8, 33.1)
disease <- c("上感", "中風", "外傷", "昏厥", "食物中毒", "其他")
lbs <- paste0(disease, percent, "%")
pie(percent, labels = lbs, col = rainbow(6))
多數統計學家不建議使用餅圖,他們更推薦用條形圖或點圖代替餅圖,因為人們對長度的判斷比對面積的判斷更精確。因此,基本包的函數 pie( )繪製餅圖的選項有限。
不過,一些捐贈包擴展了 R 繪製餅圖的功能,例如 plotrix 包。該包提供的函數 pie3D( )可以繪製三維餅圖,另一個函數 fan.plot( )可以繪製功能與餅圖相似的扇形圖,感興趣的讀者可以安裝該包並查看其幫助文檔。
5. 箱線圖和小提琴圖
箱線圖(box plot)又稱箱須圖(box-whisker plot),常用於展示數據的大致分佈特徵,也用於探索異常值和離羣點。函數 boxplot( )可用於繪製箱線圖。
下面用箱線圖展示數據集 anorexia 裏體重前後變化的分佈。
anorexia$wt.change <- anorexia$Postwt - anorexia$Prewt
boxplot(anorexia$wt.change, ylab = "Weight change (lbs)", las = 1)
為了讓讀者更好地理解箱線圖各部分的含義,在下圖中添加了手工標註。如果數據是對稱分佈,中位數(Median)應該位於上四分位數(Upper quartile)和下四分位數(Lower quartile)的中間,即箱線圖的方盒關於中位線對稱。在上邊緣(Upper hinge)和下邊緣(Lower hinge)以外的值通常被認為是異常值。
fivenum(anorexia$wt.change)
anorexia$wt.change <- anorexia$Postwt - anorexia$Prewt
b <- boxplot(anorexia$wt.change, ylab = "Weight change (lbs)", las = 1)
# text(x= 1, y=1:5, labels= c("some","more","red text"))
text(1.2, 21.5, "Upper hinge")
text(1.13, 15.5, "←—— Whisker")
text(1.31, 9.2, "Upper quantile")
text(1.26, 1.65, "Median")
text(1.31, -2.45, "Upper quantile")
text(1.13, -7, "←—— Whisker")
text(1.2, -12.2, "Upper hinge")
平行排列的箱線圖可以用於比較在某個分類變量各個類別下某指標的分佈。例如,要比較不同治療方式下體重變化的情況,可以使用下面的命令:
boxplot(wt.change ~ Treat, data = anorexia,
ylab = "Weight change (lbs)", las = 1)
函數 boxplot( )的第一個參數輸入的是一個公式。R 裏公式一般用符號 ~ 連接變量,~ 左邊可以看作因變量, ~ 右邊可以看作自變量。從下圖(a)可以看出,“FT”(family treatment)組體重的改變量高於其他兩組。但是,差異的顯著性需要進一步的顯著性檢驗才能確定。
小提琴圖(violin plot)可以看作是箱線圖和密度圖的結合。vioplot 包裏的函數 vioplot( )可用於繪製小提琴圖,使用前請先安裝並加載該包。例如,上圖可以換成小提琴圖展示
options(warn=-1) # 清爽顯示
library(vioplot)
vioplot(wt.change ~ Treat, data = anorexia,
ylab = "Weight change (lbs)",
col = "gold", las = 1)
6. 克利夫蘭點圖
克利夫蘭點圖(Cleveland dot plot)本質上也是散點圖,它通過點的位置展示數據的大小,是一種在簡單水平刻度上繪製大量有標籤的值的方法,其功能與條形圖類似,但強調數據的排序以及相互之間的差距。
函數 dotchart( )可用於繪製克利夫蘭點圖。datasets 包裏的數據集 VADeaths 是 1940 年美國弗吉尼亞州城市和農村不同年齡段人羣的死亡率(以 ‰ 表示)情況。
VADeaths
dotchart(VADeaths)
dotchart(t(VADeaths),pch = 19)
從上圖可以看出,死亡率隨着年齡的升高而升高;在同一年齡段,農村地區的死亡率均高於城市地區;在同一年齡段同一地區,男性的死亡率均高於女性。
7. 導出圖形
如果想要把圖形保存下來,可以通過圖形用户界面和代碼兩種方式。在 RStudio 右下方的“Plots”下,單擊“Export”,選擇“Save as Image”或“Save as PDF”,可以把圖形保存在指定的文件夾下。我們還可以選擇“Copy to Clipboard”把圖形直接複製到 Word 或 PowerPoint 文檔。需要注意的是,這種方式保存的圖形與 RStudio 圖形窗口的尺寸有關,即不同大小的窗口得出的圖形會有差異(在 ModelWhale 中,可以右鍵單擊圖片,直接另存為)。
如果想把圖形保存下來用於報告或論文中,筆者建議使用代碼的方式,將繪圖語句放置在開啓目標圖形設備的語句和關閉目標圖形設備的語句之間即可。例如,下面的代碼會把圖形保存到當前工作目錄中並命名“mygraph.pdf”:
pdf("mygraph.pdf")
boxplot(wt.change ~ Treat,
data = anorexia,
ylab = "Weight change (lbs)",
las = 1)
dev.off() # work 下可以看到該 pdf
除了函數 pdf( ),我們還可以使用函數 png( )、jpeg( )、tiff( )和 postscript( )等將圖形保存為其他格式。
bmp、png 和 jpeg 格式的圖形文件都是非矢量格式,容易受到分辨率的影響,但它們佔用的空間很小,適合運用於 Word 和PowerPoint 文檔中;ps 格式的圖形文件是矢量格式文件,它與分辨率無關,適合運用於排版印刷;而 tiff(或 tif)格式的圖形文件可以支持很多色彩系統,而且獨立於操作系統,在各類出版物中運用得最為廣泛。例如:
tiff(filename = "mygraph.tiff",
width = 15, height = 12, units = "cm", res = 300)
boxplot(wt.change ~ Treat, data = anorexia, ylab = "Weight change (lbs)")
dev.off() # work 下可以看到該 tiff
上述命令生成了一個名為“mygraph.tiff”的圖形文件,參數 width 和 height 分別用於設置圖形的寬度和高度,參數 units 用於設置寬度和高度的單位,參數 res 用於設置分辨率,這裏設為了大多數出版物要求的最低值 300。
小結
其他一些專門的圖形,例如散點圖矩陣、相關圖、正態 QQ 圖、生存曲線、聚類圖、碎石圖、ROC 曲線和 Meta 分析森林圖等。在 R 的應用中,可視化是一個非常活躍的領域,新的包層出不窮。網站 The R Graph Gallery 收集了各種新穎的圖形以及相應的示例代碼,值得對可視化感興趣的讀者關注。