我正在使用带有R Markdown和knitr的xtable来生成我用\ input {}调用的.tex文件。效果很好,但我还没想出如何创建像here那样的多列。有谁知道怎么做到这个?
到目前为止,我正在使用:
tbl <- xtable(data, align="l r r r r r")
colnames(tbl) <- c("Variable",
"Mean", "Std Dev",
"Mean", "Std Dev",
"Difference")
caption(tbl) <- c("Table Title")
print(tbl,
include.rownames=FALSE,
caption.placement="top",
booktabs=TRUE,
type="latex",
file="output.tex")
我想在每个“Mean”和“Std Dev”(“治疗”和“控制”)上设置不同的分组标题。
或者,是否有更好的方法可以使用R Markdown / knitr自动生成表格?我不想手动编辑表格,因为报告需要自动生成。
更新 @agstudy:我是乳胶新手,但我认为这是我希望用xtable(或类似xtable)自动生成的输出:
\begin{tabular}{lrrrrr}
\toprule
& \multicolumn{2}{c}{Treatment} & \multicolumn{2}{c}{Control} & \\
\cmidrule(lr){2-3} \cmidrule(lr){4-5}
Variable & Mean & Std Dev & Mean & Std Dev & Difference \\
\midrule
var1 & 1 & 2 & 3 & 4 & 5 \\
\bottomrule
\end{tabular}
更新2: @Jonathan:我花了几些时间来理解你的建议。我接受了你的推荐,但它确实有效。
在R markdown chunk中我现在使用:
tbl <- xtable(data)
print(tbl,
only.contents=TRUE,
include.rownames=FALSE,
type="latex",
digits(tbl) <- c(0,1,1,1,1,1),
file="output/tblout.tex")
然后在文中,我使用:
\begin{tabular}{lddddd}
\toprule
& \multicolumn{2}{c}{Treatment} & \multicolumn{2}{c}{Control} & \\
\cmidrule(lr){2-3} \cmidrule(lr){4-5}
Variable & \multicolumn{1}{r}{Mean} & \multicolumn{1}{r}{Std Dev} & \multicolumn{1}{r}{Mean} & \multicolumn{1}{r}{Std Dev} & \multicolumn{1}{r}{Difference} \\
\midrule
\input{../output/tblout}
\bottomrule
\end{tabular}
我会看到是否有人对本机xtable(或其他包)解决方案有任何其他建议。否则,我会接受你的回答。谢谢!
答案 0 :(得分:26)
我认为xtable中的add.to.row
选项可以完美地实现这一目标。
此处的示例代码:
require(xtable)
age <- sample(c('30-50', '50-70', '70+'), 200, replace=T)
sex <- sample(c('Male', 'Female'), 200, replace=T)
val <- table(age, sex)
val <- rbind(val, formatC(prop.table(val)*100, format='f', digits=1))
val <- structure(val, dim=c(3, 4))
val <- rbind(c('n', '%'), val)
rownames(val) <- c('', sort(unique(age)))
val <- xtable(val)
addtorow <- list()
addtorow$pos <- list(0)
addtorow$command <- paste0(paste0('& \\multicolumn{2}{c}{', sort(unique(sex)), '}', collapse=''), '\\\\')
print(val, add.to.row=addtorow, include.colnames=F)
答案 1 :(得分:17)
假设表格的形式在运行中是相同的(即,只有数字正在改变),我的建议是使用only.contents
参数print.xtable
并编码多列标题手工。据我所知,xtable
无法自行完成多列单元格。
答案 2 :(得分:7)
考虑使用tables包。
答案 3 :(得分:2)
这是一款带有kableExtra
package的儿童游戏。
\documentclass{article}
\usepackage{booktabs}
\begin{document}
<<setup, include=FALSE>>=
library(knitr)
opts_chunk$set(echo=FALSE)
library(kableExtra)
options(knitr.table.format = "latex")
mx <- matrix(1:6, ncol=3)
rownames(mx) <- LETTERS[1:NROW(mx)]
colnames(mx) <- sprintf("Col %s", LETTERS[1:NCOL(mx)])
@
<<results='asis'>>=
kable(mx, booktabs = TRUE, caption = "My table", align = "c") %>%
add_header_above(c(" ", "First"=2, "Second"=1)) %>%
kable_styling(latex_options = "hold_position")
@
<<results='asis'>>=
kable(mx, booktabs = TRUE, caption = "My other table", align = "c") %>%
add_header_above(c(" ", "First"=2, "Second"=1)) %>%
kable_styling(latex_options = "hold_position") %>%
group_rows("Nice!", 1, 2)
@
\end{document}
答案 4 :(得分:1)
通常我会做这样的事情:
tableLines <- print (xtable (mymatrix)) ## no file
multicolumns <- "& \\\\multicolumn{3}{c}{A} & \\\\multicolumn{3}{c}{B} \\\\\\\\"
tableLines <- sub ("\\\\toprule\\n", paste0 ("\\\\toprule\n", multicolumns, "\n"), tableLines) ## booktabs = TRUE
tableLines <- sub ("\\\\hline\\n", paste0 ("\\\\hline\n", multicolumns, "\n"), tableLines) ## booktabs = FALSE
writeLines (tableLines, con = "myfile")
注意所需的许多\\\\
。 sub
和paste
命令中会丢失反斜杠。
答案 5 :(得分:0)
这里的游戏有点晚了我的答案,类似于ashkan,但更一般,允许不同的参数。
首先,为什么要有新答案?好吧,我需要一个没有表格环境的输出(我想在我的tex文档中写我的标题等而不是在我的r代码中)kableExtra
似乎没有提供(如果我错了,请纠正我) 。
但我也希望输入具有灵活性(即有线和无线,不同跨距等)。
结果是一个函数construct_header()
,为我们构建标题。
首先是一个简短的例子:
library(xtable)
set.seed(123)
df <- matrix(round(rnorm(16), 2), ncol = 4)
df <- cbind(paste("Var", 1:4), df)
colnames(df) <- c("Var", rep(c("X", "Y"), 2))
df
# Var X Y X Y
# [1,] "Var 1" "-0.56" "0.13" "-0.69" "0.4"
# [2,] "Var 2" "-0.23" "1.72" "-0.45" "0.11"
# [3,] "Var 3" "1.56" "0.46" "1.22" "-0.56"
# [4,] "Var 4" "0.07" "-1.27" "0.36" "1.79"
a_header <- construct_header(
# the data.frame or matrix that should be plotted
df,
# the labels of the groups that we want to insert
grp_names = c("", "Group A", "Group B"),
# the number of columns each group spans
span = c(1, 2, 2),
# the alignment of each group, can be a single character (lcr) or a vector
align = "c"
)
print(xtable(df), add.to.row = a_header, include.rownames = F, hline.after = F)
# % latex table generated in R 3.4.2 by xtable 1.8-2 package
# % Fri Oct 27 16:39:44 2017
# \begin{table}[ht]
# \centering
# \begin{tabular}{lllll}
# \hline
# \multicolumn{1}{c}{} & \multicolumn{2}{c}{Group A} & \multicolumn{2}{c}{Group B} \\ \cmidrule(lr){2-3} \cmidrule(lr){4-5}
# Var & X & Y & X & Y \\
# \hline
# Var 1 & -0.56 & 0.13 & -0.69 & 0.4 \\
# Var 2 & -0.23 & 1.72 & -0.45 & 0.11 \\
# Var 3 & 1.56 & 0.46 & 1.22 & -0.56 \\
# Var 4 & 0.07 & -1.27 & 0.36 & 1.79 \\
# \hline
# \end{tabular}
# \end{table}
请注意,我们必须指定hline.after = FALSE
(对我来说很重要,但此处省略了指定floating = FALSE
的可能性)。
此表中的结果(请注意,此方法需要在LaTeX中加载booktabs
包):
您可以指定省略行construct_header(..., draw_line = FALSE)
,对齐组,并以不同的方式使用它们,即
ugly_header <- construct_header(df, c("One", "Two", "Three"), c(2, 1, 2), c("l", "c", "r"))
print(xtable(df), add.to.row = ugly_header, include.rownames = F, hline.after = F)
该功能的代码是:
#' Constructs a header i.e., groups for an xtable
#'
#' @param df a data.frame or matrix
#' @param grp_names the names of the groups
#' @param span where the groups span
#' @param align the alignment of the groups, defaults to center
#' @param draw_line if the group-names should be underlined
#'
#' @return a list that can be given to the \code{add.to.row} argument of the of \code{print.xtable}
#' @export
#'
#' @examples
#' library(xtable)
#' mx <- matrix(rnorm(16), ncol = 4)
#' mx <- cbind(paste("Var", 1:4), mx)
#' colnames(mx) <- c("Var", rep(c("X", "Y"), 2))
#'
#' addtorow <- construct_header(mx, c("", "Group A", "Group B"), span = c(1, 2, 2), "c")
#' print(xtable(mx), add.to.row = addtorow, include.rownames = F, hline.after = F)
construct_header <- function(df, grp_names, span, align = "c", draw_line = T) {
if (length(align) == 1) align <- rep(align, length(grp_names))
if (!all.equal(length(grp_names), length(span), length(align)))
stop("grp_names and span have to have the same length!")
if (ncol(df) < sum(span)) stop("Span has to be less or equal to the number of columns of df")
header <- mapply(function(s, a, grp) sprintf("\\multicolumn{%i}{%s}{%s}", s, a, grp),
span, align, grp_names)
header <- paste(header, collapse = " & ")
header <- paste0(header, " \\\\")
if (draw_line) {
# where do we span the lines:
min_vals <- c(1, 1 + cumsum(span)[1:(length(span) - 1)])
max_vals <- cumsum(span)
line <- ifelse(grp_names == "", "",
sprintf("\\cmidrule(lr){%i-%i}", min_vals, max_vals))
line <- paste(line[line != ""], collapse = " ")
header <- paste0(header, " ", line, "\n ")
}
addtorow <- list(pos = list(-1, -1, nrow(df)),
command = c("\\hline\n ", header, "\\hline\n "))
return(addtorow)
}