【问题标题】:possible to create latex multicolumns in xtable?可以在 xtable 中创建乳胶多列吗?
【发布时间】:2026-01-25 22:55:01
【问题描述】:

我正在使用带有 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")

我想在每个“平均值”和“标准差”(“治疗”和“控制”)上使用不同的分组标题。

或者,有没有更好的方法来使用 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 块中:

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(或其他包)解决方案有任何其他建议。否则,我会接受你的回答。谢谢!

【问题讨论】:

标签: r latex knitr xtable r-markdown


【解决方案1】:

我认为 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. pos 变量是列表中的列表,而不是标量或向量(我错过了),以及 2. pos 变量可以设置为 0(将添加的行放在标题下方)甚至 -1(将其放在标题上方,这是我想要的位置)
  • 这应该是公认的答案! Here 是使用 add.to.row 参数和 \multirow 的另一个示例。
  • 请参阅小插图中标题为“使用 add.to.row 参数”的部分:cran.r-project.org/web/packages/xtable/vignettes/…
【解决方案2】:

假设表格的形式在运行中是相同的(即,只有数字在变化),我的建议是使用 print.xtableonly.contents 参数并手动编写多列标题。据我所知,xtable 本身无法处理多列单元格。

【讨论】:

    【解决方案3】:

    考虑使用tables 包。

    【讨论】:

    • 例如,如果您的数据是一个名为dat 的data.frame,它具有三列:Group,一个具有“控制”和“治疗”级别的因子; var,具有“var1”、“var2”(等)级别的因子;和value,它是数字,您可以执行以下操作:latex(tabular(var~Heading()*Group*((mean+sd)*Heading()*value), data=dat) 以获得您想要的表格类型。 ?tables::tabular 中的示例有助于解释语法。
    【解决方案4】:

    这是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}
    

    【讨论】:

      【解决方案5】:

      通常我会做这样的事情:

      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")
      

      注意需要的许多\\\\subpaste 命令中的反斜杠丢失了。

      【讨论】:

      • 请注意,R 4.0 的原始字符常量有助于减少反斜杠。
      【解决方案6】:

      我的回答有点晚了,这与 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)
      }
      

      【讨论】:

      • 我喜欢你的解决方案,但我认为对于 '\cmidrule',我们需要添加使用 \usepackage{booktabs},不是吗?
      • 没错,谢谢指出。我在答案中添加了信息。
      最近更新 更多