基线表显示了研究中包含的研究对象的特征。根据研究设计,它们可能有特定目的,但通常它们显示所包含的人群是否符合研究的资格标准。它们还表明结果可以推广到哪些人群。在病例对照研究中,基线表可以确认成功匹配或表明组之间的差异。在随机对照试验 (RCT) 中,随机化过程旨在分配可能影响结果的组参与者特征;基线表将表明随机化是否导致成功平衡。在可行性研究中,基线表也可能表明哪些人群愿意参与研究设计。在对单个研究进行荟萃分析时,可能需要多个基线表,其中应为组合数据集以及每个单个研究提供特征,以便评估人群中的同质性
前言说了这么多,其实就是如上图所示(表来源于The Lancet Regional Health – Europe,https://doi.org/10.1016/j.lanepe.2021.100096),其中数据主要分为两大类,分类变量(例如性别)和连续变量(例如临床指标胆红素、血红蛋白......)
1. 问题
问题1: 这么多统计量,P值计算需要注意什么?
答: 一句话总结:
连续变量需要区分方差是否齐,是否为正态分布,两组比较还是多组比较;
分类变量需要区分二分类变量(性别(男/女)****)还是多分类变量(血型(A、B、O、AB))。
根据不同的统计结果,就可以选择不同的统计方法了(t检验,方差分析,秩和检验,kruskal检验,卡方检验,fisher检验等等)
问题2: 怎么选?你能帮帮我吗?
答: 一图搞定
问题3: 太复杂了,不仅得一个一个计算,还得考虑不同的算法,你还能帮帮我吗?
答: 这也就引出了今天的主角,自动生成Table 1的神器
2. 正文
tableone包是一个 R 包,它简化了“表1”的构建,即在生物医学研究论文中常见的患者基线特征表。这些包可以汇总一个表中混合的连续变量和分类变量。分类变量可以概括为计数和/或百分比。连续变量可以用“正态”方式(平均值和标准差)或“非正态”方式(中位数和四分位距)来概括
2.1 包的安装与加载
install.packages("tableone") #安装包
library("tableone") #载入包
2.2 数据介绍
数据主要来源于survival包中的pbc数据集,里边包含了患者的生存时间生存状态以及其他临床指标的一些数据,可以通过以下代码在R中加载,也可以后台回复Tableone获取这个数据
library(survival) # 需要安装
data(pbc)
2.3 最简单的用法
CreateTableOne(data = pbc)
#此处为结果,无需运行
Overall
n 418
id (mean (SD)) 209.50 (120.81)
time (mean (SD)) 1917.78 (1104.67)
status (mean (SD)) 0.83 (0.96)
trt (mean (SD)) 1.49 (0.50)
age (mean (SD)) 50.74 (10.45)
sex = f (%) 374 (89.5)
ascites (mean (SD)) 0.08 (0.27)
hepato (mean (SD)) 0.51 (0.50)
spiders (mean (SD)) 0.29 (0.45)
edema (mean (SD)) 0.10 (0.25)
bili (mean (SD)) 3.22 (4.41)
chol (mean (SD)) 369.51 (231.94)
albumin (mean (SD)) 3.50 (0.42)
copper (mean (SD)) 97.65 (85.61)
alk.phos (mean (SD)) 1982.66 (2140.39)
ast (mean (SD)) 122.56 (56.70)
trig (mean (SD)) 124.70 (65.15)
platelet (mean (SD)) 257.02 (98.33)
protime (mean (SD)) 10.73 (1.02)
但是,这还是和我们之前看见的表还是有差距的:
- 没有分组与P值
- 类似于status这种分类变量,它还是用均值(标准差)来表示的,这肯定不符合要求
因此需要找出分类变量和连续变量
2.4 分类变量的操作
大多数分类变量都是用数字编码的,所以我们要么必须将它们转换为数据集中的因子,要么使用 factorVars 参数即时转换它们。此外,最好通过 vars 参数指定要汇总的变量,并排除 ID 变量。什么是因子,其实就是分组,二分类就是2个因子,三分类就是3个因子
## 查看变量类型
dput(names(pbc))
## 此处为结果,无需运行
c("id", "time", "status", "trt", "age", "sex", "ascites", "hepato",
"spiders", "edema", "bili", "chol", "albumin", "copper", "alk.phos",
"ast", "trig", "platelet", "protime", "stage")
然后把分类变量挑出来
## 所有变量
myVars <- c("time", "status", "trt", "age", "sex", "ascites", "hepato",
"spiders", "edema", "bili", "chol", "albumin", "copper", "alk.phos",
"ast", "trig", "platelet", "protime", "stage")
## 分类变量
catVars <- c("status", "trt", "ascites", "hepato",
"spiders", "edema", "stage")
## 创建Table one
tab2 <- CreateTableOne(vars = myVars, data = pbc, factorVars = catVars)
tab2
#此处为结果,无需运行
Overall
n 418
time (mean (SD)) 1917.78 (1104.67)
status (%)
0 232 (55.5)
1 25 ( 6.0)
2 161 (38.5)
trt = 2 (%) 154 (49.4)
age (mean (SD)) 50.74 (10.45)
sex = f (%) 374 (89.5)
ascites = 1 (%) 24 ( 7.7)
hepato = 1 (%) 160 (51.3)
spiders = 1 (%) 90 (28.8)
edema (%)
0 354 (84.7)
0.5 44 (10.5)
1 20 ( 4.8)
bili (mean (SD)) 3.22 (4.41)
chol (mean (SD)) 369.51 (231.94)
albumin (mean (SD)) 3.50 (0.42)
copper (mean (SD)) 97.65 (85.61)
alk.phos (mean (SD)) 1982.66 (2140.39)
ast (mean (SD)) 122.56 (56.70)
trig (mean (SD)) 124.70 (65.15)
platelet (mean (SD)) 257.02 (98.33)
protime (mean (SD)) 10.73 (1.02)
stage (%)
1 21 ( 5.1)
2 92 (22.3)
3 155 (37.6)
4 144 (35.0)
这分组是正常了,但是还没有分组和P值,而且在文章中不同分类一般是单独一列的。
如果要显示所有级别,可以使用 print() 方法的 showAllLevels 参数
print(tab2, showAllLevels = TRUE, formatOptions = list(big.mark = ","))
# 此处为结果,无需运行
level Overall
n 418
time (mean (SD)) 1,917.78 (1,104.67)
status (%) 0 232 (55.5)
1 25 ( 6.0)
2 161 (38.5)
trt (%) 1 158 (50.6)
2 154 (49.4)
age (mean (SD)) 50.74 (10.45)
sex (%) m 44 (10.5)
f 374 (89.5)
ascites (%) 0 288 (92.3)
1 24 ( 7.7)
hepato (%) 0 152 (48.7)
1 160 (51.3)
spiders (%) 0 222 (71.2)
1 90 (28.8)
edema (%) 0 354 (84.7)
0.5 44 (10.5)
1 20 ( 4.8)
bili (mean (SD)) 3.22 (4.41)
chol (mean (SD)) 369.51 (231.94)
albumin (mean (SD)) 3.50 (0.42)
copper (mean (SD)) 97.65 (85.61)
alk.phos (mean (SD)) 1,982.66 (2,140.39)
ast (mean (SD)) 122.56 (56.70)
trig (mean (SD)) 124.70 (65.15)
platelet (mean (SD)) 257.02 (98.33)
protime (mean (SD)) 10.73 (1.02)
stage (%) 1 21 ( 5.1)
2 92 (22.3)
3 155 (37.6)
4 144 (35.0)
2.5 连续变量正态与非正态分布描述是不一样的
除了时间、年龄、白蛋白和血小板外,大多数连续变量看起来都高度偏斜(生物标志物通常以强正偏态分布)。一般来说,正态分布数据使用均值(标准差),偏态分布采用中位数(上四分位数,下四分位数)进行描述会获得同行评审的认可。因此,在 print() 方法中定义非正态分布的变量,即可完成。如果设置 nonnormal = TRUE,那么所有变量都以“非正态”的方式汇总
biomarkers <- c("bili","chol","copper","alk.phos","ast","trig","protime")
2.6 两组之间进行统计检验
就像例子所示,我们一般根据实验目的,将患者分组并逐组统计检验。这也很简单。按暴露类别(数据中为trt列)分组可能是最常见的方式。因此,代码如下,主要设定strata参数为"trt",分类参数为factorVars:
tab3 <- CreateTableOne(vars = myVars, strata = "trt" , data = pbc, factorVars = catVars)
# 此处为结果,无需运行
Stratified by trt
level 1 2 p test
n 158 154
time (mean (SD)) 2,015.62 (1,094.12) 1,996.86 (1,155.93) 0.883
status (%) 0 83 ( 52.5) 85 ( 55.2) 0.894
1 10 ( 6.3) 9 ( 5.8)
2 65 ( 41.1) 60 ( 39.0)
trt (%) 1 158 (100.0) 0 ( 0.0) <0.001
2 0 ( 0.0) 154 (100.0)
age (mean (SD)) 51.42 (11.01) 48.58 (9.96) 0.018
sex (%) m 21 ( 13.3) 15 ( 9.7) 0.421
f 137 ( 86.7) 139 ( 90.3)
ascites (%) 0 144 ( 91.1) 144 ( 93.5) 0.567
1 14 ( 8.9) 10 ( 6.5)
hepato (%) 0 85 ( 53.8) 67 ( 43.5) 0.088
1 73 ( 46.2) 87 ( 56.5)
spiders (%) 0 113 ( 71.5) 109 ( 70.8) 0.985
1 45 ( 28.5) 45 ( 29.2)
edema (%) 0 132 ( 83.5) 131 ( 85.1) 0.877
0.5 16 ( 10.1) 13 ( 8.4)
1 10 ( 6.3) 10 ( 6.5)
bili (median [IQR]) 1.40 [0.80, 3.20] 1.30 [0.72, 3.60] 0.842 nonnorm
chol (median [IQR]) 315.50 [247.75, 417.00] 303.50 [254.25, 377.00] 0.544 nonnorm
albumin (mean (SD)) 3.52 (0.44) 3.52 (0.40) 0.874
copper (median [IQR]) 73.00 [40.00, 121.00] 73.00 [43.00, 139.00] 0.717 nonnorm
alk.phos (median [IQR]) 1,214.50 [840.75, 2,028.00] 1,283.00 [922.50, 1,949.75] 0.812 nonnorm
ast (median [IQR]) 111.60 [76.73, 151.51] 117.40 [83.78, 151.90] 0.459 nonnorm
trig (median [IQR]) 106.00 [84.50, 146.00] 113.00 [84.50, 155.00] 0.370 nonnorm
platelet (mean (SD)) 258.75 (100.32) 265.20 (90.73) 0.555
protime (median [IQR]) 10.60 [10.03, 11.00] 10.60 [10.00, 11.40] 0.588 nonnorm
stage (%) 1 12 ( 7.6) 4 ( 2.6) 0.201
2 35 ( 22.2) 32 ( 20.8)
3 56 ( 35.4) 64 ( 41.6)
4 55 ( 34.8) 54 ( 35.1)
如果需要显示标准均值差(SMD),仅需设置参数即可
print(tab3, nonnormal = biomarkers, exact = "stage", smd = TRUE)
# 结果略
但是,这些结果都是在R中,复制粘贴的话,操作不方便,而且还需要后期对齐,因此推荐输出为csv,代码如下:
tab3Mat <- print(tab3, nonnormal = biomarkers, exact = "stage", quote = FALSE, noSpaces = TRUE, printToggle = FALSE)
## 保存为csv格式文件
write.csv(tab3Mat, file = "myTable.csv")
2.7 简洁版
上面看着还挺复杂,其实实际使用的话仅需要这么几步
library(tableone)
library(survival)
data(pbc)
#设置所有变量
myVars <- c("time", "status", "trt", "age", "sex", "ascites", "hepato",
"spiders", "edema", "bili", "chol", "albumin", "copper", "alk.phos",
"ast", "trig", "platelet", "protime", "stage")
## 设置分类变量
catVars <- c("status", "trt", "ascites", "hepato",
"spiders", "edema", "stage")
for (i in catVars) {
pbc[,i]<-as.factor(pbc[,i])
}
## 设置偏态分布变量
biomarkers <- c("bili","chol","copper","alk.phos","ast","trig","protime")
tab3 <- CreateTableOne(vars = myVars, strata = "trt" , data = pbc, factorVars = catVars)
tab3Mat <- print(tab3, nonnormal = biomarkers, exact = "stage", quote = FALSE, noSpaces = TRUE, printToggle = FALSE)
## 保存为csv格式文件
write.csv(tab3Mat, file = "myTable.csv")
在Excel或者WPS中就可以优雅地打开了
最后一个常见问题,就是这个技能、代码是否有必要学习?
我认为其实就像过河一样,有的河一辈子就过一次,那么游过去就行了;而有的时候每隔几天就需要过一次河,那么为什么不花点时间造个桥,平平坦坦走过去呢
感谢观看,如果有用还请点赞,收藏,转发!