Related
Is there a way to pass xtable's identity function to sanitize the column names AND another custom function to bold the column names? There are two code chunks below, one to set up the dummy function and then another to print the xtable. It fails on the $ symbol in the first column name and the $ symbol in the table value is properly sanitized.
Thanks!
<<setup>>=
library(knitr)
library(xtable)
two_functions = function(x){
paste("\\textbf{", x, "}", sep = "")
# use xtable's 'identity' function to convert special characters
}
options(xtable.sanitize.colnames.function = two_functions)
#
<<xtable, results='asis'>>=
xtab = data.frame(a = c("Horse and $buddy", "Paddy Wagon", "Hospital Care", "Peanut butter and toast", "Cheese Whiz with Mayo"),
b = c(10000000, 200000.4533, 3098765435.65456, 408765467.654456, 50.00000))
colnames(xtab) = c("Hello money $ bag$", "Numbers")
print(xtable(xtab))
#
I think the solution maybe as simple as using gsub within the two_functions call.
\documentclass{article}
\begin{document}
<<<setup>>=
library(knitr)
library(xtable)
two_functions = function(x){
gsub("\\$", "\\\\$", paste("\\textbf{", x, "}", sep = ""))
}
options(xtable.sanitize.colnames.function = two_functions,
xtable.sanitize.rownames.function = NULL,
xtable.sanitize.text.function = NULL)
#
<<xtable, results='asis'>>=
xtab = data.frame(a = c("Horse and $buddy", "Paddy Wagon", "Hospital Care", "Peanut butter and toast", "Cheese Whiz with Mayo"),
b = c(10000000, 200000.4533, 3098765435.65456, 408765467.654456, 50.00000))
colnames(xtab) = c("Hello money $ bag$", "Numbers")
print(xtable(xtab))
#
\end{document}
Edit
To use the default xtable function for sanitizing a string replace the two_functions function above with the following:
two_functions = function(x){
paste0("\\textbf{", xtable::sanitize(x, type = "latex"), "}")
}
Here the xtable::sanitize function is called first and then the resulting stings are placed inside of the LaTeX \textbf{} environment.
The resulting table is:
Given a data.frame A, how can I use subscripted rows and columns names? Eventually I want produce a table through kable() in rmarkdown (output: word document).
A <- data.frame(round(replicate(3, runif(2)),2))
rownames(A) <- c("Hola123", "Hola234")
A
X1 X2 X3
Hola123 0.47 0.55 0.66
Hola234 0.89 0.45 0.20
How could I make all numbers from row and column names subscripted when creating a table through kable(A)?
I have tried:
rownames(A) <- c(expression(Hola["123"]), expression(Hola["234"]))
names(A) <- c(expression(X["1"]), expression(X["2"]), expression(X["3"]))
But it does not appears subscripted when creating the table through kable() in the .rmd file.
To add subscripts in a rmarkdown document, you can embed your text between two tilde: text~sub~.
When using function kable, any text in the table is recognized as markdown syntax. So that your rmarkdown code should be:
```{r}
A <- data.frame(round(replicate(3, runif(2)),2))
rownames(A) <- c("Hola~123~", "Hola~234~")
names(A) <- c("X~1~", "X~2~", "X~3~")
knitr::kable(A)
```
Just one note about bamphe's response is that the correct code is misspelled. It should be \\textsubscript{}. It is missing the second "t".
And completing the answer, you might choose to use the arguments row.names and col.names inside kable, in this way:
A <- data.frame(round(replicate(3, runif(2)),2))
rownames(A) <- c("Hola\\textsubscript{123}", "Hola\\textsubscript{234}")
knitr::kable(A,
row.names = T,
col.names = c("X\\textsubscript{1}", "X\\textsubscript{2}", "X\\textsubscript{3}"),
escape = F)
I, too, was looking for a method that would allow for subscript and superscript in both html and pdf formats in markdown tables with kable. After a bit of searching, I finally found the text reference method explained here by #yihui-xie : bookdownguide
(ref:foo) H~2~O where foo is the reference and H~2~O the text.
My code example shows how the text reference can be used. Make sure to follow the cardinal rules:
The reference needs to be unique throughout the document
The reference should not have a white space following the "to be inserted stuff"
The reference needs to be in its own paragraph and have an empty line both above and below it
Note that only the referenced "foo" and "fo" will give the subscripts while the ~[]~ method will only work in html but not pdf.
(ref:foo) CO~2~/CO~2~
(ref:fo) CO~2~
```{r chunk-to-show-the-text-reference-method, echo = FALSE }
library(dplyr)
library(knitr)
library(kableExtra)
# Make lists
dtmin_name <- c("ref/ref","refrigerant/CO2","(ref:foo)",paste0("ground/","(ref:fo)"),"ground/water","air/refrigerant","water/refrigerant","water/CO2")
temp_diff <- c( 2.3, 1.4, 0.8, 6.8, 14, 6, 4, 3.46)
# Make dataframe and column names
dtmin_df <- data.frame(dtmin_name,temp_diff, stringsAsFactors = FALSE)
colnames <- data.frame("Interface Type ", "dT~min~ Interval [K]", stringsAsFactors = FALSE)
colnames(dtmin_df) <- colnames
# Make Table
kable(dtmin_df, caption = "Typical dT~min~ Temperature Intervals", booktabs = TRUE, format.args = list(big.mark = ",")) %>%
kable_styling(bootstrap_options = c("striped", "hover"),latex_options = c("striped","scale_down"))```
Using knitr and Rstudio, I'm trying to print a dataframe to HTML or Word, so that the leading whitespaces in versicolor, will push versicolor to the right.
#data
library(knitr )
library(xtable)
df <- iris[c(1,51),c(5,1)]
df$Species <- as.character(df$Species)
df$Species[ df$Species=="versicolor"] <- " versicolor"
Trying different combinations of kable()...
#table
kable( df)
kable( df, right = FALSE,align = c("l", "l" ) )
kable( df, right = FALSE,align = c("r", "l" ) )
I get this:
...or this:
But I'm trying to get this:
If you're willing to muck with some HTML:
df$Species[ df$Species=="versicolor"] <-
"<code style='background:white'> </code>versicolor"` will get you something like you want
or
df$Species[ df$Species=="versicolor"] <-
"<span style='padding-left:30px'> versicolor</span>"
will get you left-space-padding.
The latter might even be cleaner programmatically (inserting multiples of # in padding-left.
You can try adding
df$Species <- gsub(" ", " ", df$Species, fixed=TRUE)
before creating the table, that will change all your spaces before versicolor to HTML non-breaking spaces.
Separate the leading spaces from the trailing text with one gsub. Then, globally replace those spaces with a second gsub. Lastly, combine the two parts with paste.
As a one-liner:
paste0(gsub('\\s', ' ', gsub('^(\\s*)\\S.*', '\\1', df$Species)), gsub('^\\s*(\\S.*)', '\\1', df$Species))
If df$Species <- " versicolor", the result is : " versicolor"
If df$Species <- " one two three", the result is : " one two three"
Or, reformatted for clarity:
x <- df$Species
paste0( # Combine edited text
gsub('\\s', ' ', # Replace leading spaces
gsub('^(\\s*)\\S.*', '\\1', x) # Extract leading spaces
),
gsub('^\\s*(\\S.*)', '\\1', x) # Extract trailing text
)
What about using kableExtra package? See "Row indentation" here:
https://cran.r-project.org/web/packages/kableExtra/vignettes/awesome_table_in_html.html
Basically,
library(kableExtra)
dt <- mtcars[1:5, 1:6]
kable(dt) %>%
kable_styling("striped", full_width = F) %>%
add_indent(c(1, 3, 5))
I am using knitr and xtable to automate my reporting procedure. I want to highlight a few rows of a table and have a horizontal line right above each row highlighted. The .Rnw file I am using reads as below:
\usepackage{colortbl, xcolor}
\usepackage{longtable}
\begin{document}
<<do_table, results = "asis">>=
library(xtable)
mydf <- data.frame(id = 1:10, var1 = rnorm(10), var2 = runif(10))
print(xtable(mydf), add.to.row = list(pos = list(0,2), command = rep("\\rowcolor[gray]{0.75}",2)),hline.after=c(0,2))
#
\end{document}
This works just fine, however, the table I am working with should be a longtable, if I adjust the last line of code to
print(xtable(mydf), add.to.row = list(pos = list(0,2), command = rep("\\rowcolor[gray]{0.75}",2)),hline.after=c(0,2),tabular.environment="longtable",floating=FALSE)
the output is quite ugly, and the rows are not highlighted as expected. Anyone might know an answer to this question?
thanks,
David
Sorry, slightly offtopic, but demonstrating a markdown-only solution for highlighting cells/rows easily:
> mydf <- data.frame(id = 1:10, var1 = rnorm(10), var2 = runif(10))
> library(pander)
> emphasize.strong.rows(c(1, 3))
> pander(mydf)
---------------------------
id var1 var2
----- ---------- ----------
**1** **0.7194** **0.6199**
2 0.8094 0.1392
**3** **-1.254** **0.5308**
4 0.4505 0.8235
5 -0.3779 0.7534
6 -0.3518 0.3055
7 1.759 0.5366
8 0.9822 0.9938
9 1.549 0.3589
10 -1.077 0.5153
---------------------------
That can be converted to LaTeX or pdf directly.
You are on the right track, but I am a bit confused: do you want the selected rows highlighted by hline and rowcolor? In my experience, rowcolor alone looks better, so I will assume that in my answer below (but you could easily use both, just append the \\hline command).
As a bonus, all code below assumes you use the LaTeX booktabs package, which gives correctly weighted rules (unlike hline). To be honest, I always work with booktabs, and I couldn't bother to adjust the code to use hline -- but if you prefer hline, replace all \toprule, \midrule and \bottomrule macros with \hline.
You seem to have missed that LaTeX longtables require a special header, and we need to supply that too as an element to the command vector of the add.to.row list (this may be the reason your typeset table looks bad).
longtable.xheader <-
paste("\\caption{Set your table caption.}",
"\\label{tab:setyourlabel}\\\\ ",
"\\toprule ",
attr(xtable(mydf), "names")[1],
paste(" &", attr(xtable(mydf), "names")[2:length(attr(xtable(mydf), "names"))], collapse = ""),
"\\\\\\midrule ",
"\\endfirsthead ",
paste0("\\multicolumn{", ncol(xtable(mydf)), "}{c}{{\\tablename\\ \\thetable{} -- continued from previous page}}\\\\ "),
"\\toprule ",
attr(xtable(mydf), "names")[1],
paste("&", attr(xtable(mydf), "names")[2:length(attr(xtable(mydf), "names"))], collapse = ""),
"\\\\\\midrule ",
"\\endhead ",
"\\midrule ",
paste0("\\multicolumn{", as.character(ncol(xtable(mydf))), "}{r}{{Continued on next page}}\\\\ "),
"\\bottomrule \\endfoot ",
"\\bottomrule \\endlastfoot ",
collapse = "")
With that taken care of, go ahead and print the xtable:
print(xtable(mydf),
floating = FALSE, % since longtable never floats
hline.after = NULL, % hline off since I use booktabs
add.to.row = list(pos = list(-1,
c(0, 2),
nrow(xtable(mydf))),
command = c(longtable.xheader,
"\\rowcolor[gray]{0.75}\n",
"%")), % comments out a spurious \hline by xtable
include.rownames = FALSE, % depends on your preference
include.colnames = FALSE, % depends on your preference
type = "latex",
tabular.environment = "longtable",
% xtable tries to escape TeX special chars, can be annoying sometimes
sanitize.text.function = function(x){x},
% not all dashes are meant to be math negative sign, set according to your data
math.style.negative = FALSE)
I hope my use of booktabs in the answer did not confuse you too much.
Keep knitting!
You might have more luck posting this on a latex forum. You should note that xcolor/longtable are not compatible: http://www.ukern.de/tex/xcolor.html.
I am using xtable with R Markdown and knitr to produce .tex files that I call with \input{}. Works great, but I have not figured out how to create multicolumns like the one shown here. Does anyone know how to to this?
So far I am using:
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")
I'd like to have a different grouping header over each "Mean" and "Std Dev" ("Treatment" and "Control").
Alternatively, is there a better method for using R Markdown/knitr to automatically generate tables? I don't want to manually edit the tables because the report needs to generate automatically.
UPDATE:
#agstudy: I'm new to latex, but I think this is the output I am looking to produce automatically with xtable (or something like 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: it took me a few reads to understand what you were suggesting. I took your recommendation, and it worked.
In the R markdown chunk I now use:
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")
Then in the text, I use:
\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}
I'll see if anyone has any other suggestions for a native xtable (or other package) solution. Otherwise, I will accept your answer. Thanks!
I think the add.to.row option in xtable achieves this perfectly.
Example code here:
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)
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.
Consider using the tables package.
This is a child's game with the 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}
Usually I am doing something like this:
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")
Pay attention to the many \\\\ needed. Backslashes are lost in the sub and paste commands.
A little bit late to the game here is my answer, which is similar to ashkan, but more general and allows for different parameters.
First of all, why a new answer? Well, I needed an output without a table-environment (I want to write my captions etc inside my tex-document not inside my r-code) which kableExtra does not seem to provide (correct me if I am wrong).
But I also wanted flexibility with the inputs (i.e., with and without line, different spans etc).
The result is a function construct_header() that constructs the header for us.
First a short example:
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}
Note that we have to specify hline.after = FALSE (important for me, but omitted here is the possibility to specify floating = FALSE).
Which results in this table (note that this approach needs the booktabs package to be loaded in LaTeX):
You can specify to omit the lines construct_header(..., draw_line = FALSE), align the groups, and have them span in different ways, i.e.,
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)
which results in this:
The code for the function is this:
#' 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)
}