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}
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
Traceback() for Interactive and Non-Interactive R Sessions
How to Plot a Classification Graph of a Svm in R
Non-Linear Color Distribution Over the Range of Values in a Geom_Raster
Search Within a String That Does Not Contain a Pattern
Roll Your Own Linked List/Tree in R
Installing a Package Offline from Github
Why Can't I Get a P-Value Smaller Than 2.2E-16
Ggplot2: Multiple Plots with Different Variables in a Single Row, Single Grouping Legend
Add Download Buttons in Dt::Renderdatatable
Most Mature Sparse Matrix Package for R
Raster Package Taking All Hard Drive
Function to Extract Domain Name from Url in R
What Is the Correct/Standard Way to Check If Difference Is Smaller Than MAChine Precision
Obtaining Separate Summary Statistics by Categorical Variable with Stargazer Package
R: How Does a Foreach Loop Find a Function That Should Be Invoked