# setup
library(tidyverse)
library(rio)
library(here)
library(sjPlot)
library(sjmisc)
<- import(here("files/data/student_sm.dta")) std_sm
如何制作表格?
为什么要制表?
数据分析过程是对数据进行减化的过程,是把大量的原始数据浓缩成一个或者一组数字,比如频数、均值、相关系数,用这些所谓的统计值来描述研究对象的特征和特征之间的关联。呈现分析结果的形式有两种:表格与图形。表格通过二维方格的形式将统计值直接显示出来。而图形则是通过色彩与形状间接地呈现这些统计值。虽然图形在表达信息方向比较直观,更容易发现多个统计值之间的关联。但是表格在表达统计信息时比较准确和全面,且图形的基础依然是统计值,因此,制表是数据分析过程中一项基本工作。R作为一门数据分析语言,提供了很多便捷的制表工具。
R制表package
表格是用来有条理地呈现统计数据,制表不仅涉及将原始数据统计计算生成统计表之外,还涉及对表格样式的设计与调整。R语言在这两个方面都有许多优秀的功能包。用于生成表格的package有base
,stats
, rstatix
, sjmisc
, janitor
, gtsummary
, modelsummary
。base
中的table()
, summary()
可分别对变量进行描述性统计,stats
中t.test()
, cor()
, lm()
, glm()
可以用来生成相关性的统计值,但这些function的输出结果不是太友好。而第三方package提供很多有效的解决方案。其中gtsummary
和modelsummary
专注于制表,gtsummary
兼容pipe,对labelled的数据支持比较友好,但其将统计值整合在一个单元格内,后期调整空间较小。相反,modelsummary
生成表格的功能及可调整空间都较大,尤其对统计回归表格的支持更为广泛。rstatix
, sjmisc
, janitor
虽然不是专门生成表格的package,但也提供一系列制表的function, 比如sjmisc
中的frq()
, janitor
中的tabyl()
. rstatix
既有计算频数和统计值的function,比如freq_table()
和get_summary_stats()
,且具有全面的相关性分析的function,比如t_test()
, chisq_test()
, cor_test()
, anova_test()
等,其生成的结果为data.frame格式,可直接用于制表。
对表格进行设计与调整的package有gt
, gtExtras
, flextable
, formattable
, kableExtra
, DT
, reactable
. 其中,flextable
可对表格全方位调整,语法结构容易理解,输出结果简约工整,适合制作正式出版所需的表格。gt
和gtExtras
配合使用,也可以对表格全方位调整,表格样式比较适合网站显示,且可以在表格中加入图形等要素。formattable
和kableExtra
配合使用,也可以对表格进行全方位的调整。DT
和reactable
主要是可以生成具有交互功能的表格,二者功能具有重合之处。本文主要讲述modelsummary
, flextable
,部分讲解rstatix
, sjmisc
, kableExtra
, DT
.
R制表技术介绍
根据表格统计信息的复杂程度,可将表格分为三种类型:描述性表格,比如频数表、统计表、交叉表等;相关性分析结果表,比如correlation, anovo, t-test, chi2-test的结果表;回归分析结果表。本文主要关注第一类,也会涉及相关性分析表,不探讨回归分析结果表制作的问题。
频数表主要是对离散型变量各取值的频数和占比进行统计。交叉表是两个或以上的离散型变量的频数及占比的统计描述。统计表主要是对连续型变量的均值、方差、区间等信息的统计。还有一种两格就是分组生成连续型变量的统计表。
单一变量的表述
base
包有两个快速统计分析的function: table()
和summary()
。可以有助于快速获得单一变量的统计信息。
#base
table(std_sm$sex)
1 2
418 394
#base
summary(std_sm$tr_chn)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
2.0 98.0 112.0 106.8 120.0 137.0 42
sjmisc
包中的frq()
可快速生成、格式友好的频数表。
library(sjmisc)
%>%
std_sm frq(computer, out="viewer")
val | label | frq | raw.prc | valid.prc | cum.prc | |
0 | 都没有 | 38 | 4.66 | 4.69 | 4.69 | |
1 | 有电脑,无网络 | 41 | 5.03 | 5.06 | 9.75 | |
2 | 有电脑和网络 | 731 | 89.69 | 90.25 | 100.00 | |
NA | NA | 5 | 0.61 | NA | NA | |
total N=815 · valid N=810 · x̄=1.86 · σ=0.47 |
对连续型变量,rstatix
中的get_summary_stats()
方便易用。
library(rstatix)
%>%
std_sm get_summary_stats(tr_chn)
# A tibble: 1 × 13
variable n min max median q1 q3 iqr mad mean sd se
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 tr_chn 773 2 137 112 98 120 22 13.3 107. 17.9 0.643
# … with 1 more variable: ci <dbl>
同时描述多个变量
在探索性分析初级阶段,需要首先对单个变量统计分析,在数据分析的下一阶段或者在分析结果展示阶段,会涉及对多个变量同时描述的情况。对于多个变量同时描述,我们将集中使用modelsummary
中的function。首先是对多个离散型变量的分析,使用datasummary_skim()
。
#modelsummary
library(sjmisc)
<- std_sm %>%
std_sm mutate(
across(c(matbias, party_p,ses_p,aftercls,
sex, hukou, computer, psyroom),to_label)
)#datasummary_skim
library(modelsummary)
%>%
std_sm select(sex, party_p, computer, aftercls) %>%
datasummary_skim(type = "categorical")
N | % | ||
---|---|---|---|
sex | 男 | 418 | 51.3 |
女 | 394 | 48.3 | |
party_p | 共产党员 | 105 | 12.9 |
民主党派 | 10 | 1.2 | |
群众 | 664 | 81.5 | |
computer | 都没有 | 38 | 4.7 |
有电脑,无网络 | 41 | 5.0 | |
有电脑和网络 | 731 | 89.7 | |
aftercls | 否 | 589 | 72.3 |
是 | 225 | 27.6 |
接下来是对多个连续型变量的同时描述,使用datasummary_skim()
。
%>%
std_sm select(tr_chn, tr_mat,tr_eng, height, weight, health) %>%
datasummary_skim(type = "numeric")
Unique (#) | Missing (%) | Mean | SD | Min | Median | Max | ||
---|---|---|---|---|---|---|---|---|
tr_chn | 92 | 5 | 106.8 | 17.9 | 2.0 | 112.0 | 137.0 | |
tr_mat | 124 | 5 | 111.1 | 30.4 | 3.0 | 121.0 | 150.0 | |
tr_eng | 183 | 5 | 107.4 | 30.3 | 10.0 | 116.0 | 148.0 | |
height | 44 | 2 | 167.1 | 7.8 | 145.0 | 166.0 | 190.0 | |
weight | 110 | 7 | 112.1 | 25.2 | 50.0 | 106.0 | 220.0 | |
health | 6 | 0 | 3.9 | 0.9 | 1.0 | 4.0 | 5.0 |
datasummary_balance()
可以将多个连续型变量和离散型变量同时显示。
%>%
std_sm select(tr_chn, tr_mat,tr_eng, height, weight, health,
%>%
sex, party_p, computer, aftercls) datasummary_balance(~ 1,data = .)
Mean | Std. Dev. | ||
---|---|---|---|
tr_chn | 106.8 | 17.9 | |
tr_mat | 111.1 | 30.4 | |
tr_eng | 107.4 | 30.3 | |
height | 167.1 | 7.8 | |
weight | 112.1 | 25.2 | |
health | 3.9 | 0.9 | |
N | Pct. | ||
sex | 男 | 418 | 51.3 |
女 | 394 | 48.3 | |
party_p | 共产党员 | 105 | 12.9 |
民主党派 | 10 | 1.2 | |
群众 | 664 | 81.5 | |
computer | 都没有 | 38 | 4.7 |
有电脑,无网络 | 41 | 5.0 | |
有电脑和网络 | 731 | 89.7 | |
aftercls | 否 | 589 | 72.3 |
是 | 225 | 27.6 |
datasummary_balance()
还可以对变量分组生成描述统计表。
%>%
std_sm select(tr_chn, tr_mat,tr_eng, height, weight, health,
%>%
sex, party_p, computer, aftercls) datasummary_balance(~ sex,data = .,dinm_statistic = "p.value")
Mean | Std. Dev. | Mean | Std. Dev. | Diff. in Means | p | ||
---|---|---|---|---|---|---|---|
tr_chn | 103.2 | 19.4 | 110.6 | 15.3 | 7.4 | 0.0 | |
tr_mat | 108.7 | 33.8 | 113.8 | 26.0 | 5.1 | 0.0 | |
tr_eng | 99.9 | 33.4 | 115.4 | 24.1 | 15.4 | 0.0 | |
height | 171.9 | 6.8 | 162.1 | 5.1 | -9.8 | 0.0 | |
weight | 121.2 | 28.6 | 102.4 | 16.1 | -18.8 | 0.0 | |
health | 4.0 | 0.9 | 3.8 | 0.9 | -0.2 | 0.0 | |
N | Pct. | N | Pct. | ||||
party_p | 共产党员 | 50 | 12.0 | 55 | 14.0 | ||
民主党派 | 4 | 1.0 | 6 | 1.5 | |||
群众 | 338 | 80.9 | 323 | 82.0 | |||
computer | 都没有 | 22 | 5.3 | 15 | 3.8 | ||
有电脑,无网络 | 28 | 6.7 | 12 | 3.0 | |||
有电脑和网络 | 367 | 87.8 | 363 | 92.1 | |||
aftercls | 否 | 278 | 66.5 | 310 | 78.7 | ||
是 | 139 | 33.3 | 84 | 21.3 |
交叉表
交叉表是对两个或以上离散型变量统计计算而产生的表,二维交叉表是两个变量统计分析的结果。datasummary_crosstab()
可以生成二维交叉表。
datasummary_crosstab(sex ~ matbias, data = std_sm)
sex | 是 | 否 | All | |
---|---|---|---|---|
男 | N | 261 | 149 | 418 |
% row | 62.4 | 35.6 | 100.0 | |
女 | N | 233 | 158 | 394 |
% row | 59.1 | 40.1 | 100.0 | |
All | N | 495 | 309 | 815 |
% row | 60.7 | 37.9 | 100.0 |
三维交叉表是三个变量交叉分析得到的分析表。datasummary_crosstab()
可以生成三维交叉表。
%>%
std_sm datasummary_crosstab(computer ~ sex * aftercls ,
statistic = 1 ~ 1 + N + Percent("col"),
data= .)
computer | 否 | 是 | 否 | 是 | All | |
---|---|---|---|---|---|---|
都没有 | N | 9 | 12 | 9 | 6 | 38 |
% col | 3.2 | 8.6 | 2.9 | 7.1 | 4.7 | |
有电脑,无网络 | N | 22 | 6 | 6 | 6 | 41 |
% col | 7.9 | 4.3 | 1.9 | 7.1 | 5.0 | |
有电脑和网络 | N | 247 | 120 | 293 | 70 | 731 |
% col | 88.8 | 86.3 | 94.5 | 83.3 | 89.7 | |
All | N | 278 | 139 | 310 | 84 | 815 |
% col | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 |
连续型变量分析表
相关系数是描述两个连续型变量之间关系强度一个统计值,datasummary_correlation()
可以快速生成多个连续变量之间的相关系数。
#datasummary_correlation
%>%
std_sm select(stcog, tr_chn, tr_mat, tr_eng, height, weight, health) %>%
datasummary_correlation()
stcog | tr_chn | tr_mat | tr_eng | height | weight | health | |
---|---|---|---|---|---|---|---|
stcog | 1 | . | . | . | . | . | . |
tr_chn | .27 | 1 | . | . | . | . | . |
tr_mat | .40 | .77 | 1 | . | . | . | . |
tr_eng | .35 | .75 | .77 | 1 | . | . | . |
height | .08 | −.07 | .03 | −.11 | 1 | . | . |
weight | .05 | −.05 | −.02 | −.13 | .56 | 1 | . |
health | −.01 | −.06 | −.04 | .00 | .14 | .05 | 1 |
R表格调整与设计技术
表格设计可以分为两种情况,一种是对其它package生成后的表格进行再调整,比如对modelsummary
生成的表格进行调整,另一种情况是对原始数据进行表格的设计。
表格的二次调整
flextable
提供了对表格表头、表尾、行、列和单元格调整的各种function。可对文字、背景、边框等多个要素调整,以下是对modelsummary
生成表格进一步调整的结果。
library(flextable)
%>%
std_sm select(tr_chn, tr_mat, tr_eng, stcog, height, weight, health,sex) %>%
datasummary_balance(~ sex,data = .,output = "flextable",
dinm_statistic = "p.value",fmt = 2) %>%
set_header_labels(
values =list(`男 (N=418) / Mean`="均值",
`男 (N=418) / Std. Dev.`="方差",
`女 (N=394) / Mean`="均值",
`女 (N=394) / Std. Dev.`="方差",
`Diff. in Means`="均值差",
p="P值")) %>%
add_header_row(
values = c("","男 (N=418)","女 (N=394)",""),
colwidths = c(1,2,2,2)) %>%
bold(j=7,bold = TRUE) %>%
color(i=4,color = "red") %>%
bg(i=7,bg="lightblue") %>%
width(j=7,width = 0.5) %>%
hline(i=3:4,border = fp_border_default()) %>%
::footnote(j=7,value = as_paragraph("男女均值之间的差值"),
flextableref_symbols = c("1"),part = "header") %>%
set_caption(caption = "Table 1. 数学偏见人群之间的差异对比表")
男 (N=418) |
女 (N=394) |
|||||
---|---|---|---|---|---|---|
|
均值 |
方差 |
均值 |
方差 |
均值差 |
P值1 |
tr_chn |
103.22 |
19.36 |
110.57 |
15.33 |
7.35 |
0.00 |
tr_mat |
108.72 |
33.77 |
113.82 |
25.95 |
5.10 |
0.02 |
tr_eng |
99.93 |
33.45 |
115.37 |
24.13 |
15.44 |
0.00 |
stcog |
10.79 |
3.82 |
10.68 |
3.56 |
-0.11 |
0.67 |
height |
171.86 |
6.83 |
162.10 |
5.11 |
-9.75 |
0.00 |
weight |
121.18 |
28.61 |
102.39 |
16.06 |
-18.79 |
0.00 |
health |
4.04 |
0.92 |
3.80 |
0.88 |
-0.24 |
0.00 |
1男女均值之间的差值 |
kableExtra
也提供了对表格表头、表尾、行、列和单元格调整的各种function,也可对文字、背景、边框等多个要素调整,以下是使用kableExtra
对modelsummary
生成表格进一步调整的结果。
library(kableExtra)
%>%
std_sm select(tr_chn, tr_mat, tr_eng, stcog,
%>%
height, weight, health, matbias) datasummary_balance(~ matbias,data = .,output = "kableExtra",
dinm_statistic = "p.value",
title = "数学偏见人群之间的差异对比表",
col.names=c("","均值","方差","均值","方差","均值差","P值")) %>%
kable_styling() %>%
group_rows("学习成绩", 1,3) %>%
group_rows("认知能力",4,4) %>%
group_rows("身体健康",5,7)%>%
column_spec(6:7, bold = T) %>%
row_spec(4, bold = T, color = "white",
background = "#D7261E",hline_after = FALSE)
均值 | 方差 | 均值 | 方差 | 均值差 | P值 | |
---|---|---|---|---|---|---|
学习成绩 | ||||||
tr_chn | 107.2 | 17.4 | 106.6 | 18.5 | -0.6 | 0.6 |
tr_mat | 110.4 | 30.2 | 112.9 | 30.3 | 2.5 | 0.3 |
tr_eng | 107.3 | 30.3 | 108.7 | 29.7 | 1.3 | 0.6 |
认知能力 | ||||||
stcog | 10.5 | 3.7 | 11.0 | 3.7 | 0.5 | 0.0 |
身体健康 | ||||||
height | 167.4 | 7.9 | 166.4 | 7.4 | -1.1 | 0.1 |
weight | 113.5 | 25.6 | 110.0 | 24.3 | -3.6 | 0.1 |
health | 4.0 | 0.9 | 3.8 | 1.0 | -0.2 | 0.0 |
对原始表格的直接调整
对原始表格进行美化,两个package组合,一个是formattable
和kableExtra
,以下是使用这两个package调整后的结果。这两个package的使用方法可参见其官方网站。
#筛选class id为100班级的学生数据
<- std_sm %>%
cls_perf filter(clsids==100) %>%
select(ids, stcog, tr_chn, tr_mat,
tr_eng,stcog, ses_p, height,weight, health, aftercls, sex)#kableExtra & formattable
library(formattable)
library(kableExtra)
%>%
cls_perf arrange(stcog) %>%
drop_na() %>%
mutate(
stcog=color_tile("white","orange")(stcog),
health = cell_spec(health, "html", angle = (1:5)*60,
background = "red", color = "white", align = "center"),
aftercls = ifelse(aftercls == "是",
cell_spec(aftercls, "html", color = "red", bold = T),
cell_spec(aftercls, "html", color = "green", italic = T)),
tr_eng = cell_spec(tr_eng,color = if_else(tr_eng>120,"#AE0B2A","black")),
height = color_bar("lightgreen",na.rm=TRUE)(height),
tr_mat = color_bar("lightblue",na.rm=TRUE)(tr_mat)
%>%
) kable(escape = FALSE,
col.names = c("ID","认知能力","语文","数学","英语",
"经济条件","身高","体重","健康水平","课外班","性别"),
caption = "Table 1. 3900班学生成绩表") %>%
kable_styling(bootstrap_options = c("condensed"),
full_width = FALSE) %>%
column_spec(7,width = "2cm") %>%
row_spec(0,color="white",background = "#AE0B2A") %>%
::footnote(general = "数据来源于CEPS.") kableExtra
ID | 认知能力 | 语文 | 数学 | 英语 | 经济条件 | 身高 | 体重 | 健康水平 | 课外班 | 性别 |
---|---|---|---|---|---|---|---|---|---|---|
3983 | 4 | 108 | 122 | 107 | 比较富裕 | 164 | 116 | 4 | 否 | 女 |
3984 | 6 | 105 | 126 | 97 | 中等 | 177 | 214 | 2 | 是 | 男 |
3995 | 7 | 105 | 120 | 115 | 中等 | 160 | 88 | 3 | 否 | 女 |
3988 | 9 | 106 | 108 | 109 | 中等 | 160 | 98 | 4 | 是 | 女 |
3990 | 10 | 106 | 138 | 80 | 中等 | 165 | 90 | 3 | 否 | 女 |
3997 | 10 | 99 | 135 | 126 | 中等 | 166 | 110 | 4 | 否 | 男 |
3998 | 10 | 91 | 126 | 100 | 中等 | 169 | 90 | 3 | 是 | 男 |
3991 | 11 | 109 | 125 | 117 | 比较富裕 | 169 | 92 | 3 | 否 | 女 |
3992 | 13 | 103 | 125 | 102 | 中等 | 160 | 120 | 4 | 否 | 女 |
3993 | 13 | 100 | 130 | 107 | 比较富裕 | 173 | 120 | 4 | 否 | 女 |
3985 | 14 | 98 | 133 | 89 | 中等 | 168 | 120 | 4 | 否 | 男 |
3986 | 15 | 105 | 139 | 130 | 中等 | 161 | 100 | 4 | 是 | 男 |
3999 | 16 | 108 | 131 | 121 | 比较困难 | 180 | 148 | 4 | 是 | 男 |
Note: | ||||||||||
数据来源于CEPS. |
gt
和gtExtras
也提供强大的表格设计功能,以下是使用这两个package调整后的结果。这两个package的使用方法可参见其官方网站。
library(gt)
library(gtExtras)
<- cls_perf %>%
cls_perf2 mutate(sex=if_else(sex=="男","mars","mercury"),
aftercls=if_else(aftercls=="是","check","xmark")) %>%
arrange(stcog) %>%
drop_na()
%>%
cls_perf2 gt() %>%
gt_hulk_col_numeric(starts_with("tr"),trim=TRUE) %>%
gt_color_box(stcog, domain = 4:16,
palette = c("purple", "green")) %>%
gt_highlight_cols(ses_p,fill="green",alpha = 0.1) %>%
gt_fa_column(sex,palette = c("purple","grey")) %>%
gt_fa_column(aftercls,palette = c("grey","green")) %>%
gt_fa_rating(health,color="green",icon = "heart") %>%
gt_duplicate_column(weight) %>%
gt_plt_bar_pct(weight_dupe) %>%
cols_move(weight_dupe,weight) %>%
gt_plt_point(height,
palette=c("purple", "lightgrey", "green"),width = 15) %>%
gt_plt_dot(height,ids) %>%
cols_label(ids="ID",stcog="认知能力",tr_chn="语文",tr_mat="数学", tr_eng="英语",
ses_p="经济条件",height="身高",health="健康水平",aftercls="课外班",
sex="性别",weight="体重", weight_dupe="体重指示") %>%
cols_align("center") %>%
tab_header(title = "Table 1. N3900班学生基本信息及成绩表") %>%
tab_source_note("Note: 数据来源于CEPS.") %>%
tab_style(cell_text(weight = "bold"),locations = cells_column_labels())
Table 1. N3900班学生基本信息及成绩表 | |||||||||||
ID | 认知能力 | 语文 | 数学 | 英语 | 经济条件 | 身高 | 体重 | 体重指示 | 健康水平 | 课外班 | 性别 |
---|---|---|---|---|---|---|---|---|---|---|---|
3983
|
4
|
108 | 122 | 107 | 比较富裕 | 116 | |||||
3984
|
6
|
105 | 126 | 97 | 中等 | 214 | |||||
3995
|
7
|
105 | 120 | 115 | 中等 | 88 | |||||
3988
|
9
|
106 | 108 | 109 | 中等 | 98 | |||||
3990
|
10
|
106 | 138 | 80 | 中等 | 90 | |||||
3997
|
10
|
99 | 135 | 126 | 中等 | 110 | |||||
3998
|
10
|
91 | 126 | 100 | 中等 | 90 | |||||
3991
|
11
|
109 | 125 | 117 | 比较富裕 | 92 | |||||
3992
|
13
|
103 | 125 | 102 | 中等 | 120 | |||||
3993
|
13
|
100 | 130 | 107 | 比较富裕 | 120 | |||||
3985
|
14
|
98 | 133 | 89 | 中等 | 120 | |||||
3986
|
15
|
105 | 139 | 130 | 中等 | 100 | |||||
3999
|
16
|
108 | 131 | 121 | 比较困难 | 148 | |||||
Note: 数据来源于CEPS. |