web-dev-qa-db-fra.com

possible de créer des latex multicolonnes dans xtable?

J'utilise xtable avec R Markdown et knitr pour produire des fichiers .tex que j'appelle avec\input {}. Fonctionne très bien, mais je n’ai pas trouvé comment créer des colonnes multiples comme celle montrée ici . Est-ce que quelqu'un sait comment faire cela?

Jusqu'à présent, j'utilise:

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

Je voudrais avoir un en-tête de regroupement différent sur chaque "Moyenne" et "Dev std" ("Traitement" et "Contrôle").

Sinon, existe-t-il une meilleure méthode pour utiliser R Markdown/knitr afin de générer automatiquement des tableaux? Je ne souhaite pas modifier manuellement les tables car le rapport doit être généré automatiquement.

UPDATE: @agstudy: Je suis nouveau au latex, mais je pense que c'est le résultat que je cherche à produire automatiquement avec xtable (ou quelque chose comme 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}

UPDATE 2: @Jonathan: il m'a fallu quelques lectures pour comprendre ce que vous proposiez. J'ai pris votre recommandation et cela a fonctionné.

Dans la partie R markdown, j'utilise maintenant:

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

Ensuite, dans le texte, j'utilise:

\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}

Je verrai si quelqu'un a d'autres suggestions pour une solution native xtable (ou un autre package). Sinon, j'accepterai votre réponse. Merci!

29
Eric Green

En supposant que la forme de la table soit la même d’une exécution à l’autre (c’est-à-dire que seuls les nombres changent), ma suggestion serait d’utiliser l’argument only.contents pour print.xtable et de coder manuellement les en-têtes multi-colonnes. À ma connaissance, xtable n'est pas capable de créer des cellules de plusieurs colonnes lui-même.

17

Je pense que l'option add.to.row dans xtable y parvient parfaitement.

Exemple de code ici:

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)
26
AdamO

Pensez à utiliser les tables package.

7
MYaseen208

C'est un jeu d'enfant avec le paquet kableExtra .

\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}

 enter image description here

2
Stéphane Laurent

D'habitude je fais quelque chose comme ça: 

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

Faites attention aux nombreux \\\\ nécessaires. Les barres obliques inverses sont perdues dans les commandes sub et paste.

1
dmontaner

Un peu tard dans le jeu, voici ma réponse, qui est similaire à ashkan, mais plus générale et permet différents paramètres.

Tout d'abord, pourquoi une nouvelle réponse? Eh bien, il me fallait une sortie sans environnement table (je veux écrire mes légendes, etc. dans mon document tex pas dans mon code-r) que kableExtra ne semble pas fournir (corrigez-moi si je me trompe). .] Mais je voulais aussi de la flexibilité avec les entrées (c'est-à-dire avec et sans ligne, des portées différentes, etc.).

Le résultat est une fonction construct_header() qui construit l'en-tête pour nous.

D'abord un petit exemple:

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}

Notez que nous devons spécifier hline.after = FALSE (important pour moi, mais omis ici est la possibilité de spécifier floating = FALSE).

Ce qui aboutit à ce tableau (notez que cette approche nécessite le chargement du package booktabs dans LaTeX):

 table

Vous pouvez spécifier d’omettre les lignes construct_header(..., draw_line = FALSE), d’aligner les groupes et de les étendre de différentes manières, c.-à-d.

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)

qui se traduit par ceci:  enter image description here

Le code de la fonction est la suivante:

#' 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)
}
0
David