8  非交互表格

一些需要打印或出版的场景需要制作非交互的表格。R 语言社区也有不少 R 包可以制作所需表格,其中 gt 包非常值得介绍,它可以制作非常复杂的表格样式,表格定制能力很强,gt 包的设计遵循 ggplot2 的设计哲学,打造制作表格的语法。

8.1 快速入门

在前面的章节,我们已经遇到用 knitr 包的 kable() 函数制作的表格。

library(knitr)
kable(head(iris))
表 8.1: 鸢尾花数据集(部分)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

首先用 gt 包制作一个非常简单的 表 8.2

library(gt)
gt(head(iris))
表 8.2: 鸢尾花数据集(部分)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

8.2 基础功能

8.2.1 添加标题

然后添加表格的标题和副标题,套上 md() 函数后,标题和副标题支持 Markdown 语法,告别 HTML 的制表方式吧!

library(data.table)
library(gt)
iris_dt <- as.data.table(iris)[, head(.SD, 2), by = .(Species)]
iris_dt |> 
  gt() |> 
  tab_header(
    title = md("**鸢尾花**数据集"),
    subtitle = "R 内置数据集"
  )
表 8.3: 鸢尾花数据集
鸢尾花数据集
R 内置数据集
Species Sepal.Length Sepal.Width Petal.Length Petal.Width
setosa 5.1 3.5 1.4 0.2
setosa 4.9 3.0 1.4 0.2
versicolor 7.0 3.2 4.7 1.4
versicolor 6.4 3.2 4.5 1.5
virginica 6.3 3.3 6.0 2.5
virginica 5.8 2.7 5.1 1.9

8.2.2 重命名列

表格作为数据分类汇总和展示的工具,有时候原始数据的列名含义不够明确,需要重新命名。

iris_dt |> 
  gt() |> 
  cols_label(
    Species = "种类",
    Sepal.Length = "花萼长度",
    Sepal.Width = "花萼宽度",
    Petal.Length = "花瓣长度",
    Petal.Width = "花瓣宽度"
  )
表 8.4: 鸢尾花数据集
种类 花萼长度 花萼宽度 花瓣长度 花瓣宽度
setosa 5.1 3.5 1.4 0.2
setosa 4.9 3.0 1.4 0.2
versicolor 7.0 3.2 4.7 1.4
versicolor 6.4 3.2 4.5 1.5
virginica 6.3 3.3 6.0 2.5
virginica 5.8 2.7 5.1 1.9

8.2.3 行列分组

行列分组的目的是让数据看起来更加清晰。让不同类型的行列分组,在数据上可以去重,在展示上表格看起来更加清爽。下表先按 Species 列进行行分组,再将剩余4列按花萼与花瓣分成两组。为了展示上的美观,在 Species 列前添加了行编号,调整表格宽度使之占页面宽度的 60%。

iris_dt |> 
  dplyr::mutate(row_id = dplyr::row_number(), .before = Species) |> 
  dplyr::group_by(Species) |> 
  gt(rowname_col = "row_id") |> 
  cols_label(
    Species = "种类",
    Sepal.Length = "长度",
    Sepal.Width = "宽度",
    Petal.Length = "长度",
    Petal.Width = "宽度"
  ) |> 
  tab_spanner(
    label = "花萼",
    columns = starts_with("Sepal")
  ) |>
  tab_spanner(
    label = "花瓣",
    columns = starts_with("Petal")
  ) |> 
  tab_options(
    table.width = pct(60)
  )
表 8.5: 鸢尾花数据集
花萼
花瓣
长度 宽度 长度 宽度
setosa
1 5.1 3.5 1.4 0.2
2 4.9 3.0 1.4 0.2
versicolor
3 7.0 3.2 4.7 1.4
4 6.4 3.2 4.5 1.5
virginica
5 6.3 3.3 6.0 2.5
6 5.8 2.7 5.1 1.9

8.2.4 填充颜色

在表格中填充颜色是为了突出和对比。gt 包提供函数 data_color() 来给表格上色,这个函数根据一组数据向量(表格中的某列)生成一组颜色值。下表根据列 Sepal.Length 的值和 viridis 调色板填充颜色。函数 data_color() 的参数 palette 可以接受来自 viridis 和 RColorBrewer 包的调色板的名称,如 “viridis”,也可以接受来自 paletteer 包的调色板,此时,指定调色板需要带所属的 R 包名,如 palette = "ggsci::red_material" 表示使用 ggsci 包的 red_material 调色板

iris_dt |> 
  gt() |> 
  tab_header(
    title = md("**鸢尾花**数据集"),
    subtitle = "R 内置数据集"
  ) |> 
  data_color(
    columns = "Sepal.Length",
    palette = "viridis",
    reverse = TRUE
  )
表 8.6: 鸢尾花数据集
鸢尾花数据集
R 内置数据集
Species Sepal.Length Sepal.Width Petal.Length Petal.Width
setosa 5.1 3.5 1.4 0.2
setosa 4.9 3.0 1.4 0.2
versicolor 7.0 3.2 4.7 1.4
versicolor 6.4 3.2 4.5 1.5
virginica 6.3 3.3 6.0 2.5
virginica 5.8 2.7 5.1 1.9

除了使用现成的 R 包中的调色板,还可以使用自定义调色板。函数 data_color() 的参数 fn 可以传递自定义的颜色生成函数,颜色映射函数 scales::col_numeric() 可以将数值转化为颜色向量。

iris_dt |> 
  gt() |> 
  tab_header(
    title = md("**鸢尾花**数据集"),
    subtitle = "R 内置数据集"
  ) |> 
  data_color(
    columns = "Sepal.Length",
    fn = scales::col_numeric(palette = "viridis", reverse = T, domain = NULL)
  )
表 8.7: 鸢尾花数据集
鸢尾花数据集
R 内置数据集
Species Sepal.Length Sepal.Width Petal.Length Petal.Width
setosa 5.1 3.5 1.4 0.2
setosa 4.9 3.0 1.4 0.2
versicolor 7.0 3.2 4.7 1.4
versicolor 6.4 3.2 4.5 1.5
virginica 6.3 3.3 6.0 2.5
virginica 5.8 2.7 5.1 1.9

8.2.5 添加脚注

类似添加表格标题和副标题,也可以添加脚注,且与表格的列指标关联。下表添加了两个脚注,一个与数据集中的 Sepal.Length 列关联,说明本数据集的来源,另一个脚注与列 Species 关联,说明其含义。

iris_dt |> 
  gt() |> 
  tab_header(
    title = md("**鸢尾花**数据集"),
    subtitle = "R 内置数据集"
  ) |> 
  data_color(
    columns = "Sepal.Length",
    method = "numeric", palette = "viridis", reverse = TRUE
  ) |> 
  data_color(
    columns = "Species", palette = "Set1"
  ) |> 
  tab_footnote(
    footnote = md("据说数据集最早收集自 Fisher's or Anderson's"),
    locations = cells_column_labels(columns = "Sepal.Length")
  ) |> 
  tab_footnote(
    footnote = "鸢尾花的类别",
    locations = cells_column_labels(columns = "Species")
  )
表 8.8: 鸢尾花数据集
鸢尾花数据集
R 内置数据集
Species1 Sepal.Length2 Sepal.Width Petal.Length Petal.Width
setosa 5.1 3.5 1.4 0.2
setosa 4.9 3.0 1.4 0.2
versicolor 7.0 3.2 4.7 1.4
versicolor 6.4 3.2 4.5 1.5
virginica 6.3 3.3 6.0 2.5
virginica 5.8 2.7 5.1 1.9
1 鸢尾花的类别
2 据说数据集最早收集自 Fisher’s or Anderson’s

8.3 扩展功能

8.3.1 输出格式

类似 ggplot2 包的函数 ggsave(),gt 包也有导出表格的函数 gtsave() ,下面将本章开头制作的简单表格保存为 PNG 格式图片。它还支持导出 HTML 、TeX 和 DOCX 等常见格式。

gt(head(iris)) |> 
  gtsave(filename = "screenshots/iris.png", expand = 10)

8.3.2 渲染公式

若表格中含有数学公式,支持 Markdown 语法。

# 示例数据
df <- tibble::tribble(
  ~名称, ~公式,
  "欧拉公式", "$e^{i \\pi} + 1 = 0$",
  "勾股定理", "$a^2 + b^2 = c^2$"
)

df |> 
  gt() |> 
  tab_header(
    title = md("$y=X\\beta+\\epsilon$")
  ) |> 
  fmt_markdown(columns = "公式")
表 8.9: 插入数学公式
\(y=X\beta+\epsilon\)
名称 公式
欧拉公式 \(e^{i \pi} + 1 = 0\)
勾股定理 \(a^2 + b^2 = c^2\)

8.3.3 添加水印

gt 包提供函数 local_image() 加载本地图片,可再用函数 html() 将图片数据渲染出来。

iris_dt |> 
  gt() |> 
  tab_header(
    title = md("**鸢尾花**数据集"),
    subtitle = html(local_image(filename = "images/Rlogo.png", height = 30)
  ))
图 8.1: 添加水印

8.4 其它 R 包

8.4.1 gtExtras 包

gtExtras 包扩展 gt 包的能力,提供一些高级操作需要的函数,比如根据某一列的数值绘制条形图,这可以用一个函数 gt_plt_bar() 搞定。略有不足的是它仅支持 HTML 输出。如果需要 PNG 格式,调用函数 gtsave() 导出。

library(gtExtras)
iris_dt |>
  gt() |>
  gt_plt_bar(column = Sepal.Length, keep_column = TRUE)
图 8.2: gtExtras 包

8.4.2 gtsummary 包

gtsummary(Sjoberg 等 2021) 也是扩展 gt 包的能力,除了简单的数据汇总(类似基础函数 summary()),尤其可以根据统计模型的输出结果进行整理、汇总成表格。

library(gtsummary)
iris |> 
  tbl_summary(include = c(Sepal.Length, Sepal.Width, Petal.Length), by = Species)
表 8.10: 按鸢尾花类别汇总
Characteristic setosa
N = 501
versicolor
N = 501
virginica
N = 501
Sepal.Length 5.00 (4.80, 5.20) 5.90 (5.60, 6.30) 6.50 (6.20, 6.90)
Sepal.Width 3.40 (3.20, 3.70) 2.80 (2.50, 3.00) 3.00 (2.80, 3.20)
Petal.Length 1.50 (1.40, 1.60) 4.35 (4.00, 4.60) 5.55 (5.10, 5.90)
1 Median (Q1, Q3)
iris_lm <- lm(Sepal.Length ~. , data = iris[, -5])
iris_lm |> tbl_regression()
表 8.11: 线性回归
Characteristic Beta 95% CI p-value
Sepal.Width 0.65 0.52, 0.78 <0.001
Petal.Length 0.71 0.60, 0.82 <0.001
Petal.Width -0.56 -0.81, -0.30 <0.001
Abbreviation: CI = Confidence Interval

除了 gt 包及其衍生包以外,另有两个制作表格的 R 包值得一提,分别是 tinytable 包和 kableExtra 包。

8.4.3 tinytable 包

顾名思义,tinytable 包是一个依赖极简的 R 包,这是一个最新出现的 R 包,与 tinyplot 包的设计同出于极简哲学。

library(tinytable)
tt(iris_dt, theme = "grid") |> 
  style_tt(i = 1:2, j = 1, color = "white", background = "#4B0055") |> 
  style_tt(i = 3:4, j = 1, color = "white", background = "#009B95") |> 
  style_tt(i = 5:6, j = 1, color = "white", background = "#FDE333")
表 8.12: iris 数据集(部分)
Species Sepal.Length Sepal.Width Petal.Length Petal.Width
setosa 5.1 3.5 1.4 0.2
setosa 4.9 3.0 1.4 0.2
versicolor 7.0 3.2 4.7 1.4
versicolor 6.4 3.2 4.5 1.5
virginica 6.3 3.3 6.0 2.5
virginica 5.8 2.7 5.1 1.9

8.4.4 kableExtra 包

kableExtra 包主要是扩展了 knitrkable() 函数的功能,相比于 gt 包,依赖更少,所以更加轻量。

library(knitr)
library(kableExtra)
options(kableExtra.html.bsTable = T)

iris_dt |>
  kable(booktabs = TRUE, escape = FALSE, align = "c",
        col.names = gsub("[.]", " ", names(iris_dt))) |>
  kable_styling(
    bootstrap_options = c("striped", "condensed"),
    latex_options = "basic", full_width = FALSE
  ) |> 
  column_spec(1, color = "white", background = spec_color(as.integer(iris_dt$Species))) |> 
  column_spec(2, color = spec_color(iris_dt$Sepal.Length))
表 8.13: iris 数据集(部分)
Species Sepal Length Sepal Width Petal Length Petal Width
setosa 5.1 3.5 1.4 0.2
setosa 4.9 3.0 1.4 0.2
versicolor 7.0 3.2 4.7 1.4
versicolor 6.4 3.2 4.5 1.5
virginica 6.3 3.3 6.0 2.5
virginica 5.8 2.7 5.1 1.9