目前最全的R語言-圖片的組合與拼接
- 2019 年 10 月 8 日
- 筆記
前言
李譽輝,四川大學碩士在讀,研究數據分析與可視化,以及網絡爬蟲。譽輝兄最近出的文章都是很系統的,從R ggplot2的基礎講解到三維數據可視化plot3D,文章都整理講解得很全面系統,我本人也是很喜歡這樣的文章,故而推薦給大家。
非常感謝譽輝兄撰寫這一篇目前最全的R語言-圖片的組合與拼接!我們也歡迎更多R和python的數據分析與可視化愛好者一起來學習與探討技術。
你們的鼓勵與探討,才是作者撰文寫稿源源不斷的動力!
張傑
1 customLayout包
參考來源:
https://www.rdocumentation.org/packages/customLayout/versions/0.2.0 https://mp.weixin.qq.com/s/zbp8pOQcNB4XBBF5SCg5GA
1.1 簡介
customLayout用於拼圖特別方便,尤其是儀錶盤布局 支持R內置的base繪圖對象,ggplot2對象(與grid結合 )
Hide
library(ggplot2)library(customLayout)
1.2 簡單畫布
通過設置簡單的數字矩陣以及對應的寬高比,可以非常方便的設置出來數字拼圖 關鍵函數:
lay_new()
創建拼圖畫布lay_show()
顯示拼圖畫布
mat數字矩陣必須從1開始,且必須連續
library(ggplot2)library(customLayout) # 創建拼圖畫布lay1 <- lay_new( mat = matrix(1:4, ncol = 2), # 矩陣分佈,mat表示指定排版的數字矩陣 widths = c(3,2), # 設定寬度比例 heights = c(2,1) # 設置高度比例)# 顯示拼圖畫布lay_show(lay1) # 創建第2個拼圖畫布,與第1個結構一樣,只是比例不一樣 lay2 <- lay_new( matrix(1:4, nc = 2), widths = c(3, 5),heights = c(2, 4)) lay_show(lay2)
1.3 畫布合併
其它拼圖包沒有的功能,非常好用 跟合併矩陣類似。分為行合併和列合併
lay_bind_col()
畫布列合併lay_bind_col(x, y, widths = c(1, 1), addmax = TRUE)
參數widths表示指定合併寬度比lay_bind_row()
畫布行合併lay_bind_row(x, y, heights = c(1, 1), addmax = TRUE)
參數heights表示指定合併高度比
library(ggplot2)library(customLayout)# 畫布列合併 cl_1 <- lay_bind_col(lay1, lay2, widths = c(3, 1)) # 指定合併寬度比為3:1 lay_show(cl_1)# 畫布行合併 cl_2 <- lay_bind_row(lay1, lay2, heights = c(3, 1)) # 指定合併高度比為3:1 lay_show(cl_2)# 多次合併 lay_show(lay_bind_col(cl_1, cl_2, widths = c(2:1)))
1.4 畫布嵌套
這個功能也是其它包沒有的,非常有用 關鍵函數:
lay_split_field(lay, newlay, field)
參數lay表示大畫布,參數newlay表示要嵌套進去的小畫布,field表示指定要嵌套的區域編號
library(ggplot2)library(customLayout) slay <- lay_split_field(lay1, lay2, field = 1) # 將畫布lay2嵌套進lay1的第1個區域,即左上角格子 lay_show(slay)
library(ggplot2) library(customLayout) slay2 <- lay_split_field(lay = lay2, new = lay1, field = 4) # 將畫布lay1嵌套進lay2的第4個區域,即右下角格子 lay_show(slay2)
1.5 填充圖片
關鍵函數:
lay_set(layout)
將畫布layout設置為繪圖布局,用於base繪圖對象lay_grid(grobs, lay, ...)
將繪圖對象grobs填充到畫布lay中, 用於ggplot2等繪圖對象
1.5.1 基礎繪圖對象填充
library(ggplot2) library(customLayout) par(mar = c(3, 2, 2, 1)) # 設定頁邊距# 創建排版畫布 lay1 <- lay_new( matrix(1:4, nc = 2), # 2行2列布局畫布 widths = c(3, 2), heights = c(2, 1)) lay2 <- lay_new(matrix(1:3)) # 3行1列布局畫布 cl <- lay_bind_col(lay1, lay2, widths = c(3, 1)) # 畫布列合併 lay_show(cl) lay_set(cl) # 設定繪圖對象布局 set.seed(123)# 繪圖填充 plot(1:100 + rnorm(100)) # 填充到畫布第1格 plot(rnorm(100), type = "l") # 填充到畫布第2格 hist(rnorm(500))acf(rnorm(100)) # 填充到畫布第4格 pie(c(3, 4, 6), col = 2:4) pie(c(3, 2, 7), col = 2:4 + 3) pie(c(5, 4, 2), col = 2:4 + 6)
1.5.2 ggplot2繪圖對象填充
library(ggplot2) library(customLayout) library(gridExtra)# 創建排版畫布 lay1 <- lay_new(matrix(1:2, ncol = 1)) # 2行1列畫布 lay2 <- lay_new(matrix(1:3)) # 3行1列畫布 cl <- lay_bind_col(lay1, lay2, widths = c(3, 1)) # 畫布列合併# 創建數據cuts <- sort(unique(diamonds[["cut"]]), decreasing = TRUE) make_cut_plot <- function(cut) { dd <- diamonds[cut == diamonds[["cut"]], ] # ggplot(dd) + geom_point(aes(carat, price)) + facet_wrap("cut") # 封裝分面} plots <- lapply(cuts, make_cut_plot) # 對不同切割水平的進行作圖 lay_grid(plots, cl) # 將繪圖對象依次填充到cl畫布中
2 cowplot包
cowplot是一個ggplot2包的簡單補充,意味着其可以為ggplot2提供出版物級的主題等。 更重要的是,這個包可以組合多個」ggplot2」繪製的圖為一個圖,並且為每個圖加上例如A,B,C等標籤, 這在具體的出版物上通常是要求的。 語法結構與ggplot類似,將ggplot2圖作為一個對象置於ggdraw()
中 表達式:
draw_plot(plot, x = 0, y = 0, width = 1, height = 1, scale = 1)
draw_text(text, x = 0.5, y = 0.5, size = 14, hjust = 0.5, vjust = 0.5,...)
draw_plot_label(label, x = 0, y = 1, hjust = -0.5, vjust = 1.5, size = 16, fontface = "bold", family = NULL, colour = NULL, ...)
參數解釋:
- plot 表示ggplot2繪圖對象
- x, y 表示子圖的起點坐標(左下角坐標),在0-1之間,表示占母圖的比例,
- width, height 表示子圖長寬所佔比例,在0-1之間
- text 表示要映射的文本向量
- label 表示要映射的文本向量
- 其它參數與ggplot2中意思一樣
library(ggplot2) library(cowplot) library(showtext) font_add_google("Dancing Script", "Dancing") plot.iris <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point() + facet_grid(cols = vars(Species)) # 按Species列分面 plot.mpg <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) + geom_point(size = 2.5) + labs(title = "dot plot") plot.diamonds <- ggplot(diamonds, aes(clarity, fill = cut)) + geom_bar() + theme(axis.text.x = element_text(angle = 70, vjust = 0.5)) + labs(title = "bar plot") gg <- ggdraw() + draw_plot(plot.iris, 0, 0.5, 1, 0.5) + # 在母圖上半部,占母圖比例1/2 draw_plot(plot.mpg, 0, 0, 0.5, 0.5) + # 在母圖左下角,占母圖比例1/4 draw_plot(plot.diamonds, 0.5, 0, 0.5, 0.5) + # 在母圖右下角,占母圖比例1/4 draw_plot_label(c("A", "B", "C"), c(0, 0, 0.5), c(1, 0.5, 0.5), size = 15, colour = "cyan", family = "Dancing") # 加上標籤, showtext_begin() print(gg) showtext_end()
3 grid 包
grid中文翻譯為網格,可將其解釋為畫布分割,通過設定相應的參數,從而可以任意的擺放圖形 常用函數:
grid.newpage()
創建新的畫布grid.layout()
分割畫布,使用參數widths和heights指定分割比例 ,從上到下,從左到右排列viewport()
在畫布中創建視窗grid.show.viewport()
在畫布中展示視窗grid.show.layout()
展示分割的畫布pushViewport()
將新建的viewport推出去,即將工作區域切換到新的viewportpopViewport()
將當前的viewport刪除,其父viewport作為新的工作區域, 子viewport中的繪製的圖形不會被刪除downViewport()
導航到子viewport,並作為工作區域,原viewport不會刪除upViewport()
導航到父viewport,父viewport變為工作區域, 原viewport不會被刪除seekViewport()
導航到name參數所在的viewport,並作為工作區域grid.text()
輸出文本標籤,坐標只與畫布有關,與viewport無關
語法:
viewport(x = unit(0.5, "npc"), y = unit(0.5, "npc"), width = unit(1, "npc"), height = unit(1, "npc"), default.units = "npc", just = "centre", gp = gpar(), clip = "inherit", xscale = c(0, 1), yscale = c(0, 1), angle = 0, layout = NULL, layout.pos.row = NULL, layout.pos.col = NULL, name = NULL) grid.layout(nrow = 1, ncol = 1, widths = unit(rep_len(1, ncol), "null"), heights = unit(rep_len(1, nrow), "null"), default.units = "null", respect = FALSE, just="centre")
參數解釋:
- name 指定viewport的名字,用於搜索和定位
- x,y 為起點坐標,默認是矩形視窗中心坐標,為0 – 1的數字,表示佔newpage的比例
- width, height 為矩形視窗的長寬,同樣是佔newpage的比例
- angle 表示角度,從-360到360,正數表示逆時針旋轉,負數表示順時針旋轉
- just 表示指定視窗起點位置,默認「centre」, 還可以設置左下角c(「left」, 「buttom」), 右上角c(「right」, 「top」) 等
- layout
grid.layout
對象,用於將當前的viewport拆分為子區域 - layout.pos.row 創建的viewport在父節點layout的行位置
- layout.pos.col 創建的viewport在父節點的layout列位置
- nrow 表示將該區域拆分為幾行
- ncol 表示將該區域拆分為幾列
- widths 表示每個子區域的寬度,向量長度等於ncol
- heights 表示每個子區域的高度,向量長度等於nrow
- gp = gpar() 表示傳遞其它參數,如: col/fill顏色,lty線型, lwd線寬, fontsize文本尺寸, fontfamily字體, fontface字型等, 可以通過
?gpar
查詢
library(ggplot2) library(grid) grid.newpage() # 創建一個空白畫布 vp1 <- viewport(x = 0.5, y = 0.5, width = 0.5, height = 0.25, angle = 45) # 設定視窗大小 grid.show.viewport(vp1) # 查看在畫布中的的視窗,自動上色,方便觀察
library(ggplot2) library(grid) library(showtext) YaHei <- windowsFont("微軟雅黑")font_add("YaHei", regular = "msyh.ttc", bold = "msyhbd.ttc") # 右鍵字體,然後點擊屬性,regular指定常規, bold表示指定粗體字體 showtext_auto() #### 父viewport grid.newpage() #新建一個page vp1 <- viewport(x = 0, y = 0.2, w = 0.9, h = 0.8, just = c("left", "bottom")) #新建一個viewport,起點為左下角, pushViewport(vp1) # 推出vp1 grid.rect(gp = gpar(col = "red")) # 新建一個矩形,gp=gpar()表示設置圖形參數 grid.text("我在vp1_1", x = 0.8, y = 0.2, gp = gpar(col = "red", fontfamily = "YaHei", fontsize = 15)) # 新建一個文本,輸出到vp1 vp2 <- viewport(x = 0, y = 0.2, w = 0.9, h = 0.8, just = c("left", "bottom")) # 新建一個viewport,起點為左下角, pushViewport(name = vp2) # 將工作區域設置到vp2 grid.rect(x = 0.1, y = 0.2, width = 0.9, height = 0.7, just = c("left", "bottom"), gp = gpar(col = "blue", lty = "dashed")) # 新建一個矩形,gp=gpar()表示設置圖形參數 grid.text("我在vp2_1", x = 0.8, y = 0.3, gp = gpar(col = "blue", fontfamily = "YaHei", fontsize = 15)) # 新建一個文本,輸出到vp2 vp3 <- viewport(x = 0.1, y = 0.2, width = 0.9, height = 0.7, just = c("left", "bottom")) pushViewport(vp3) grid.rect(x = 0.1, y = 0.2, width = 0.7, height = 0.7, just = c("left", "bottom"), gp = gpar(col = "orange", lty = "twodash", lwd = 2)) # 新建一個矩形,gp=gpar()表示設置圖形參數 grid.text("我在vp3_1", x = 0.6, y = 0.4, gp = gpar(col = "orange", fontfamily = "YaHei", fontsize = 15)) # 新建一個文本,輸出到vp2
layout參數
library(ggplot2) library(grid) grid.newpage() g1 <- grid.layout(nrow = 3, ncol = 2, widths = c(2, 3), heights = c(2, 1, 3)) # 設置分割的寬度和長度比例 grid.show.layout(l = g1)
綜合例子
library(ggplot2) library(grid) library(showtext) YaHei <- windowsFont("微軟雅黑")font_add("YaHei", regular = "msyh.ttc", bold = "msyhbd.ttc") # 右鍵字體,然後點擊屬性,regular指定常規, bold表示指定粗體字體 showtext_auto() plot.iris <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point() + facet_grid(cols = vars(Species)) # 按Species列分面 plot.mpg <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) + geom_point(size = 2.5) + labs(title = "dot plot") plot.diamonds <- ggplot(diamonds, aes(clarity, fill = cut)) + geom_bar() + theme(axis.text.x = element_text(angle = 70, vjust = 0.5)) + labs(title = "bar plot") grid.newpage() # 新建畫布 layout_1 <- grid.layout(nrow = 3, ncol = 2, widths = c(1, 1), heights = c(1, 4, 5)) # 分成上下2*3共6個版塊,最上面版塊顯示標題 pushViewport(viewport(layout = layout_1)) # 推出分成6個版塊的視窗 print(plot.iris, vp = viewport(layout.pos.row = 2, layout.pos.col = c(1, 2))) # 在中間一行子視窗中畫plot.iris print(plot.mpg, vp = viewport(layout.pos.row = 3, layout.pos.col = 1)) # 在左下角子視窗中畫plot.mpg print(plot.diamonds, vp = viewport(layout.pos.row = 3, layout.pos.col = 2)) #在右下角子視窗中畫plot.diamonds grid.text("我是畫布名稱", x = 0.5, y = 0.95, gp = gpar(col = "orange", fontfamily = "YaHei", fontsize = 15)) # 增加畫布標題
3.1 子母圖
字母圖,主要是形成局部放大的效果,既可以從整體上對比,又兼顧特別小的數據組,或特別密的數據點可以查看,而沒有必要單獨做2張圖
library(ggplot2)library(grid)
3.2 grid 拼接蝴蝶圖
library(ggplot2) library(grid) library(dplyr) library(showtext) library(Cairo) YaHei <- windowsFont("微軟雅黑") font_add("YaHei",regular = "msyh.ttc", bold = "msyhbd.ttc") # 右鍵字體,然後點擊屬性,regular指定常規, bold表示指定粗體字體 CairoPNG(file = "E:/R_input&output/images_output/蝴蝶圖_exercing.png", width = 1200, height = 700) showtext_begin()#生成圖形所需數據集: mydata<-data.frame(id=1:14, A=c(5.0,14.7,2.5,8.5,5.1,6.9,7.7,6.8,4.4,4.9,5.3,1.0,0.9,7.8), B=c(31.3,24.7,17.8,17.2,15.3,14.3,13.9,13.9,12.4,10.0,6.5,4.2,2.5,0.9), Label=c("Website","Customer & Employee Referral","Webinar","Facebook/Twitter/Other Social","Marketting & Advertising","Paid Serch","Other","Sales generated","Tradeshows","Parter","Linkedin","Events","Lead list","Emial Campaign")) p1<-ggplot(mydata) + # 繪製右側的柱形圖 geom_hline(yintercept=mean(mydata$A),linetype=2,size=.25,colour="grey")+ geom_bar(aes(x=id,y=A),stat="identity",fill="#E2BB1E",colour=NA)+ ylim(-5.5,16)+ scale_x_reverse()+ geom_text(aes(x=id,y=-4,label=Label),vjust=.5)+ geom_text(aes(x=id,y=A+.75,label=paste0(A,"%")),size=4.5,family="YaHei",fontface="bold")+ coord_flip()+ theme_void() p1
p2<-ggplot(mydata)+ # 繪製左側柱形圖, 左側圖沒有橫坐標刻度標籤 geom_hline(yintercept=-mean(mydata$B),linetype=2,size=.25,colour="grey")+ geom_bar(aes(x=id,y=-B),stat="identity",fill="#C44E4C",colour=NA)+ # y=-B,繪製的圖形在另一側 ylim(-40,0)+ scale_x_reverse()+ # geom_text(aes(x=id,y=-B-1.75,label=paste0(B,"%")),size=4.5,family="YaHei",fontface="bold")+ coord_flip()+ theme_void() p2
# 圖形拼接 grid.newpage() # 新建畫布 layout_1 <- grid.layout(nrow = 2, ncol = 2, widths = c(2, 3), heights = c(1, 9)) # 分成2*2共4個版塊 pushViewport(viewport(layout = layout_1)) # 推出分為4個版塊的視窗 print(p1, vp = viewport(layout.pos.row = 2, layout.pos.col = 2)) # 將p1輸出到右下角 print(p2, vp = viewport(layout.pos.row = 2, layout.pos.col = 1)) # 將p2輸出到左下角# 添加主標題和分標題 grid.text(label="我是主標題",x = 0.5,y = 0.97,gp=gpar(col="cyan",fontsize=15,fontfamily="YaHei",draw=TRUE,just = "centre")) grid.text(label="我是左標題", x = 0.15,y =0.94,gp=gpar(col="blue",fontsize=10,fontfamily="YaHei",draw=TRUE,just = c("left", "top"))) grid.text(label="我是右標題",x = 0.85,y =0.94,gp=gpar(col="blue",fontsize=10,fontfamily="YaHei",draw=TRUE,just = c("right", "top")))
showtext_end() dev.off()
## png ## 2
蝴蝶圖
4 gridExtra包
主要函數:
arrangeGrob()
grid.arrange()
marrangeGrob()
語法:
arrangeGrob(..., grobs = list(...), layout_matrix, vp = NULL, name = "arrange", as.table = TRUE, respect = FALSE, clip = "off", nrow = NULL, ncol = NULL, widths = NULL, heights = NULL, top = NULL, bottom = NULL, left = NULL, right = NULL, padding = unit(0.5, "line")) grid.arrange(..., newpage = TRUE) marrangeGrob(grobs, ..., ncol, nrow, layout_matrix = matrix(seq_len(nrow * ncol), nrow = nrow, ncol = ncol), top = quote(paste("page", g, "of", npages)))
參數解釋:
- grobs 圖形對象列表,grob是graphical object兩個單詞的縮寫,表示ggpot等圖形對象
- layout_matrix 表示布局的矩陣
library(ggplot2) library(gridExtra) library(showtext) YaHei <- windowsFont("微軟雅黑") font_add("YaHei",regular = "msyh.ttc", bold = "msyhbd.ttc") # 右鍵字體,然後點擊屬性,regular指定常規, bold表示指定粗體字體 showtext.auto() empty <- ggplot() + geom_point(aes(1, 1), colour = "white") + theme(axis.ticks = element_blank(), panel.background = element_blank(), axis.line = element_blank(), axis.text.x = element_blank(), axis.text.y = element_blank(), axis.title.x = element_blank(), axis.title.y = element_blank()) scatter <- ggplot() + geom_point(aes(mtcars$mpg, mtcars$qsec)) # 繪製主圖散點圖 hist_top <- ggplot() + geom_histogram(aes(mtcars$mpg)) # 繪製上方頻率分佈直方圖 hist_right <- ggplot() + geom_histogram(aes(mtcars$qsec)) + coord_flip() # 繪製右側頻率分佈直方圖# 最終組合,由4個圖拼圖而成,只有右上角的圖已經將標註移除了 grid.arrange(hist_top, empty, scatter, hist_right, # 按從左到右,從上到下順序排列4個圖ncol = 2, nrow = 2, widths = c(4, 1), heights = c(1, 4)) # 4個版塊的長寬比例# 其實這種組合圖已經有相應的R包了,ggExtra# df <- data.frame(x = mtcars$mpg, y = mtcars$qsec)# p <- ggplot(df, aes(x, y)) + geom_point() + theme_classic()# ggExtra::ggMarginal(p, type = "histogram")
把繪圖對象添加到列表總,並把該列表傳遞給grid.arrange()
函數中的grobs參數
library(ggplot2) library(gridExtra) library(lattice) # 類似ggplot,但是語法更加複雜 library(showtext) YaHei <- windowsFont("微軟雅黑") font_add("YaHei", regular = "msyh.ttc", bold = "msyhbd.ttc") # 右鍵字體,然後點擊屬性,regular指定常規, bold表示指定粗體字體 showtext.auto() gs <- list(NULL) gs[[1]] <- qplot(1, 1) gs[[2]] <- xyplot(1 ~ 1) # lattice包 grid.arrange(grobs = gs, ncol = 2)
子母圖 grid包可以畫字母圖 安裝gridExtra包後,ggplot2中多了一個ggplotGrob(
)函數,可以創建grob對象參數
library(ggplot2) library(gridExtra) g <- ggplotGrob(qplot(1, 1) + theme(plot.background = element_rect(colour = "black"))) qplot(1:10, 1:10) + annotation_custom( # 通過添加註釋的方式,向圖形內部添加一個圖形 grob = g, # 插入圖形對象,即添加內容 xmin = 1, xmax = 5, ymin = 5, ymax = 10 # 添加位置4個坐標 )