资讯动态

tableone基线表:连续型与分类变量的输出、正态性判据与检验选择

发布时间:2026/10/27 12:17:29 来源:尧图企业网站定制
上一次帮人看一份基线表作者把年龄写成了 58.3 ± 11.7审稿意见只有一句话年龄分布明显偏态请改用中位数和四分位数间距。作者很委屈说软件自动算的就是均数。我打开脚本一看CreateTableOne里什么参数都没写全靠默认值跑出来的。问题就出在这里tableone 默认会自己对连续型变量做一次正态性判断判断结果直接决定表里印均数还是印中位数而这个判断过程是静默的不会在表里留任何痕迹。分类变量那边也是同样的道理showAllLevels、exact、categorical这几个参数谁都没设出来的表看着挺整齐但水平展开方式和检验方法可能完全不是你想要的那一套。这篇就专门聊 tableone 里连续型变量和分类变量这两条主线。前面几篇把安装、数据准备、基本调用过了一遍这一篇往深里走正态性判据到底在哪一步生效、nonnormal怎么用才不出错、分类变量的水平怎么展开、Fisher 精确检验什么时候必须上、有序分类变量为什么常常要绕道处理。适合已经能把表跑出来、但对结果不太放心的人看也适合刚接手别人脚本、想搞清楚每一列是怎么来的朋友。文中所有数据都是模拟构造的代码可以直接抄去改。1. 那次被退回来的基线表连续型与分类变量的输出其实只有两条路tableone 对变量的处理逻辑非常干脆一个变量进来先判断它是连续型还是分类型然后走两条完全不同的分支。连续型走集中趋势 离散程度的路线输出均数标准差或者中位数四分位数间距分类型走计数 占比的路线输出 n (%)。两条分支各有各的组间比较检验各有各的参数开关混在一起讲容易乱所以先把这两条路的骨架摆出来。判断变量类型的依据是数据框里这一列的类型。数值型numeric / integer默认进连续型分支因子型factor和字符型character默认进分类型分支。这里第一个坑就出现了很多原始数据里性别用 1/2 编码、分级用 1/2/3/4 编码读进来全是数值型tableone 会老老实实把它们当连续变量算均数。你会得到一行性别 1.52 ± 0.50这样荒谬的结果。这个问题后面第 4 节专门讲先记住一句话类型决定分支编码方式不决定分支。连续型分支里tableone 还要再做一次判断这个变量服从正态分布吗服从就印均数标准差不服从就印中位数 [P25, P75]。这个判断默认由 Shapiro-Wilk 检验完成按分组逐组做任何一组拒绝正态整个变量就按非正态处理。所以同一个变量stratify 的变量换一下输出的形式可能就变了这一点很多人没意识到。分类型分支里tableone 要做的是把因子的每一个水平展开成一行或者只留第一行然后根据单元格频数决定用卡方还是 Fisher。展开方式和检验方式都由参数控制默认值未必适合你的表。举个最常见的场景一个五分类的变量showAllLevels TRUE展开成五行如果表里有七八个这样的变量一张表能拉到三页纸编辑部大概率会要求压缩。分支默认输出形式组间比较默认方法关键开关连续型正态mean (SD)oneway.test两组即 Welch tnonnormal连续型非正态median [P25, P75]kruskal.testnonnormal、minMax分类型无序n (%)chisq.test2×2 带连续性校正exact、showAllLevels分类型有序n (%) 或 median [IQR]chisq / kruskalnonnormal、categorical这张表建议贴在脚本注释里。每次出表前扫一眼问自己三个问题这一列的变量类型对吗正态判断的结果是我认可的吗组间比较用的检验是我想在方法学段落里写的那一个吗三个问题都过了表基本就不会出大问题。2. 连续型变量的正态性判据tableone 到底在哪一步替你做了决定2.1 分组内 Shapiro-Wilk 检验的执行顺序nonnormal参数不指定的时候tableone 不会问你直接自己判。判的方式是在每个分组内部对连续变量做一次 Shapiro-Wilk 正态性检验显著性水平取 0.05。注意是分组内不是整体。假设你按治疗组分成试验组和对照组年龄在试验组 p 0.31在对照组 p 0.04那么整个年龄变量就被判为非正态两组都印中位数。这个逻辑本身是保守的出发点是不能一个组印均数、另一个组印中位数那样表就没法看了。但它带来两个副作用值得提前想清楚。第一个副作用是分组越多越容易翻车。你把 strata 从一个三分类变量换成一个六分类变量每个格子里的样本量掉一半Shapiro-Wilk 的检验效能跟着下降p 值反而可能变大也可能变小结果就是同一个变量在两张表里的呈现形式不一致。这种情况在亚组分析里特别常见主表用均数、亚组表用中位数审稿人一眼就看出来了。第二个副作用是大样本下几乎必然拒绝正态。Shapiro-Wilk 检验对偏度非常敏感样本量上到几千哪怕分布只是轻微右偏p 值也会远小于 0.05。真实的临床数据里住院天数、费用、某些指标几乎没有真正对称的所以大样本研究里自动判出来全是中位数是常态。这时候反而要考虑是不是该手动指定一部分变量按正态处理因为均数在群体层面的解释性更好而且很多模型比如线性回归对自变量的分布并没有硬性要求。提示Shapiro-Wilk 检验在 R 里要求样本量在 3 到 5000 之间。某一组的样本量超过 5000 时这个检验会直接报错tableone 那一步就过不去。处理办法只有两个手动指定nonnormal或者先对数据做随机抽样判断后再指定。这一点在跑大型队列数据时几乎一定会遇到。还有一个细节值得说tableone 内部用来判正态的就是shapiro.test没有开放参数让你换成 Anderson-Darling 或者偏度峰度法。想换判据只能自己先算好再把结论通过nonnormal传进去。我个人的习惯是出表之前先自己画一遍直方图或者密度图对每个连续变量的形态心里有数然后用nonnormal显式指定尽量不让它自动判。这样表的呈现形式是确定的换个分组变量也不会变复现性好得多。2.2 nonnormal 参数的四种写法与覆盖时机nonnormal可以在CreateTableOne里给也可以在print里给两处都行效果一样。写法有几种各有适用场合。第一种是逻辑值nonnormal TRUE表示所有连续变量都按非正态处理全部印中位数。适合数据整体偏态明显、或者你压根不想纠结判据的场景。缺点是粒度太粗一个变量都跑不掉。第二种是字符向量nonnormal c(los, cost, crp)精确点名。这是最常用的写法可读性好写进脚本里别人一看就懂。建议在脚本顶部把这类变量名定义成变量别散落在调用里。第三种是公式写法nonnormal ~ los cost crp。适合变量多、想少打几个引号的情况。功能上和字符向量等价。第四种是逻辑向量长度和变量数一致按位置对应。不推荐因为变量顺序一变就错位了而且没人记得住第几个是哪个变量。# 三种等价写法选一种固定下来 print(tab, nonnormal c(los, cost, crp)) print(tab, nonnormal ~ los cost crp) print(tab, nonnormal TRUE) # 全部按非正态覆盖时机上有个容易混淆的点CreateTableOne阶段给的nonnormal和print阶段给的会叠加吗实测下来是以print阶段的为准也就是说你在建表时指定了一批打印时又指定另一批最终按打印时的来。这个行为不算坑但如果你把建表封装成一个函数、打印又单独写很容易忘了其中一处导致表的形式和预期不一致。我的做法是只在print里指定建表那一步保持干净。另外提一句非正态变量的组间比较默认走kruskal.test。两组的时候它就是 Wilcoxon 秩和检验p 值和wilcox.test基本一致有连续性校正的细微差别。多组就是 Kruskal-Wallis。方法学段落里描述的时候要写准别写成采用 Wilcoxon 检验却在三组数据上跑那是描述与实现不符。2.3 均数标准差、中位数四分位数间距与 min-max 的呈现取舍输出形式的选择不只是统计问题也是表达问题。均数标准差适合描述对称分布读者能直接感受到平均水平 ± 波动范围中位数 [P25, P75] 适合偏态分布对极端值不敏感。这两者在临床表里都有各自的地盘比如年龄、身高、体重指数通常用均数住院天数、费用、某些炎症指标通常用中位数。print里有个minMax参数设成TRUE之后连续变量的输出会变成median [P25, P75, min, max]这样四段式。有人喜欢这么写觉得信息量大我不太推荐在正文表里用因为一列里塞四个数字表格宽度撑不住而且最小值最大值本质上是极端观测对描述总体 центральная趋势帮助有限。真要放放在附录表里更合适。contDigits控制连续变量的小数位数默认 2。这个默认值对大多数指标是合适的但有两类情况要调一是本身就是整数的变量比如住院天数、发作次数印成7.00 (3.00, 12.00)很别扭设成 0 更清爽二是精密度很高的实验室指标可能需要 3 位。同一个表里不同变量想要不同位数contDigits做不到它只能全局设一个值。变通办法是用formatOptions配合后期手工调整或者干脆导出成数据框再改。还有一个很实用的参数是varLabels用来给变量加可读标签。原始数据里的列名往往是sbp、hba1c这种缩写直接印在表里读者看不懂。用命名向量把标签挂上输出的表就自带中文或英文全称省得后期在 Word 里一个个改。varLabels - c( age Age, years, bmi Body mass index, kg/m2, sbp Systolic blood pressure, mmHg, los Length of stay, days ) print(tab, nonnormal c(los), contDigits 2, varLabels varLabels, minMax FALSE)los这种以天为单位的变量如果设contDigits 0中位数会印成7 [4, 12]读起来干净很多。但注意这会同时影响所有连续变量所以要么全表统一 0要么这一列单独处理。这类细节看着小但一张表几十行位数不统一会显得很不专业。2.4 检验链条oneway.test 与 kruskal.test 是怎么被选中的组间比较的检验不是随便挑的tableone 有一套默认映射搞清楚这套映射才能在看 p 值的时候心里有底。连续变量判为正态走oneway.test参数是var.equal FALSE。两组的时候这就是 Welch t 检验不假设方差齐性。这一点和很多人的预期不一样——很多人以为默认是 Student t 检验实际上是 Welch。两者在方差接近时结果几乎一致方差悬殊时 Welch 更保守自由度是小数。方法学里如果要写得写Welch t 检验或者不假设方差齐性的 t 检验写成t 检验严格说不准确。有些期刊会追问这个细节提前写清楚省事。连续变量判为非正态走kruskal.test。两组是 Wilcoxon 秩和多组是 Kruskal-Wallis。这个没什么争议。分类变量走chisq.test参数correct TRUE。这是 Pearson 卡方加 Yates 连续性校正。R 里correct参数只对 2×2 表生效三分类及以上的表会自动忽略所以你会看到有些变量的 p 值和手算的完全一致有些差一点原因就在这里。如果审稿人拿统计软件复核2×2 表那一行的 p 值对不上百分之八九是连续性校正的锅。场景默认检验关键参数常见困惑正态连续、两组Welch toneway.testvar.equal FALSE与 Student t 结果略有差异正态连续、多组oneway.testvar.equal FALSE不是方差分析非正态连续、任意组数Kruskal-Wallis无两组时等同 Wilcoxon无序分类、2×2卡方 Yates 校正correct TRUE与未校正卡方有差异无序分类、r×c卡方无校正无期望频数过小时不可靠稀疏表Fisher 精确检验exact需逐变量指定需要强调的是p 值本身不该是基线表的重点。现在越来越多的期刊要求基线表报告标准化均数差SMD因为它不受样本量影响能真实反映组间差异的大小。tableone 里加smd TRUE就会多出一列 SMD 列连续变量和分类变量都支持。对于随机对照研究通常只报 SMD 不报 p 值因为 p 值在随机化之后本来就该是大的报出来反而误导。观察性研究里两者都报也常见但要在方法学里说清楚 SMD 的计算口径。3. 分类变量的展开逻辑n (%) 只是表象水平与检验才是重点3.1 showAllLevels 决定表格长度因子水平顺序决定显示哪一行分类变量的输出默认是每个水平一行格式是n (%)百分比的分母是该组的有效样本数不含缺失。showAllLevels TRUE是默认值也就是说一个四分类变量会占四行。表格一长问题就来了。假设你有 30 个变量其中 10 个是分类变量平均 3 个水平光分类变量就 30 行加上连续变量和表头一张表 50 行起步投出去编辑部第一反应就是能不能压缩成 supplementary。showAllLevels FALSE会把每个分类变量压缩成一行只保留一个水平。保留哪个水平由因子水平的顺序决定默认是第一个水平。这就引出一个非常关键的操作因子水平的顺序要自己定不能靠默认的字母序。比如性别编码成female/male默认按字母序female在前表格里那行就写female你要是想让它显示male就得relevel一下。再比如一个分级变量I/II/III/IV默认按字母序排成I、II、III、IV看着没问题但如果你编码成1、2、3、4再转因子字符序还是对的一旦编码成A、B、C、D以外的形式比如low、mid、high字母序就乱了。# 显式设定水平顺序避免默认字母序带来的意外 df$sex - factor(df$sex, levels c(male, female)) df$stage - factor(df$stage, levels c(I, II, III, IV), ordered TRUE)压缩成一行之后读者能不能还原出另一水平的信息能。因为同一行里有各组的 n 和百分比用组内总人数减去这一水平的 n就能算出另一水平。二分类变量这样处理完全没问题三分类及以上就不行了因为丢掉了两个水平的信息。所以showAllLevels FALSE只适合二分类变量多分类变量还得老老实实展开。还有一个细节多分类变量的展开行里第一行往往不写变量名下面几行留空靠缩进对齐。这是排版惯例导出到 Word 之后要检查缩进有没有丢。用print(tab, printToggle FALSE)拿到矩阵再转数据框缩进是靠行名里的空格实现的转成 CSV 再导入 Word 时这些空格可能被吃掉需要重新排版。3.2 exact 参数与稀疏单元格什么时候非用 Fisher 不可卡方检验有个前提期望频数不能太小。经验法则是所有单元格的期望频数不小于 5或者期望频数小于 5 的单元格不超过 20%。不满足这个条件卡方近似就不可靠p 值会偏小容易假阳性。tableone 不会自动帮你检查这个前提它只会在chisq.test内部发现期望频数过小时给一条警告然后照算不误。警告信息在控制台一闪而过很多人根本没注意。所以出表之后要专门看一眼有没有警告有的话就得改。exact参数就是干这个的。写法有两种exact TRUE表示所有分类变量都用 Fisher 精确检验exact c(stage, grade)表示只对这些变量用。前者不推荐因为 Fisher 精确检验在行列数多、样本量大的表上计算量爆炸慢到能把你卡在那里。# 只对稀疏的那几个变量用 Fisher print(tab, exact c(stage, complication), showAllLevels TRUE)什么样的表算稀疏举几个典型罕见并发症的发生率比如 3/120 对 1/118某些亚型的分布一个格子只有 2 例小样本研究里的多分类变量。这几类在临床研究里太常见了尤其是安全性分析不良反应的发生率本来就低。实际操作中的麻烦在于你很难预判哪个变量稀疏得先跑一遍看警告。可行的流程是第一次跑不加exact把所有警告记下来第二次跑再针对性指定。或者更稳妥一点写个小函数遍历所有分类变量算一遍期望频数把最小值小于 5 的变量名挑出来直接塞进exact。# 快速筛查哪些分类变量存在稀疏单元格 sparse_vars - c(stage, complication, grade) for (v in sparse_vars) { tb - table(df[[v]], df$group) expected - suppressWarnings(chisq.test(tb)$expected) if (any(expected 5)) { cat(v, 的最小期望频数:, round(min(expected), 2), \n) } }还有一个坑Fisher 精确检验在 2×2 表上R 报的是双侧 p 值但在 r×c 表上算法是基于超几何分布的精确枚举计算时间和表的维度呈指数关系。超过 3×4 的表加上样本量上千可能要跑几分钟。所以exact TRUE全局打开是自找麻烦。最后说一个表述问题。用了 Fisher 之后方法学里要写明对于期望频数小于 5 的分类变量采用 Fisher 精确检验而且要说明这个判断是基于数据观察后决定的不是预先设定的。有些严格的审稿人会追问这一点认为事后选择检验方法有偏倚风险。折中的做法是一开始就在统计分析计划里写清楚阈值规则出表时严格按照规则执行。3.3 categorical 参数把数值变量拉回分类阵营的三种场景前面说过变量类型由数据框里的列类型决定。但现实里经常遇到形式上连续、实质上是分类的变量这时候就要用categorical参数强制改判。第一种场景是编码型变量。性别存成 1/2吸烟状态存成 0/1/2心功能分级存成 1/2/3/4。这类变量如果直接进表会得到性别 1.48 ± 0.50这种没法看的结果。解决办法有两个一是在数据准备阶段就转成因子用factorVars指定二是在print阶段用categorical指定。我更推荐前者因为因子化之后整个 R 环境里的建模、绘图都会按分类处理不只是出表这一处。categorical更像是一个补丁适合临时救场。第二种场景是分组化的连续变量。年龄本来是连续的但你想按60/60两档来看于是新建了一列age_cat存成 1/2。这一列如果不指定会按连续变量印出1.35 ± 0.48。用categorical age_cat就能正确显示。不过要注意分组化的连续变量在建模时是有代价的会损失信息出表可以建模要谨慎。第三种场景是评分型变量。某些量表得分只有 0 到 5 这几个整数取值按连续变量算均数在数学上没错但解释起来很别扭——平均得分 2.37 分不如得分 ≥3 者占 42%直观。这种变量到底算连续还是分类是个判断问题没有标准答案。我的习惯是看取值个数取值少于 6 个、且每个取值都有实际临床意义比如疼痛评分 0-10 但只在 0、2、4、6、8 这几个值上有分布按分类处理取值多且接近连续按连续处理。# CreateTableOne 建表阶段用 factorVars推荐 tab - CreateTableOne( vars c(age, sex, smoke, stage, sbp), strata group, data df, factorVars c(sex, smoke, stage) ) # print 阶段用 categorical 打补丁 print(tab, categorical c(smoke))两种方式的区别在于factorVars影响的是建表时的内部处理而categorical只在打印时改变呈现。如果变量同时出现在连续变量摘要和分类变量摘要里行为会不一致。建议统一在建表阶段解决categorical只用来处理偶尔漏掉的变量。3.4 有序分类变量的第三条路转数值 nonnormal有序分类变量是基线表里最麻烦的一类。典型例子是疾病分期I/II/III/IV、NYHA 心功能分级1/2/3/4、Likert 量表很不同意到很同意。它们既不是真正的连续变量级别之间的距离不一定相等也不是纯粹的无序分类变量级别之间有顺序。按无序分类处理会展开成四行占篇幅而且卡方检验完全没有利用顺序信息检验效能低。按连续变量处理算均数在数学上是可以的但平均分期 2.3 期这种表述在临床上是没有意义的审稿人也会质疑。实际研究里最常见的折中方案是三条路第一条仍然按无序分类处理展开所有水平用卡方检验。最保守什么都不丢就是占地方。小样本研究或者分期是主要暴露变量时我倾向这条。第二条转成数值后用中位数和四分位数间距描述组间比较用 Kruskal-Wallis。这正是nonnormal参数的用武之地。做法是先as.numeric()把因子转成数字然后用nonnormal指定它输出形式就变成了median [P25, P75]检验也自动变成秩和检验。# 有序因子转数值再按非正态连续变量处理 df$stage_num - as.numeric(df$stage) tab - CreateTableOne( vars c(age, stage_num), strata group, data df ) print(tab, nonnormal c(stage_num))第三条转成二分类比如 IIIIV 对 III按二分类变量处理。适合临床上本来就有明确切点的场景比如晚期对早期。损失信息最多但解释最清楚。第二和第三条路都要在表注里说明处理方式不能悄悄做。我见过一份稿子把分期转成数值算了中位数表注里一个字没提审稿人直接问分期为什么会有小数。这一点务必注意。注意把有序因子转成as.numeric()得到的是因子内部的整数编码1、2、3……不是标签里的数字。如果你的水平是I/II/III/IV转出来就是 1/2/3/4正好对应但如果水平是低/中/高转出来也是 1/2/3虽然数值本身没意义但排序关系保住了秩和检验仍然有效。要点是转之前必须确认levels的顺序正确顺序错了整个检验就错了。4. 两类变量的灰色地带误判、缺失与格式化4.1 变量被自动判错时的排查顺序表出来之后发现某个变量的形式不对比如明明是分类变量却印了均数或者明明是连续变量却印了百分比排查是有固定顺序的。第一步查列类型。str(df)或者sapply(df, class)一秒钟就能看出来。数值型进了分类分支说明你在factorVars里误加了它因子型进了连续分支说明它本该转数值但你没转或者转的时候用了as.numeric(as.character(x))之外的方式导致编码错乱。第二步查因子水平。一个连续变量被当成分类很可能是它其实是个因子而你没注意。反过来一个分类变量印成了均数也可能是它被存成了数值。第三步查nonnormal有没有写错变量名。注意是写错不是漏写。写错名字不会报错只会静默失效你以为指定了非正态其实没有。这种错误最难查因为脚本跑得通、表也出得来就是结果不对。解决办法是在脚本里把变量名定义成常量别在两处手打。第四步查分组变量的水平数。table(df$group)看一眼如果有意外的空组或者 NA 组检验结果会异常甚至报错。现象最可能的原因检查位置分类变量印出均数存成了数值型未进factorVarsstr(df)连续变量印出 n (%)存成了因子或误加进factorVarsstr(df)、factorVarsnonnormal指定无效变量名拼写不一致打印变量名逐字比对p 值为 NA某组样本量为 0 或全缺失table(df$group, useNA ifany)百分比合计不等于 100缺失值被排除在分母外missing TRUE检查缺失4.2 缺失值在表里的位置includeNA 与 missing基线表里的缺失值怎么处理是个容易被忽略但审稿人很爱问的问题。默认情况下连续变量的均数和中位数按有效样本算剔除缺失分类变量的百分比按该组的有效样本算。表里不会体现缺失了多少读者以为数据是完整的。print里加missing TRUE会给每个变量多出一列显示缺失个数。这个功能建议在探索阶段一直开着出正式表的时候再决定要不要保留。如果某个变量缺失率超过 10%通常需要在正文里专门说明缺失机制和处理方式光在表里显示个数是不够的。CreateTableOne的includeNA参数是另一个思路把缺失当成一个独立的分类水平。设成TRUE之后分类变量的缺失会变成一行 NA参与卡方检验。这个做法在缺失本身有信息的时候是有意义的比如未检测代表某种临床决策但多数情况下不建议因为把缺失当水平会稀释组间差异而且方法学上不好解释。# 探索阶段显示缺失个数 print(tab, missing TRUE, showAllLevels TRUE) # 正式表不显示缺失但在正文交代缺失率 print(tab, missing FALSE)连续变量的缺失还有个隐藏问题如果缺失比例高均数和中位数的稳定性都会下降此时更稳妥的做法是报告有效样本数。tableone 的输出里默认不显示每个变量的 n需要靠print的test和missing配合才能间接推出来。真在意这个可以用print(tab, printToggle FALSE)拿到矩阵后自己加一列 n。4.3 位数、千分位与变量标签的规范化一张专业的基线表位数是要统一的。经验规则是均数比原始数据多保留一位小数标准差比均数多保留一位百分比保留一位小数p 值保留三位小数小于 0.001 时写0.001。tableone 的默认值基本符合这个规则contDigits 2、catDigits 1、pDigits 3。但默认值不是万能的。年龄通常保留整数或一位小数血压保留整数体重指数保留一位实验室指标保留一到两位。一张表里混着这些很难用一个全局参数搞定。实际做法有两类一是接受全局设置后期在 Word 里手工微调二是导出成数据框后用格式化函数批量处理。formatOptions参数主要用来处理大数字。样本量上万的研究里计数会变成五六位数加上千分位分隔符读起来舒服很多。print(tab, contDigits 1, catDigits 1, pDigits 3, formatOptions list(big.mark ,), varLabels varLabels, explain TRUE)explain TRUE会在表的下方自动生成一段缩写说明解释表里出现的 SD、IQR、n、% 分别代表什么。这个功能省事但生成的英文说明未必符合目标期刊的格式要求多数情况下还是得自己重写表注。我一般开着它当参考最终表注手写。变量标签的规范化值得单独投入时间。所有变量名统一成英文全称加单位单位用方括号或者逗号隔开全文保持一致。中文期刊的表变量名用中文单位放括号里。这个工作看着琐碎但一张表的专业度很大程度上就体现在这些地方。5. 把参数串起来一份可直接套用的基线表生成流程5.1 模拟数据与变量清单下面用一份完全模拟的数据把整套流程走一遍。数据里故意埋了几个典型的坑性别存成数值、分期是有序因子、住院天数是明显的右偏分布、并发症是稀疏的二分类变量。set.seed(20240501) n - 240 df - data.frame( group factor(sample(c(A, B), n, replace TRUE)), age round(rnorm(n, 58, 11)), sex_code sample(c(1, 2), n, replace TRUE), # 数值编码的性别 bmi round(rnorm(n, 24.3, 3.1), 1), sbp round(rnorm(n, 132, 16)), stage factor(sample(c(I, II, III, IV), n, replace TRUE, prob c(.3, .3, .25, .15)), levels c(I, II, III, IV), ordered TRUE), los rpois(n, 7) rpois(n, 2) * 3, # 右偏 cost round(rlnorm(n, 9.2, 0.6)), # 强右偏 complication rbinom(n, 1, 0.04) # 稀疏 ) # 关键一步显式设定因子水平和标签 df$sex - factor(df$sex_code, levels c(1, 2), labels c(Male, Female)) df$complication - factor(df$complication, levels c(0, 1), labels c(No, Yes)) df$stage_num - as.numeric(df$stage) # 有序变量转数值供 nonnormal 使用 str(df)跑完str之后确认age、bmi、sbp是数值sex、stage、complication是因子los、cost是数值且明显偏态sex_code是冗余列不要进表。这一步看着简单但真正做研究时进表的变量清单经常在数据准备阶段就定错了后面全盘返工。5.2 CreateTableOne 的参数组合建表阶段只做两件事指定变量清单和因子变量。正态性的判断留到打印阶段处理这样建表函数可以复用。library(tableone) vars - c(age, sex, bmi, sbp, stage_num, los, cost, complication) factorVars - c(sex, complication) tab - CreateTableOne( vars vars, strata group, data df, factorVars factorVars, addOverall TRUE, smd TRUE )几个选择的理由说明一下。addOverall TRUE加一列总体方便读者看整体分布多数期刊的表 1 都有这一列。smd TRUE加标准化均数差列现在越来越多的期刊推荐尤其是随机分组的研究。factorVars里只放sex和complication因为stage_num要按非正态连续变量处理stage本身不进表避免和stage_num重复。关于重复变量同一个变量不要在表里出现两次一份按连续、一份按分类。有人为了信息全面同时放stage和stage_num表里两行说的是一回事审稿人一定会指出来。5.3 print 阶段的三次检查与导出打印阶段的参数最多我习惯分三次调用每次只验证一件事最后再合成一次。第一次看类型和水平展开对不对print(tab, showAllLevels TRUE, missing TRUE)重点看sex有没有正确显示两行、complication是不是两行、stage_num是不是印成了中位数这时候还没有指定nonnormal它可能印成均数正常。missing TRUE那几列顺便检查缺失情况。第二次看正态判断和检验方法print(tab, nonnormal c(los, cost, stage_num), showAllLevels TRUE, exact c(complication), smd TRUE, pDigits 3, contDigits 1, catDigits 1)重点看los、cost、stage_num三行是不是显示为median [P25, P75]complication用的是不是 Fisherstage_num的 p 值是不是变了说明秩和检验生效。这一步如果出现 Chi-squared approximation may be incorrect 的警告说明还有变量没进exact回去补。第三次出正式表并导出res - print(tab, nonnormal c(los, cost, stage_num), exact c(complication), showAllLevels TRUE, smd TRUE, contDigits 1, catDigits 1, pDigits 3, varLabels varLabels, printToggle FALSE, explain FALSE) write.csv(res, table1.csv, row.names TRUE)printToggle FALSE让print只返回矩阵不打印到控制台随后write.csv就能落盘。注意矩阵的行名里带着变量名导出的 CSV 第一列是行名转 Word 时用制表符粘贴比直接贴更整齐。5.4 出表前的自检清单表做完之后我有一份固定的自查清单逐条过一遍基本不会有遗漏。检查项目的tableone 中实现变量类型是否与业务含义一致防止 1.48±0.50 这类错误str、factorVars每个连续变量的分布形态判断该用均数还是中位数自己画直方图nonnormal因子水平顺序决定showAllLevelsFALSE时显示哪一行factor(levels)、relevel稀疏单元格决定是否用 Fisher预筛期望频数exact缺失分布判断是否需要专门说明missing TRUE检验方法与方法学描述是否一致防止描述与实现脱节对照默认映射表位数与单位是否统一提升可读性contDigits、catDigits、varLabels按我自己这几年出表的经验返工最多的不是统计方法选错而是变量类型和因子水平这类看起来不起眼的基础问题。统计方法选错了p 值不对劲跑一遍敏感性分析就能发现类型和水平错了表照样能出来数字看着也正常只有懂行的人一眼才能看出别扭。所以每次出表之前花五分钟按上面这张清单走一遍比事后返工划算得多。最后再补一个小技巧把上面这套脚本封装成一个函数变量清单和参数作为参数传进去。同一个研究换一批数据、换一个分组变量改两行就能重新出表。我自己的项目里就是这么干的主表、亚组表、敏感性分析的表共用一套代码一致性有保障也省了大量重复调试的时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑