R Package Xtable, How to Create a Latextable with Multiple Rows and Columns from R

R package xtable, how to create a latextable with multiple rows and columns from R

You will have to be more specific about what exactly you are trying to tabulate, but I guess tabular function from the tables package might be helpful.

Here's an example of tabulating means of a variable according to 4 binary factor variables:

mydf <- data.frame(rowFactor1 = sample(letters[1:2], 100, replace = TRUE), 
colFactor1 = sample(LETTERS[1:2], 100, replace = TRUE),
x = rnorm(100),
rowFactor2 = sample(1:2, 100, replace = TRUE),
colFactor2 = sample(1:2, 100, replace = TRUE))

tab1 <- tabular(Heading()*RowFactor(rowFactor2, spacing = 1,
levelnames = c("rowLabel1", "rowLabel2"))*
Heading()*RowFactor(rowFactor1,
levelnames = c("b1", "b2")) ~
Heading()*Factor(colFactor2,
levelnames = c("colLabel1", "colLabel2") )*
Heading()*Factor(colFactor1,
levelnames = c("a1", "a2"))*
Heading()*(x)*Heading()*(mean),
data = mydf)

which gives you something like this, but nicely formated when using the latex output

                                                                colLabel1         colLabel2        
a1 a2 a1 a2
\\nopagebreak rowLabel1 \\nopagebreak b1 -0.1450 0.2633 0.91454 0.1222
\\nopagebreak b2 -0.1499 -0.4290 -0.09706 -0.6977
\\rule{0pt}{1.7\\normalbaselineskip}rowLabel2 \\nopagebreak b1 0.6976 -0.4888 -0.68492 1.6764
\\nopagebreak b2 -0.2369 -0.1428 -0.66405 0.9469

Finally latex(tab1) gives you the latex code:

\begin{tabular}{llcccc}
\hline
& & \multicolumn{2}{c}{colLabel1} & \multicolumn{2}{c}{colLabel2} \\
& & a1 & a2 & a1 & \multicolumn{1}{c}{a2} \\
\hline
\nopagebreak rowLabel1 & \nopagebreak b1 & $-0.1450$ & $\phantom{-}0.2633$ & $\phantom{-}0.91454$ & $\phantom{-}0.1222$ \\
& \nopagebreak b2 & $-0.1499$ & $-0.4290$ & $-0.09706$ & $-0.6977$ \\
\rule{0pt}{1.7\normalbaselineskip}rowLabel2 & \nopagebreak b1 & $\phantom{-}0.6976$ & $-0.4888$ & $-0.68492$ & $\phantom{-}1.6764$ \\
& \nopagebreak b2 & $-0.2369$ & $-0.1428$ & $-0.66405$ & $\phantom{-}0.9469$ \\
\hline
\end{tabular}

possible to create latex multicolumns in xtable?

Assuming the form of the table is the same across runs (i.e., only the numbers are changing), my suggestion would be to use the only.contents argument to print.xtable and code the multi-column headers in by hand. To the best of my knowledge xtable is not capable of doing multi-column cells itself.

Problems forming a LaTeX table using xtable-package in R

Here's my approach (with inspiration from)

library(xtable)
financial <- c(1.23, 1.19)
macro <- c(1.50, 1.40)
X <- rbind(financial, macro)
colnames(X) <- c("A","B")

addtorow <- list()
addtorow$pos <- list()
addtorow$pos[[1]] <- 0
addtorow$pos[[2]] <- 0
addtorow$pos[[3]] <- 0
addtorow$pos[[4]] <- 0
addtorow$pos[[5]] <- 0
addtorow$pos[[6]] <- 0
addtorow$pos[[7]] <- 0
addtorow$command <- c('Country: & United States & \\\\\n',
'\\hline',
'Method: & Regression & \\\\\n',
'\\hline',
'Models: & RMSE Result & \\\\\n',
'\\hline',
'& A & B \\\\\n')

print(xtable(X), add.to.row = addtorow, include.colnames = FALSE )

add.to.row can be used to include custom latex code into xtable.

I did could not figure out how to place your column names & A & B after the custom header. My solution for this was to use include.colnames = FALSE and manually inserted column headers in add.to.row. (perhaps someone else has a more elegant solution?)

Result:

% latex table generated in R 4.0.5 by xtable 1.8-4 package
% Mon Jan 17 14:16:51 2022
\begin{table}[ht]
\centering
\begin{tabular}{rrr}
\hline
Country: & United States & \\
\hline Method: & Regression & \\
\hline Models: & RMSE Result & \\
\hline & A & B \\
\hline
financial & 1.23 & 1.19 \\
macro & 1.50 & 1.40 \\
\hline
\end{tabular}
\end{table}

Sample Image

combine multiple xtables in R

An xtable is essentially a data frame with a printing method that shows the latex code for reproducing the table in a latex document. It is therefore not possible to create an xtable object that prints the way you want it to.

However, it is possible to get the desired output to the console by capturing the console output and manipulating it to join multiple tables together. Here is a function that will do it:

multi_xtable <- function(...)
{
vars <- as.list(match.call(expand.dots = TRUE))[-1]
df_list <- lapply(vars, eval)
num_cols <- sapply(df_list, length)
if (!all(num_cols == num_cols[1]))
stop("All data frames must have equal number of columns")
xtables <- lapply(df_list, function(x) capture.output(xtable::xtable(x)))
if (length(xtables) == 1)
return(xtables[[1]])
header <- xtables[[1]][1:6]
tail <- xtables[[1]][length(xtables[[1]]) + (-1:0)]
xtables <- lapply(xtables, function(x) x[7:(length(x) - 2)])
xtables <- do.call("c", xtables)
cat(header, xtables, tail, sep = "\n")
}

And you can use it like this:

multi_xtable(iriss, iriss1)
#> % latex table generated in R 3.6.2 by xtable 1.8-4 package
#> % Wed Apr 29 12:51:24 2020
#> \begin{table}[ht]
#> \centering
#> \begin{tabular}{rrrrrl}
#> \hline
#> & Sepal.Length & Sepal.Width & Petal.Length & Petal.Width & Species \\
#> \hline
#> 1 & 5.10 & 3.50 & 1.40 & 0.20 & setosa \\
#> 2 & 4.90 & 3.00 & 1.40 & 0.20 & setosa \\
#> 3 & 4.70 & 3.20 & 1.30 & 0.20 & setosa \\
#> 4 & 4.60 & 3.10 & 1.50 & 0.20 & setosa \\
#> 5 & 5.00 & 3.60 & 1.40 & 0.20 & setosa \\
#> 6 & 5.40 & 3.90 & 1.70 & 0.40 & setosa \\
#> \hline
#> & Sepal.Length & Sepal.Width & Petal.Length & Petal.Width & Species \\
#> \hline
#> 145 & 6.70 & 3.30 & 5.70 & 2.50 & virginica \\
#> 146 & 6.70 & 3.00 & 5.20 & 2.30 & virginica \\
#> 147 & 6.30 & 2.50 & 5.00 & 1.90 & virginica \\
#> 148 & 6.50 & 3.00 & 5.20 & 2.00 & virginica \\
#> 149 & 6.20 & 3.40 & 5.40 & 2.30 & virginica \\
#> 150 & 5.90 & 3.00 & 5.10 & 1.80 & virginica \\
#> \hline
#> \end{tabular}
#> \end{table}

Split headings in groups over multiple rows in xtable

Change your data frame so the grouped names are of the form <group><separator><name> then use the function at the bottom of this answer.

\documentclass{article}
\usepackage{booktabs}

\begin{document}
.
<<two-row-headings, results='asis'>>=
library(xtable)
library(grattanCharts) # devtools::install_github('hughparsonage/grattanCharts')
example_df <-
data.frame(yr = 2001:2005,
Revenue__foo = 1:5,
Revenue__bar = 11:15,
Revenue__baz = 21:25,
ordinary = 1:5,
Expense__foo = 1:5,
Expense__bar = 11:15,
Expense__baz = 21:25,
Last__foo = 1:5,
Last__baz = 2:6)

print_2heading_xtable(example_df, separator = "__")
@

\end{document}

The package is exported from the grattanCharts package. But you can use the function independently:

#' Print LaTeX tables with headings over two lines
#'
#' @param .data A data frame with at least some of the column names having a \code{separator}.
#' @param separator A regular expression that splits the top and bottom rows. If the separator is not found in the names of \code{.data}, the function returns an error (saying you should probably just use \code{print.xtable()})
#' @param xtable.align Passed to \code{xtable}: Character vector of length equal to the number of columns of the resulting table, indicating the alignment of the corresponding columns.
#' @param booktabs Should the tabular environment produced use booktabs? Set to TRUE for (my) convenience. This will cause an error if \verb{\usepackage{booktabs}} has not been called in LaTeX.
#' @param heading_command A (simple) LaTeX control sequence (properly escaped) to apply to each heading names.
#' @param ... Arguments passed to \code{print.xtable}. You cannot pass \code{add.to.row}, \code{include.rownames}, or \code{include.colnames} as we make use of these options in this function.
#' @return Output intended for LaTeX. A table produced using xtable where groups of column names are put in the top row.
#' @author Hugh Parsonage
#' @examples
#' example_df <-
#' data.frame(yr = 2001:2005,
#' Revenue__foo = 1:5,
#' Revenue__bar = 11:15,
#' Revenue__baz = 21:25,
#' ordinary = 1:5,
#' Expense__foo = 1:5,
#' Expense__bar = 11:15,
#' Expense__baz = 21:25,
#' Last__foo = 1:5,
#' Last__baz = 2:6,
#' last = 101:105)
#' print_2heading_xtable(example_df, separator = "__")
#' @export

print_2heading_xtable <- function(.data,
separator = "__",
xtable.align = NULL,
booktabs = TRUE,
heading_command = "\\textbf", ...){
orig_names <- names(.data)
if (!any(grepl(separator, orig_names))){
stop("No separator found in column names, so there is no point in using this function. Make sure you have specified the right separator; otherwise, just use print.xtable().")
}

if (any(c("add.to.row", "include.colnames", "include.rownames") %in% names(list(...)))){
stop("You should not pass add.to.row, include.colnames, or include.rownames to print.xtable() via this function.")
}

split_names <- grep(separator, orig_names, value = TRUE)
split_positions <- grep(separator, orig_names, value = FALSE)

# get the names before the separator
top_headers <- gsub(paste0("^(.*)", separator, ".*$"), "\\1", split_names)
# Where in the original table is there a new top header?

orig_names_no_suffix <-
gsub(paste0("^(.*)", separator, ".*$"), paste0("\\1", separator), orig_names)

# For cmidrule{}
position_of_header_instance <-
# Need to test first column
which(orig_names_no_suffix == dplyr::lead(orig_names_no_suffix) &
(orig_names_no_suffix != dplyr::lag(orig_names_no_suffix) | is.na(dplyr::lag(orig_names_no_suffix))))

position_of_header_final <-
# Need to test final column
which((orig_names_no_suffix != dplyr::lead(orig_names_no_suffix) | is.na(dplyr::lead(orig_names_no_suffix))) &
orig_names_no_suffix == dplyr::lag(orig_names_no_suffix))

if (length(position_of_header_instance) != length(position_of_header_final)){
stop("This is a bug. Sorry. Please provide your data frame to the grattan package maintainer.")
}

double_row_column_names <-
rbind(gsub("^(.*)__(.*)$", "\\1", orig_names), gsub("^(.*)__(.*)$", "\\2", orig_names))

# factor etc in table to preserve order
top_headers_widths <-
as.data.frame(table(factor(double_row_column_names[1,], levels = unique(double_row_column_names[1,]))))

first_row <-
unique(double_row_column_names[1,])

first_row_formatted <-
paste0(heading_command, "{", first_row, "}")

top_row <- character(length(first_row))

# Could do paste0() directly but decided that it would
# avoid the point which is to add \multicolumn only to the rows that call for it.
for (ii in seq_along(first_row)){
if (first_row[ii] %in% top_headers){
top_row[ii] <- paste0("\\multicolumn{", top_headers_widths$Freq[ii], "}{c}{", first_row_formatted[ii], "}")
}
}
rm(ii)

for_latex_top_row <-
paste0(paste0(top_row, collapse = " & "), "\\\\")

if (booktabs){
# (lr) to avoid cmidrule touching adjacent groups
between_row <- paste0("\\cmidrule(lr){", position_of_header_instance, "-", position_of_header_final, "}")
} else {
between_row <- paste0("\\cline{", position_of_header_instance, "-", position_of_header_final, "}")
}
for_latex_between_row <-
paste0(paste0(between_row, collapse = ""))

for_latex_second_row <-
paste0(heading_command, "{", double_row_column_names[2,], "}")

for_latex_second_row <-
paste0(paste0(for_latex_second_row, collapse = " & "), "\\\\")

addtorow <- list()
addtorow$pos <- list(0, 0, 0)
addtorow$command <-
paste0(paste0(c(for_latex_top_row, for_latex_between_row, for_latex_second_row)), "\n")

xtable::print.xtable(xtable::xtable(.data, align = xtable.align),
type = "latex",
add.to.row = addtorow,
include.colnames = FALSE,
include.rownames = FALSE,
booktabs = booktabs,
...)

}

R to LaTeX - use xtable to produce long table with line wrapping

Will leave this open for a more elegant solution but...

The addition of this line allowed the hardcoding of column widths

library("xtable")
glossary2 <- data.frame(names=letters[1:4], definition=c("very long long long text","very long long long long long text","very long long long long long long text","very long long long text"), include=c(NA,"YES",NA,"NO"))
glossaryprint <- xtable(glossary2, label="tab:codebook", caption="glossary")

align(glossaryprint) <- "lp{2in}p{3in}p{1in}" #here is the change
print(glossaryprint, tabular.environment="longtable", floating=FALSE)

Create a table in R with header expanding on two columns using xtable or any package

The tables package has a different approach to constructing tables and has a latex.tabular method to output the relevant latex.

This will work best if your data is in long form

library(tables)

# rbind with rownames as a column
st <- rbind(
data.frame(stT1, station = 'T1', what = factor(rownames(stT1), levels = rownames(stT1)),
row.names= NULL, check.names = FALSE),
data.frame(stT2,station = 'T2',what = factor(rownames(stT2), levels = rownames(stT2)),
row.names = NULL,check.names = FALSE)
)

mytable <- tabular(Heading()*what ~ station*(`Observed-modeled` +`|observed-modeled|`)*Heading()*(identity),data=st)
mytable
## station
## T1 T2
## Observed-modeled |observed-modeled| Observed-modeled |observed-modeled|
## Min. -1.5360000 0.0001891 -2.3740 0.001259
## 1st Qu. 0.0002512 0.1633000 -1.2280 0.674700
## Median 0.3593000 0.5390000 -0.6202 1.101000
## Mean 0.8554000 1.0020000 -0.2094 1.085000
## 3rd Qu. 1.6470000 1.6470000 0.7418 1.413000
## Max. 5.5370000 5.5370000 5.0530 5.053000

The is a great deal of fine control you have over the outcomes. These are described in the vignette.

In the example above you can see that Heading() will suppress (or change) the headings for a column while identity is the function which is used to summarize each combination.

And to show that it will create the appropriate tabular latex object (with multicolumns)

latex(mytable)

\begin{tabular}{lcccc}
\hline
& \multicolumn{4}{c}{station} \\
& \multicolumn{2}{c}{T1} & \multicolumn{2}{c}{T2} \\
& Observed-modeled & |observed-modeled| & Observed-modeled & \multicolumn{1}{c}{|observed-modeled|} \\
\hline
Min. & $-1.5360000$ & $0.0001891$ & $-2.3740$ & $0.001259$ \\
1st Qu. & $\phantom{-}0.0002512$ & $0.1633000$ & $-1.2280$ & $0.674700$ \\
Median & $\phantom{-}0.3593000$ & $0.5390000$ & $-0.6202$ & $1.101000$ \\
Mean & $\phantom{-}0.8554000$ & $1.0020000$ & $-0.2094$ & $1.085000$ \\
3rd Qu. & $\phantom{-}1.6470000$ & $1.6470000$ & $\phantom{-}0.7418$ & $1.413000$ \\
Max. & $\phantom{-}5.5370000$ & $5.5370000$ & $\phantom{-}5.0530$ & $5.053000$ \\
\hline
\end{tabular}

As noted above you can remove any column header using Heading()* the column in question

eg to remove "station" from the first row (as a header for all columns)

tabular(Heading()*what ~ Heading()*station*(`Observed-modeled` +`|observed-modeled|`)*Heading()*(identity),data=st)


Related Topics



Leave a reply



Submit