R實戰:grid包


grid包是一個底層的繪圖系統,能夠靈活地控制圖形輸出的外觀和布局,但是grid包不提供創建完整圖形的高級繪圖系統,例如,ggplot2和lattice,而是提供繪制開發這些高級繪圖的基礎接口,例如:定制lattice的輸出,產生高水平圖或非統計圖,為輸出添加復雜注釋。在繪圖時,有時候會遇到這樣一種情景,客戶想把多個代表不同KPI的圖形分布到同一個畫布(Page)上,而且每一個圖形都是單獨繪制的。對於這種需求,可以使用grid包來實現,grid包能把圖形逐個地添加到畫布中,並按照業務的需求,把圖形擺放到合適的位置上去。在布局完成之后,把圖形繪制出來。

一,grid包概述

grid包不僅可以輸出圖形,還可以產生可編輯的圖形組件,這些圖形組件可以被復用和重組,並能把圖形輸出到指定的位置上。在使用grid包之前,需要安裝和載入grid包:

install.packages("grid")
library(grid)

1,繪圖原語

常用的繪圖原語的明明格式是grid.**,用於輸出圖形:

grid.rect(...)   
grid.lines(...)  
grid.polygon(...)
grid.circle(...)
grid.text(...)

用戶可以通過參數修改繪圖函數輸出的圖形:

grid.rect(x = unit(0, “native"), y = unit(1.5, “npc"), height = unit(0.5, "inches"), width = unit(0.8, “lines”),
    gp=gpar(col="red", lwd=2, fill="yellow", lty="dotted"))

2,Grob對象

每一個繪圖原語都對應一個Grob,grob的命名格式是**Grob,Grob對象是一個可編輯的圖形組件,該組件保留圖形的所有屬性,但不會立即輸出圖形:

rectGrob(...)
linesGrob(...)
polygonGrob(...)
circleGrob(...)
textGrob(..)

要輸出Grob表示的圖形,可以使用grid.draw()函數繪制圖形:

rect <- rectGrob(...) 
grid.draw(rect)

二,核心對象viewport

繪制圖形需要畫布,是R的繪圖設備,在grid包中,畫布被定義為page,通過函數newpage()創建一個新的畫布:

grid.newpage()

viewport是grid包的核心對象,簡單來說,它就是畫布中的一個矩形的繪圖區域,直譯為視口,通過viewport()函數新建一個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)

參數注釋:

  • x:視口的幾何中心點相對頁面左下角原點的x坐標軸,默認單位是npc
  • y:視口的幾何中心點相對頁面左下角原點的y坐標軸,默認單位是npc
  • width:視口的寬度(x軸方向)
  • height:視口的高度(y軸方向)
  • default.units:默認單位為npc (Normalised Parent Coordinates),含義是規范化化的父區域坐標
  • just:x和y所指的位置,默認為矩形中心位置
  • gp:gpar對象,用於設置圖形參數;
  • clip:裁剪區域,有效值是“on”,“inherit”或“off”,指示剪裁到視口范圍內,從父視口繼承剪裁區域,或者完全關閉剪裁。 為了后向兼容性,邏輯值TRUE對應於“on”,而FALSE對應於“inherit”
  • xscale,yscale:兩個數值元素的向量,用於表示坐標軸的最小值和最大值。
  • angle:把視口逆時針旋轉的角度
  • layout:布局(grid.layout)對象,用於把視口划分為多個子區域
  • layout.pos.row,layout.pos.col:子區域在父布局中的行位置和列位置
  • name:此視口的名字,用於搜索和定位

viewport是繪圖的基礎,創建一個viewport:

vp <- viewport(x = 0.5, y = 0.5, width = 0.5, height = 0.25, angle=45)

通過函數grid.show.viewport()查看創建的視口:

grid.show.viewport(viewport(x=0.6, y=0.6, width=unit(1, "inches"), height=unit(1, "inches"), angle=30))

height和width是矩形的長和寬,x和y是視口中心點(也就是,矩形的幾何中心點)距離x坐標抽和y坐標軸的距離:

三,基於viewport繪制圖形

使用grid包繪圖時,首先要創建一個空的畫布:

grid.newpage()

在畫布中創建viewport對象:

vp <- viewport(x = 0.5, y = 0.5, width = 0.5, height = 0.25, angle=45)

此時,畫布中是空的,需要把viewport推到畫布中:

pushViewport(vp)

viewport是繪圖的區域,也就是說,基於viewport繪制圖形,在視口規定的范圍內作圖,例如,向視口中繪制矩形。

grid.rect()

四,viewport樹

grid包為每一個畫布維護了一個由viewport構成的樹,樹的根節點是由系統創建的,名字是ROOT的viewport,每一個節點都是一個viewport。活躍viewport是樹的當前位置,在樹中是唯一的,用戶只能向活躍viewport中繪圖,所有的操作都是基於活躍viewport。viewport()函數用於創建viewport,而一個viewport只有被push到viewport樹中,才能在其區域中繪圖。

通過5個函數實現對viewport樹的遍歷和更新:

  • pushViewport()函數:向活躍viewport中添加一個viewport,作為樹中的活躍viewport,原活躍viewport變成父viewport,這意味着,當一個viewport被push到樹中時,該viewport變成活躍viewport,是原活躍viewport的子viewport。
  • popViewport()函數:把活躍viewport從樹中刪除,其父viewport變成活躍viewport。
  • upViewport()函數:導航到活躍viewport的父viewport,當前viewport變成活躍viewport,原viewport不會被刪除;
  • downViewport()函數:導航到活躍viewport的父viewport,當前viewport變成活躍viewport,原viewport不會被刪除;
  • searchViewport()函數:根據viewport的名字,導航到任意viewport,當前viewport變成活躍viewport,原viewport不會被刪除。

注意:當向樹中push一個viewport時,如果樹中存在一個級別(level)相同,名字相同的viewport,那么push操作會把該viewport替換掉。

There is only ever one current viewport, which is the current position within the viewport tree. All drawing and viewport operations are relative to the current viewport. When a viewport is pushed it becomes the current viewport. When a viewport is popped, the parent viewport becomes the current viewport. Use upViewport to navigate to the parent of the current viewport, without removing the current viewport from the viewport tree. Use downViewport to navigate to a viewport further down the viewport tree and seekViewport to navigate to a viewport anywhere else in the tree.

查看當前的viewport樹結構:

current.vpTree()

例如,下面我們連續push三個viewport到一個圖形中。

grid.newpage()
colvec <- c('red', 'green', 'blue')
xvec <- c(0.3, 0.4, 0.5)
for (i in 1 : 3) {
  vp <- viewport(x = xvec[i], y = 0.5, width = 0.4, height = 0.4,
                 gp = gpar(col = colvec[i]))
  pushViewport(vp)
  grid.rect()
}

繪制的圖形依次嵌套,這說明,每push一次,原活躍viewport都變成父節點,把當前的veiwport作為子viewport:

五,布局

grid包中定義了布局對象,布局是矩形的子分區,也就是說,布局(layout)把一個矩形區域細分為更小的分區。

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")

參數注釋:

  • nrow,ncol:布局分為多少個行和列,每一個行和列構成的單元叫做分區(subdivision)
  • widths,heights:每一個分區的寬和高
  • default.units:默認單位
  • respect:邏輯值,如果為true,指定行高度和列寬度都遵守。
  • just:指定對齊方式,有效的值是:"left", "right", "centre", "center", "bottom", 和 "top".

1,創建布局

把top.vp視口分割為3X3的分區,使用函數grid.show.layout()查看布局,創建的布局如下圖所示:

layout <- grid.layout(nrow=3, ncol=3,
                   widths=unit(c(5, 1, 2), c("lines", "null", "lines")),
                   heights=unit(c(5, 1, 4), c("lines", "null", "lines")))

top.vp <-viewport(layout=layout, name="top")

grid.show.layout(layout)

 創建一系列的viewport,占用布局的各個分區,由於沒有push任何viewport,因此畫布中沒有繪制任何圖形。在為每個視口命名時,使用統一的格式:margin+數值,如下代碼所示:

margin1 <- viewport(layout.pos.col = 2, layout.pos.row = 3, name = "margin1") #(3,2)
margin2 <- viewport(layout.pos.col = 1, layout.pos.row = 2, name = "margin2") #(2,1)
margin3 <- viewport(layout.pos.col = 2, layout.pos.row = 1, name = "margin3") #(1,2)
margin4 <- viewport(layout.pos.col = 3, layout.pos.row = 2, name = "margin4") #(2,3)
plot <- viewport(layout.pos.col = 2, layout.pos.row = 2, name = "plot")       #(2,2)

R用數字來表示位置,數值代表的含義是:1=Buttom,2=Left,3=Top,4=Right,視口被布局分割的分區如下圖所示:

2,創建viewport樹

使用vpList()把視口排列成一個樹形結構,並把top.vp作為視圖的父節點,把所有其他視口作為子節點,使用vpTree()創建一個viewport樹:

 splot <- vpTree(top.vp, vpList(margin1, margin2, margin3, margin4, plot))

把整個viewport樹push到活躍視口中,這樣,在繪圖區域中,我們可以在不同的散點視口中繪制圖形。

 pushViewport(splot)

在把整個樹push到活躍視口之后,就可以在不同的區域內繪制圖形,使用seekViewport()函數按照視口名稱切換到指定的視口,並把當前視口激活。

seekViewport("plot")
grid.xaxis()
grid.yaxis()
grid.rect()
grid.points(x, y)

完整的代碼如下:

library(grid)

layout <- grid.layout(nrow=3, ncol=3,
                      widths=unit(c(5, 1, 2), c("lines", "null", "lines")),
                      heights=unit(c(5, 1, 4), c("lines", "null", "lines")))
#grid.show.layout(layout)

top.vp <-viewport(layout=layout,name="top")
#grid.show.viewport(top.vp)

x <- runif(10)
y <- runif(10)
xscale <- extendrange(x)
yscale <- extendrange(y)

margin1 <- viewport(layout.pos.col = 2, layout.pos.row = 3, name = "margin1")
margin2 <- viewport(layout.pos.col = 1, layout.pos.row = 2, name = "margin2")
margin3 <- viewport(layout.pos.col = 2, layout.pos.row = 1, name = "margin3")
margin4 <- viewport(layout.pos.col = 3, layout.pos.row = 2, name = "margin4")
plot <- viewport(layout.pos.col = 2, layout.pos.row = 2, name = "plot",xscale = xscale, yscale = yscale)

splot <- vpTree(top.vp, vpList(margin1, margin2, margin3, margin4, plot))
#grid.show.viewport(splot)

pushViewport(splot)

seekViewport("plot")
grid.xaxis()
grid.yaxis()
grid.rect()
grid.points(x, y,pch=20)

seekViewport("margin1")
grid.text("Random X", y = unit(1, "lines"))

seekViewport("margin2")
grid.text("Random Y", x = unit(1, "lines"), rot = 90)

3,把圖形逐個打印到視口中

利用布局在同一個畫布中繪制多個圖形的另外一種方法是使用print()函數,代碼摘抄於《R統計繪圖(2):grid布局》。

step1,創建多個圖形

library(grid)
library(ggplot2)
# prepare ggplot charts
p.hist.len <- ggplot(iris) + geom_histogram(aes(x=Sepal.Length))
p.hist.wid <- ggplot(iris) + geom_histogram(aes(x=Sepal.Width)) + coord_flip()
p.scatter <- ggplot(iris) + geom_point(aes(x=Sepal.Length, y=Sepal.Width))

step2,創建布局,分割視口,並push當前視口

grid.newpage()
pushViewport(viewport(layout = grid.layout(3, 3)))

step3,把圖形輸出到布局的不同區域中

print(p.scatter, vp=viewport(layout.pos.row=2:3, layout.pos.col=1:2))
print(p.hist.len, vp=viewport(layout.pos.row=1, layout.pos.col=1:2))
print(p.hist.wid, vp=viewport(layout.pos.row=2:3, layout.pos.col=3))

六,unit對象和gpar對象

在grid包中,unit對象用於表示:長度和單位,gpar對象用於設置:圖形參數

1,unit對象

用unit對象表示長度的大小和單位:

unit(x, units, data=NULL)

參數注釋:

  • x:數值向量
  • units:單位向量

2,gpar對象

用gapr對象表示圖形參數:

  • col: Colour for lines and borders.
  • fill: Colour for filling rectangles, polygons, ...
  • alpha: Alpha channel for transparency
  • lty: Line type
  • lwd: Line width
  • lex: Multiplier applied to line width
  • lineend: Line end style (round, butt, square)
  • linejoin: Line join style (round, mitre, bevel)
  • linemitre: Line mitre limit (number greater than 1)
  • fontsize: The size of text (in points)
  • cex: Multiplier applied to fontsize
  • fontfamily: The font family
  • fontface: The font face (bold, italic, ...)
  • lineheight: The height of a line as a multiple of the size of text
  • font: Font face (alias for fontface; for backward compatibility)

 

參考文檔:

Working with grid Viewports

R統計繪圖(2):grid布局

R語言grid包使用筆記——viewport

The Grid Graphics Package


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM