R语言:三线表(1)


前言

之所以想发这个,想起来就气,上次因为数据库中病例数的变化(病例的纳入排除标准改变),导致多次统计分析,反复修改我的三线表,还有几次填错数据,还被老板骂,想起来就气,然后愤愤的把table1这个函数学习了一下,下次做三线表就很简单啦,开心。

三线表这部分我会分2-3部分写,今天写第一部分:主要是对三线表部分内容进行优化。下一次主要会讲添加P值,卡方值等列。

table1直接画出的三线表对于连续资料是将均值和中位值全部输出的,而不判定数据的正态性。
本文解决的问题:对于计量资料,分正态度分布和非正态分布,分别用mean(±sd)和median(min, max)这两种方法表示。这样我们还省去了做正态性检验的步骤

先看下效果吧。注意看年龄和正态性数据哪一行那一行在这里插入图片描述
在这里插入图片描述

如果你只是想用,直接跳到最后的代码总结部分,中间的内容可能更适合打破砂锅问到底的同志。


提示:关于table1中添加标题,表头,脚注我就不赘述,因为这部分对我们不重要。想了解的善用浏览器

一、table1

先了解一下这个函数画三线表的过程,姑且可以这样去理解。每次绘制一个交叉的格子时,如上图中红圈,函数会传入数据框中所有性别为男性的生存状态数据,然后取计算频数。下面某些过程可能会比较晦涩,欢迎大家在评论区提问或者私信。

二、简单制作

1.一般过程(使用formula形式)

其实这个table1中既可以放入formula格式(简单),也可以放入strata,labels的格式(灵活)。

代码如下(示例):

rm(list = ls())
library(table1)
library(survival)
options(stringsAsFactors = F)
a <- lung
str(a)

#设置因子。需要注意,分类变量必须设置为因子或者字符串格式,否则它就把它当作数值进行计算了,这里,性别和生存状态都是分类变量。
a$sex <- factor(a$sex,levels = c(1,2),labels = c('男','女'))
a$status <- factor(a$status,levels = c(1,2),labels = c("存活","死亡"))

#可以检验age是非正态的数据,这个p>0.1说明是正态,反之。
shapiro.test(a$age)$p.value

#为了验证能否根据数据的正态性输出不同的格式,这里新增一组正态数据。
a$normdata <- rnorm(nrow(a),1,1)

#设置缺失值,看函数是否能识别缺失数据
a$age[3] <- NA

#设置单位,这样可以在行名那儿显示单位,如图中的(years,Kg)
units(a$age) <- 'year'
units(a$wt.loss) <- 'Kg'

#设置标签,这里的标签就是行名。否则三线表行名会显示为status,age等等
label(a$status) <- "生存状态"
label(a$age) <- "年龄"
label(a$wt.loss) <- "体重减低"
label(a$sex) <- "性别"
label(a$normdata) <- "正态数据"
table1(~status+age+wt.loss+normdata|sex,data = a)

2.发现问题

运行完代码,确实出现了我们要的三线表,但发现了吗?不论我们的数据是否正态,他都会将均值(标准差)和中位数(极值)两种形式输出。这不是我们想要的。为了去删除,我们还要重新去进行正态性检验然后一行一行删除,显然这不是我们学习R语言的初衷。

3.端倪

抛开这个问题先不谈,我们先学习一下,如何改变这个三线表?
看一下帮助文档,我已经为大家探索好了,这里表格中的数据是通过render参数传递的。
在这里插入图片描述render指向一个render.default函数,render.default中包含了传递连续型变量、分类变量、空值的传递,是函数的形式。调出这两个函数的代码看一下。
在这里插入图片描述
从函数可以发现分类变量和数值变量的表现形式(红色框标记)。

这里我重写这个render函数和render.continuous.default函数就初步实现函数根据正态检验选择性输出。

代码如下(示例):

重写render.default函数
rd <- function(x, name, ...){
#这里传入的参数x和name
#举个例子,当绘制男性,age这个格子的时候(一下所有的例子都是这个),x就是:数据中性别为男性的所有年龄数据。name就是age,那么这里的a[[name]]用来提取数据a中年龄数据(男性女性都包括)
  y <- a[[name]]
#判断年龄数据中是否存在空值,如果是空值,我们要加入missing行
  m = any(is.na(y))
#根据传入的数据是否是数值分别处理
  if (is.numeric(y)) {
#判断是否正态,并将判断的结果传入my.render.cont函数中
    normt=(shapiro.test(y)$p.value>0.1)+2
    my.render.cont(x,n=normt,m=m,...)
  }else{
#因为分类变量我们不需要处理,这里直接调用默认的函数就行了
    render.default(x=x, name=name, ...)
    }
  }
  
#重写render.continuous.default函数。
my.render.cont <- function (x,n,m, ...) {
#x和上面函数中的意义一样,n是判断是否正态的结果,m是判断是否空值的结果
  a=with(stats.apply.rounding(stats.default(x, ...), ...), c("", 
                                                           `Mean (±SD)` = sprintf("%s (±%s)", MEAN, SD),
                                                           `Median (Min, Max)` = sprintf("%s (%s, %s)", MEDIAN, MIN, MAX)
                                                           ))[-n] #根据正态判断结果判定输出均值还是中位数
  if (m) {
    a <- c(a,with(stats.apply.rounding(stats.default(is.na(x), ...), ...)$Yes,
                  c(`Missing` = sprintf("%s (%s%%)", FREQ, PCT))))
                  #这里就是根据是否存在空值输出missing行
  }
  a
}

###最后的最后就是运行我们的绘制三线表函数,render参数和render.continuous参数换成我们重写的函数。
table1(~status+age+wt.loss+normdata|sex,data = a,render = rd,render.continuous=my.render.cont)

在这里插入图片描述
出图,完美,可以发现正态性的数据和非正态性的数据展现形式是不一样的。

4.代码汇总

最后的最后当然是要把代码汇总,方便你们享用啦

rm(list = ls())
library(survival)
library(survminer)
library(table1)
options(stringsAsFactors = F)
#重写函数
rd <- function(x, name, ...){
  y <- a[[name]]
  m = any(is.na(y))
  if (is.numeric(y)) {
    normt=(shapiro.test(y)$p.value>0.1)+2
    my.render.cont(x,n=normt,m=m,...)
  }else{
    render.default(x=x, name=name, ...)
    }
  }

#重写连续变量处理函数
my.render.cont <- function (x,n,m, ...) {
  
  a=with(stats.apply.rounding(stats.default(x, ...), ...), c("", 
                                                             `Mean (±SD)` = sprintf("%s (±%s)", MEAN, SD),
                                                             `Median (Min, Max)` = sprintf("%s (%s, %s)", MEDIAN, MIN, MAX)
  ))[-n]
  if (m) {
    a <- c(a,with(stats.apply.rounding(stats.default(is.na(x), ...), ...)$Yes,
                  c(`Missing` = sprintf("%s (%s%%)", FREQ, PCT))))
  }
  a
}
#########################分界线#############################################
####上面的部分是可以重复使用的不用变,以下部分需要你换成自己的数据并更改部分内容
############################################################################
a <- lung
str(a)

#设置因子、以年龄、性别、体重减低、状态为例子
a$sex <- factor(a$sex,levels = c(1,2),labels = c('男','女'))
a$status <- factor(a$status,levels = c(1,2),labels = c("存活","死亡"))
a$normdata <- rnorm(nrow(a),1,1)
a$age[3] <- NA

#设置单位
units(a$age) <- 'year'
units(a$wt.loss) <- 'Kg'

#设置标签
label(a$status) <- "生存状态"
label(a$age) <- "年龄"
label(a$wt.loss) <- "体重减低"
label(a$sex) <- "性别"
label(a$normdata) <- "正态数据"
table1(~status+age+wt.loss+normdata|sex,data = a)
table1(~status+age+wt.loss+normdata|sex,data = a,render = rd,render.continuous=my.render.cont)
##两相对比,你更喜欢哪一个呢?


总结

其实之前就看过这个函数,但是当时只是玩玩的心态,学了个一知半解,这次真的是痛定思痛想要搞定它。
预告:下一篇就是添加卡方检验和t检验的p值
本人还是初学者,如有错误,欢迎批评指正,虚心接受大家的建议。

  • 17
    点赞
  • 53
    收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
评论 10

打赏作者

一口吃两口饭

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值