R 數(shù)據(jù)可視化 —— gtable 介紹

前言

多次有人問我,如何使用 R 來繪制多個 Y 軸。那我就研究了下,稍微介紹一下學(xué)習(xí)所得。

當(dāng)我們需要展示的數(shù)據(jù),包含相同的 X 軸,但是具有兩個不同的 Y 軸變量時,我們會選擇在右側(cè)添加一條軸,在 ggplot2 中,可以使用 sec_axis 來設(shè)置。

那如果 Y 軸變量有多個呢?我們該如何繪制?

一種簡單的思路就是,在圖像繪圖區(qū)域的左右兩側(cè)添加兩條軸,多余的軸可以添加到右側(cè),圖像還是填充到繪制區(qū)域。

那如何實(shí)現(xiàn)這一功能呢?下面我們將介紹今天的主角 —— gtable,搭配 grid 包,可以輕松的為 ggplot 添加上多個 Y

gtable

1. 介紹

gtable 是基于 grid 包的布局引擎,可以用來抽象化地創(chuàng)建網(wǎng)格視圖,每個網(wǎng)格內(nèi)都可以放置不同的圖形對象

gtable 可以很容易地將圖形元素的對齊,組合成復(fù)雜的圖形。同時,完美兼容 ggplot2 圖形

首先,當(dāng)然是安裝和導(dǎo)入了,不必多說

# 從 CRAN 上安裝
install.packages("gtable")

# 從 GitHub 上安裝
# install.packages("remotes")
remotes::install_github("r-lib/gtable")

導(dǎo)入

library(gtable)

library(ggplot2)
library(grid)

2. 創(chuàng)建布局

創(chuàng)建布局的函數(shù)主要有如下幾個:

  • gtable: 創(chuàng)建 grobgrid object) 的 table 布局
  • gtable_matrix: 矩陣布局
  • gtable_col: 單列
  • gtable_row: 單行
  • gtable_row_spacer/gtable_col_spacer:

2.1 gtable

table 布局可以將 grob 以表的排列形式放置。它支持跨行、跨列,還提供了一些工具來自動計(jì)算出正確的尺寸

gtable(widths = list(), heights = list(), respect = FALSE,
  name = "layout", rownames = NULL, colnames = NULL, vp = NULL)

其中前兩個參數(shù)指定了表格布局,例如

> a <- gtable(unit(1:3, c("cm")), unit(5, "cm"))
> a
TableGrob (1 x 3) "layout": 0 grobs
> class(a)
[1] "gtable" "gTree"  "grob"   "gDesc"

創(chuàng)建了一個寬度分別為 1cm2cm、3cm,長度為 5cm 的一行三列的表格布局,,返回了一個 gtable 對象

使用 gtable_show_layout 函數(shù)可以繪制布局結(jié)構(gòu)

gtable_show_layout(a)

每個繪圖對象(grob)都放置在自己的視圖中,并占滿整個視圖,因此對齊方式在這里不會發(fā)揮作用

布局中包含三個基礎(chǔ)組件,表格的規(guī)格(單元格高度和寬度)、布局(對每個 grob 來說就是它的位置,名稱及其他屬性)和全局參數(shù)

每個單元格內(nèi)可以有 01 或多個 grob。每個 grob 必須至少屬于一個單元格,但可以跨越多個單元格。

布局的細(xì)節(jié)被存儲在一個數(shù)據(jù)框中,每行表示一個 grob,列包括:

  • t: grob 的頂部位置
  • r: grob 的右邊位置
  • b: grob 的底部位置
  • l: grob 的左邊位置
  • z: grob 的次序
  • clip: grob 的裁剪方式,可以是:"on"、"off""inherit"
  • name: 每個 grob 和視圖的名稱

不能直接修改這個數(shù)據(jù)框,需要使用 gtable_add_grob 等操作函數(shù)來修改

2.2 gtable_matrix

gtable_matrix 可以創(chuàng)建矩陣布局,允許為每個單元格設(shè)置不同的的高度和寬度

gtable_matrix(name, grobs, widths = NULL, heights = NULL, z = NULL,
  respect = FALSE, clip = "on", vp = NULL)

首先,創(chuàng)建圖形對象

a <- rectGrob(gp = gpar(fill = "red"))
b <- circleGrob()
c <- linesGrob()

row <- matrix(list(a, b, c), nrow = 1)
col <- matrix(list(a, b, c), ncol = 1)
mat <- matrix(list(a, b, c, nullGrob()), nrow = 2)

設(shè)置布局,一行三列

> gtable_matrix("demo", row, unit(c(1, 2, 3), "null"), unit(1, "null"))
TableGrob (1 x 3) "demo": 3 grobs
  z     cells name                    grob
1 1 (1-1,1-1) demo     rect[GRID.rect.143]
2 2 (1-1,2-2) demo circle[GRID.circle.144]
3 3 (1-1,3-3) demo   lines[GRID.lines.145]

三行一列

> gtable_matrix("demo", col, unit(1, "null"), unit(c(1, 2, 3), "null"))
TableGrob (3 x 1) "demo": 3 grobs
  z     cells name                    grob
1 1 (1-1,1-1) demo     rect[GRID.rect.143]
2 2 (2-2,1-1) demo circle[GRID.circle.144]
3 3 (3-3,1-1) demo   lines[GRID.lines.145]

兩行兩列

> gtable_matrix("demo", mat, unit(c(1, 1), "null"), unit(c(1, 1), "null"))
TableGrob (2 x 2) "demo": 4 grobs
  z     cells name                    grob
1 1 (1-1,1-1) demo     rect[GRID.rect.143]
2 2 (2-2,1-1) demo circle[GRID.circle.144]
3 3 (1-1,2-2) demo   lines[GRID.lines.145]
4 4 (2-2,2-2) demo     null[GRID.null.146]

設(shè)置繪制順序

> z <- matrix(c(3, 1, 2, 4), nrow = 2)
> gtable_matrix("demo", mat, unit(c(1, 1), "null"), unit(c(1, 1), "null"), z = z)
TableGrob (2 x 2) "demo": 4 grobs
  z     cells name                    grob
1 3 (1-1,1-1) demo     rect[GRID.rect.143]
2 1 (2-2,1-1) demo circle[GRID.circle.144]
3 2 (1-1,2-2) demo   lines[GRID.lines.145]
4 4 (2-2,2-2) demo     null[GRID.null.146]

2.3 gtable_col 和 gtable_row

相較于前兩個布局,gtable_colgtable_row 就比較簡單,只是把所有 grob 繪制在一列或一行

gtable_col(name, grobs, width = NULL, heights = NULL, z = NULL,
  vp = NULL)

gtable_row(name, grobs, height = NULL, widths = NULL, z = NULL,
  vp = NULL)

也是通過 widthheight 控制寬度和高度,例如,繪制成一列

> gt <- gtable_col("demo", list(a, b, c))
> gt
TableGrob (3 x 1) "demo": 3 grobs
  z     cells name                    grob
1 1 (1-1,1-1) demo     rect[GRID.rect.143]
2 2 (2-2,1-1) demo circle[GRID.circle.144]
3 3 (3-3,1-1) demo   lines[GRID.lines.145]
> plot(gt)
gtable_show_layout(gt)

繪制成一行

> gt <- gtable_row("demo", list(a, b, c))
> plot(gt)
gtable_show_layout(gt)

3. 布局操作

  • gtable_add_grob: 添加一個 grob,可以跨越多行或多列
  • gtable_add_cols: 在指定位置添加新列
  • gtable_add_rows: 在指定位置添加新行
  • gtable_add_padding: 在 table 邊界添加填充
  • gtable_add_col_spacegtable_add_row_space: 添加行/列間距
  • gtable_trim: 刪除空單元格
  • gtable_filter: 通過名稱來篩選單元格

3.1 gtable_add_grob

gtable_add_grob 可以把一個 grob 添加到 table 布局中,但是不會影響 table 布局

gtable 中,添加的對象會全部覆蓋整個單元格,所以,如果你要設(shè)置對齊方式,則需要為 grob 設(shè)置絕對大小

使用方式

gtable_add_grob(x, grobs, t, l, b = t, r = l, z = Inf, clip = "on",
  name = x$name)

來看下面這個例子,新建一個一行三列的 table 布局,每列的寬度分別為 1、2、3

a <- gtable(unit(1:3, c("cm")), unit(5, "cm"))
gtable_show_layout(a)

為第一列添加一個矩形

> rect <- rectGrob(gp = gpar(fill = "black"))
> a <- gtable_add_grob(a, rect, 1, 1)
> a
TableGrob (1 x 3) "layout": 1 grobs
  z     cells   name                grob
1 1 (1-1,1-1) layout rect[GRID.rect.236]
> plot(a)

可以使用 t 對布局進(jìn)行轉(zhuǎn)置

> dim(a)
[1] 1 3
> t(a)
TableGrob (3 x 1) "layout": 1 grobs
  z     cells   name                grob
1 1 (1-1,1-1) layout rect[GRID.rect.236]
> dim(t(a))
[1] 3 1
> plot(t(a))

布局索引

> b <- gtable(unit(c(2, 2, 2), "cm"), unit(c(2, 2, 2), "cm"))
> b <- gtable_add_grob(b, rect, 2, 2)
> b[1, ]
TableGrob (1 x 3) "layout": 0 grobs
> b[, 1]
TableGrob (3 x 1) "layout": 0 grobs
> b[2, 2]
TableGrob (1 x 1) "layout": 1 grobs
  z     cells   name                grob
1 1 (1-1,1-1) layout rect[GRID.rect.236]
> plot(b)

訪問 gtable 對象的名稱

> rownames(b) <- 1:3
> rownames(b)[2] <- 200
> colnames(b) <- letters[1:3]
> dimnames(b)
[[1]]
[1]   1 200   3

[[2]]
[1] "a" "b" "c"

3.2 gtable_add_cols

gtable_add_cols 函數(shù)可以為 gtable 對象插入新的列,并會調(diào)整相應(yīng)的 grob 的位置。

如果在一個橫跨多列的 grob 中間添加列,grob 將繼續(xù)橫跨所有列。如果列被添加到 grob 的左邊或右邊,grob 將不會跨越新的列。

gtable_add_cols(x, widths, pos = -1)

創(chuàng)建一個 33 列的 table 布局,并添加三個矩形對象

rect <- rectGrob(gp = gpar(fill = "#00000080"))
tab <- gtable(unit(rep(1, 3), "null"), unit(rep(1, 3), "null"))
# 矩形占據(jù)第一行
tab <- gtable_add_grob(tab, rect, t = 1, l = 1, r = 3)
# 矩形占據(jù)第一列
tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 1)
# 矩形占據(jù)第三列
tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 3)

plot(tab)

在中間添加一列

> tab2 <- gtable_add_cols(tab, unit(1, "null"), 1)
> dim(tab2)
[1] 3 4
> plot(tab2)

指定 pos0 在左邊添加列,-1(默認(rèn))在右邊添加列

tab3 <- gtable_add_cols(tab, unit(1, "null"))
tab3 <- gtable_add_cols(tab3, unit(1, "null"), 0)
> dim(tab3)
[1] 3 5
> plot(tab3)

gtable_add_rows 的左右是添加列,這里就不再贅述了

3.3 gtable_add_padding

gtable_add_padding 可以為 gtable 四周添加 padding,使用方式

gtable_add_padding(x, padding)

padding 為長度為 4 的向量,分別表示上、右、下、左的 padding,如果長度不足 4,則會循環(huán)使用

例如,對于這樣一個只包含一個以矩形對象填充的單元格的布局

gt <- gtable(unit(1, "null"), unit(1, "null"))
gt <- gtable_add_grob(gt, rectGrob(gp = gpar(fill = "black")), 1, 1)

plot(gt)

可以使用 rbindcbind 來連接兩個 gtable 對象,繪制的圖形根本看不出有兩個圖形

plot(cbind(gt, gt))

gtable 添加 padding,四周的 padding 都是 1cm

pad <- gtable_add_padding(gt, unit(1, "cm"))
plot(pad)
plot(rbind(pad, pad))
plot(cbind(pad, pad))

3.4 gtable_add_col/row_space

為網(wǎng)格的單元格之間添加行距和列距

gtable_add_col_space(x, width)
gtable_add_row_space(x, height)

創(chuàng)建矩陣布局,每個單元格放置一個矩形對象

rect <- rectGrob()
rect_mat <- matrix(rep(list(rect), 9), nrow = 3)

gt <- gtable_matrix(
  "rects", rect_mat, widths = unit(rep(1, 3), "null"),
  heights = unit(rep(1, 3), "null")
)

plot(gt)

添加行列間距

# 行距 0.5cm
gt <- gtable_add_row_space(gt, unit(0.5, "cm"))
# 列距分別為 0.5cm、1cm
gt <- gtable_add_col_space(gt, unit(c(0.5, 1), "cm"))

plot(gt)

3.5 gtable_trim

gtable_trim 函數(shù)用于刪除不包含 grob 的行或列,如果刪除的行和列的高度或?qū)挾葹?0,則有可能會改變整個布局

rect <- rectGrob(gp = gpar(fill = "black"))
base <- gtable(unit(c(2, 2, 2), "cm"), unit(c(2, 2, 2), "cm"))

center <- gtable_add_grob(base, rect, 2, 2)
plot(center)
gtable_show_layout(center)

刪除空行、空列

plot(gtable_trim(center))

最后只剩下一個單元格

gtable_show_layout(gtable_trim(center))

3.6 gtable_filter

通常來說,gtable 對象在索引時可以將其當(dāng)做一個矩陣,gtable_filter 可以根據(jù)名稱對 grob 進(jìn)行篩選

gtable_filter(x, pattern, fixed = FALSE, trim = TRUE, invert = FALSE)
  • pattern: 可以是正則表達(dá)式,匹配 grob 名稱
  • fixed: 如果為 TRUE,關(guān)閉正則匹配
  • trim: 如果為 TRUE,調(diào)用 gtable_trim
  • invert: 是否逆向匹配

創(chuàng)建 13 列的布局,并在第一列繪制矩形,第三列繪制圓形

gt <- gtable(unit(rep(5, 3), c("cm")), unit(5, "cm"))
rect <- rectGrob(gp = gpar(fill = "black"))
circ <- circleGrob(gp = gpar(fill = "red"))

gt <- gtable_add_grob(gt, rect, 1, 1, name = "rect")
gt <- gtable_add_grob(gt, circ, 1, 3, name = "circ")

plot(gt)

只顯示矩形

plot(gtable_filter(gt, "rect"))

只顯示圓形

plot(gtable_filter(gt, "circ"))

不刪除空列

plot(gtable_filter(gt, "circ", trim = FALSE))

介紹完了 gtable 之后,下一節(jié)將介紹如何繪制多個 Y

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容