- 介紹
- 例1
- 例2
- 使用縮寫代碼指定自定義renderer
- 顯示不同變量的不同統(tǒng)計信息
- 改變表的外觀 - 使用內(nèi)置的樣式
- p-values列
- 表格的轉(zhuǎn)置
介紹
在流行病學(xué)和其相關(guān)領(lǐng)域期刊上的標(biāo)準(zhǔn)做法是,用第一個表格(即Table 1)展示按暴露程度分層的研究人群的基線特征的描述性統(tǒng)計。使用R包table1去生成Table 1相當(dāng)?shù)膃asy。輸出格式為HTML,其優(yōu)點是易于復(fù)制到Word文檔中。這個包允許相當(dāng)大的靈活性來定制表的內(nèi)容和外觀,但是這需要更多的編程和CSS知識。
例1
使用boot包中的melanoma數(shù)據(jù)集來進行展示,數(shù)據(jù)集的變量定義和具體描述利用?melanoma進行查看,melanoma數(shù)據(jù)框有205行,7列。
library(table1)
library(boot)
melanoma2 <- melanoma
# Factor the basic variables that
# we're interested in
melanoma2$status <-
factor(melanoma2$status,
levels=c(2,1,3),
labels=c("Alive", # Reference
"Melanoma death",
"Non-melanoma death"))
先來個簡單的操作試試感覺:
table1(~ factor(sex) + age + factor(ulcer) + thickness | status, data=melanoma2)
注意,table1包使用了一個熟悉的公式接口,各變量之間用 + 分隔,條件符號 | 的右邊為分層變量,參數(shù)data指定使用的數(shù)據(jù)集。
變量和分類的標(biāo)簽可能不適合用來描述結(jié)果,可以給分類變量指定標(biāo)簽,給特定的連續(xù)變量指定單位。
melanoma2$sex <- factor(melanoma2$sex, levels=c(1,0), labels=c("Male", "Female"))
melanoma2$ulcer <- factor(melanoma2$ulcer, levels=c(0,1), labels=c("Absent", "Present"))
label(melanoma2$sex) <- "Sex"
label(melanoma2$age) <- "Age"
label(melanoma2$ulcer) <- "Ulceration"
label(melanoma2$thickness) <- "Thickness"
units(melanoma2$age) <- "years"
units(melanoma2$thickness) <- "mm"
table1(~ sex + age + ulcer + thickness | status, data=melanoma2, overall="Total")
這看起來好了很多,但是還可以做一些調(diào)整,如:調(diào)整Total列至最左側(cè)、兩個“Death”層(Melanoma Death和Non-melanoma Death)應(yīng)該在一個共同的標(biāo)題下分組、修改連續(xù)變量(Age和Thickness)的展示形式 Means (SD)為 Means (± SD)、不輸出默認(rèn)的Median [Min, Max] 統(tǒng)計量、有效數(shù)字的修改等。這稍微有些復(fù)雜,一般的操作可以修改示例中定義的函數(shù)來實現(xiàn)。
首先,利用list來設(shè)置我們的標(biāo)簽:
labels <- list(
variables=list(sex="Sex",
age="Age (years)",
ulcer="Ulceration",
thickness="Thickness (mm)"),
groups=list("", "", "Death"))
# Remove the word "death" from the labels, since it now appears above
levels(melanoma2$status) <- c("Alive", "Melanoma", "Non-melanoma")
然后,按照我們想要展示的順序設(shè)置層或列為data.frame的列表:
strata <- c(list(Total=melanoma2), split(melanoma2, melanoma2$status))
最后,我們可以使用自定義的renderers來定制表的內(nèi)容。renderers可以是一個函數(shù),它以一個向量作為第一個參數(shù)并返回一個(命名的)字符向量。還有一種更簡單的方法來定制表內(nèi)容,使用簡短的代碼語法而不是render函數(shù)。例如,在這里,我們?yōu)檫B續(xù)變量和分類變量指定render函數(shù),如下所示:
my.render.cont <- function(x) {
with(stats.apply.rounding(stats.default(x), digits=2), c("",
"Mean (SD)"=sprintf("%s (± %s)", MEAN, SD)))
}
my.render.cat <- function(x) {
c("", sapply(stats.default(x), function(y) with(y,
sprintf("%d (%0.0f %%)", FREQ, PCT))))
}
結(jié)果展示如下:
table1(strata, labels, groupspan=c(1, 1, 2),
render.continuous=my.render.cont, render.categorical=my.render.cat)
例2
例2中使用模擬數(shù)據(jù)。我們設(shè)想一個臨床試驗,受試者被隨機以2:1的比例接受積極治療或安慰劑。為了簡單起見,只考慮三個基本特征:年齡、性別和體重。
f <- function(x, n, ...) factor(sample(x, n, replace=T, ...), levels=x)
set.seed(427)
n <- 146
dat <- data.frame(id=1:n)
dat$treat <- f(c("Placebo", "Treated"), n, prob=c(1, 2)) # 2:1 randomization
dat$age <- sample(18:65, n, replace=TRUE)
dat$sex <- f(c("Female", "Male"), n, prob=c(.6, .4)) # 60% female
dat$wt <- round(exp(rnorm(n, log(70), 0.23)), 1)
# Add some missing data
dat$wt[sample.int(n, 5)] <- NA
label(dat$age) <- "Age"
label(dat$sex) <- "Sex"
label(dat$wt) <- "Weight"
label(dat$treat) <- "Treatment Group"
units(dat$age) <- "years"
units(dat$wt) <- "kg"
先來看看默認(rèn)設(shè)置下得到的表格長什么樣子:
table1(~ age + sex + wt | treat, data=dat)
請注意,當(dāng)包含缺失值(這里的weight變量)時,無論是連續(xù)還是分類變量,都將報告一個不同的類別Missing (帶有計數(shù)和百分比)。
“Overall” 這一列可以輕松的去掉或者重新給標(biāo)簽:
table1(~ age + sex + wt | treat, data=dat, overall=F)
我們還可以通過兩個變量進行分層,在這種情況下,它們是嵌套的。例如,將每個治療組按性別劃分:
table1(~ age + wt | treat*sex, data=dat)
或者,調(diào)換一下順序,按照性別分別劃分治療情況:
table1(~ age + wt | sex*treat, data=dat)
或者,直接不分層展示(即展示總的統(tǒng)計情況):
table1(~ treat + age + sex + wt, data=dat)
最后,我們可能會再次考慮一些更復(fù)雜的東西,使用默認(rèn)(即non-formula)接口。假設(shè)不是簡單地分配給安慰劑或積極治療,實際上有兩種隨機的治療劑量,5毫克和10毫克,我們希望單獨列出每個劑量水平,以及所有接受治療的受試者。
dat$dose <- (dat$treat != "Placebo")*sample(1:2, n, replace=T)
dat$dose <- factor(dat$dose, labels=c("Placebo", "5 mg", "10 mg"))
strata <- c(split(dat, dat$dose), list("All treated"=subset(dat, treat=="Treated")), list(Overall=dat))
labels <- list(
variables=list(age=render.varlabel(dat$age),
sex=render.varlabel(dat$sex),
wt=render.varlabel(dat$wt)),
groups=list("", "Treated", ""))
table1(strata, labels, groupspan=c(1, 3, 1))
使用縮寫代碼指定自定義renderer
假設(shè)對于連續(xù)變量,我們想要顯示百分比變異系數(shù)(CV%)而不是標(biāo)準(zhǔn)差(SD)。我們還想顯示幾何平均值和幾何變異系數(shù)。我們已經(jīng)討論了可用于完成此任務(wù)的自定義render函數(shù),但是更簡單的替代方法是使用縮寫代碼。這是一個包含某些關(guān)鍵字的字符串,這些關(guān)鍵字被替換為表輸出中的計算值。識別關(guān)鍵字列表來自于stats.default函數(shù)的輸出,包括:N, NMISS, MEAN, SD, CV, GMEAN, GCV, MEDIAN, MIN, MAX, IQR, Q1, Q2, Q3, T1, T2, FREQ, PCT。關(guān)鍵字匹配不區(qū)分大小寫,除了關(guān)鍵字之外的任何文本都保持原樣。我們可以指定一個字符串向量,在這種情況下,每個結(jié)果都將顯示在表的行中。我們可以使用一個命名向量為每一行指定標(biāo)簽,點號(‘.’)可用于指示將縮寫的代碼字符串本身用作行標(biāo)簽。有效數(shù)字可以使用digits參數(shù)(默認(rèn)值: 3)進行控制。以下是上一節(jié)示例的后續(xù),該示例生成了所需的結(jié)果:
table1(strata, labels, groupspan=c(1, 3, 1),
render.continuous=c(.="Mean (CV%)",
.="Median [Min, Max]",
"Geo. mean (Geo. CV%)"="GMEAN (GCV%)"))
顯示不同變量的不同統(tǒng)計信息
假設(shè)需要顯示年齡的中位數(shù)和極差,而顯示體重的平均值和標(biāo)準(zhǔn)差。這可以使用自定義的render函數(shù)實現(xiàn):
rndr <- function(x, name, ...) {
if (!is.numeric(x)) return(render.categorical.default(x))
what <- switch(name,
age = "Median [Min, Max]",
wt = "Mean (SD)")
parse.abbrev.render.code(c("", what))(x)
}
table1(~ age + sex + wt | treat, data=dat,
render=rndr)
注意,除了分別重寫render.continuous和render.categorical,也可以重寫 render 來同時處理這兩種情況。render函數(shù)獲取變量的名稱作為它的第二個參數(shù),并且還應(yīng)該接受...來獲取傳遞給它的任何其他參數(shù)。還要注意函數(shù)parse.abbrev.render.code可用于將縮寫代碼轉(zhuǎn)換為相應(yīng)的render函數(shù)。
改變表的外觀
table1的默認(rèn)樣式使用Arial字體,類似于LaTeX中常用的 booktabs樣式。雖然這種默認(rèn)樣式并不難看,但不可避免地會希望定制表的視覺外觀(字體、顏色、網(wǎng)格線等)。該包提供了有限數(shù)量的內(nèi)置選項來更改樣式,而進一步的定制可以在使用CSS的R R Markdown文檔中實現(xiàn)(不展示)。
使用內(nèi)置的樣式
該package包括有限數(shù)量的內(nèi)置樣式,包括:
- zebra: 交替的陰影行和非陰影行(斑馬條紋)
- grid: 顯示所有網(wǎng)格線
- shade: 將標(biāo)題行涂成灰色
- times: 使用serif字體
- center: 將所有列居中,包括第一列(行標(biāo)簽列)
可以使用table1的topclass參數(shù)選擇這些樣式。一些例子:
table1(~ age + sex + wt | treat, data=dat, topclass="Rtable1-zebra")
table1(~ age + sex + wt | treat, data=dat, topclass="Rtable1-grid")
table1(~ age + sex + wt | treat, data=dat, topclass="Rtable1-grid Rtable1-shade Rtable1-times")
注意,樣式名前面需要加上前綴Rtable1-。通過使用空格分隔多個樣式,可以組合應(yīng)用多個樣式。
p-values列
顯示與單變量檢驗相關(guān)的p值,以檢驗各變量在不同層中的差異。目前包中還沒有內(nèi)置實現(xiàn)這一點的工具,但是使用自定義render函數(shù)和空層,可以“欺騙”它來生成所需的結(jié)果。下面的示例使用來自MatchIt包的lalonde數(shù)據(jù)。在本例中,分類變量使用獨立卡方檢驗,連續(xù)變量使用t檢驗(如果需要,還可以使用其他檢驗)。
library(MatchIt)
## Warning: package 'MatchIt' was built under R version 3.5.3
data(lalonde)
lalonde$treat <- factor(lalonde$treat, levels=c(0, 1, 2), labels=c("Control", "Treatment", "P-value"))
lalonde$black <- factor(lalonde$black)
lalonde$hispan <- factor(lalonde$hispan)
lalonde$married <- factor(lalonde$married)
lalonde$nodegree <- factor(lalonde$nodegree)
lalonde$black <- as.logical(lalonde$black == 1)
lalonde$hispan <- as.logical(lalonde$hispan == 1)
lalonde$married <- as.logical(lalonde$married == 1)
lalonde$nodegree <- as.logical(lalonde$nodegree == 1)
label(lalonde$black) <- "Black"
label(lalonde$hispan) <- "Hispanic"
label(lalonde$married) <- "Married"
label(lalonde$nodegree) <- "No high school diploma"
label(lalonde$age) <- "Age"
label(lalonde$re74) <- "1974 Income"
label(lalonde$re75) <- "1975 Income"
label(lalonde$re78) <- "1978 Income"
units(lalonde$age) <- "years"
rndr <- function(x, name, ...) {
if (length(x) == 0) {
y <- lalonde[[name]]
s <- rep("", length(render.default(x=y, name=name, ...)))
if (is.numeric(y)) {
p <- t.test(y ~ lalonde$treat)$p.value
} else {
p <- chisq.test(table(y, droplevels(lalonde$treat)))$p.value
}
s[2] <- sub("<", "<", format.pval(p, digits=3, eps=0.001))
s
} else {
render.default(x=x, name=name, ...)
}
}
rndr.strat <- function(label, n, ...) {
ifelse(n==0, label, render.strat.default(label, n, ...))
}
table1(~ age + black + hispan + married + nodegree + re74 + re75 + re78 | treat,
data=lalonde, droplevels=F, render=rndr, render.strat=rndr.strat, overall=F)
誠然,這不是很優(yōu)雅,但是它仍然很好地說明了這個包的靈活性,因為它能夠完成我們從未真正計劃過的事情。
表格的轉(zhuǎn)置
默認(rèn)情況下,table1生成的表將以層或亞組為列,以變量為行。在某些情況下,可能需要對表進行轉(zhuǎn)置,使每一列都是變量,行是層。當(dāng)所有的變量都是連續(xù)的,并且需要一個緊湊的表示形式時,這是最有意義的。它可以通過使用transpose = TRUE選項來實現(xiàn)。
一個例子:
dat <- expand.grid(i=1:50, group=LETTERS[1:3])
dat <- cbind(dat, matrix(round(exp(rnorm(6*nrow(dat))), 1), nrow=nrow(dat)))
names(dat)[3:8] <- paste0("V", 1:6)
默認(rèn):
table1(~ V1 + V2 + V3 + V4 + V5 + V6 | group, data=dat,
topclass="Rtable1-grid Rtable1-center",
render="Mean (CV%)<br/>Median [Min, Max]<br/>GMean (GCV%)")
轉(zhuǎn)置:
table1(~ V1 + V2 + V3 + V4 + V5 + V6 | group, data=dat,
topclass="Rtable1-grid Rtable1-center",
render="Mean (CV%)<br/>Median [Min, Max]<br/>GMean (GCV%)",
transpose=TRUE)
參考閱讀:
https://cran.r-project.org/web/packages/table1/vignettes/table1-examples.html
https://stackoverflow.com/questions/54945344/p-value-column-in-table1-using-table1-function
https://cran.r-project.org/web/packages/table1/table1.pdf
https://cran.r-project.org/web/packages/furniture/vignettes/Table1.html