Sanitize column names for Bold and Identity with xtable in knitr - r

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:

Related

Pasting within a function used to print with kableExtra

I use kableExtra for producing several tables and I'd like to use a function instead of repeating all the code. But with my limited knowledge of R, I am not able to.
Below is a simplified example (so simple it doesn't show why I want to collapse all the code into a function). First code without any added convenience function.
library(kableExtra)
library(glue)
library(tidyverse)
data <- mtcars[1:10, ] |> select(mpg, cyl, am, carb)
# KableExtra, without added convenience function
kbl(data, booktabs = T, linesep = "", digits = 2,
caption = "Ordinary kbl()") |>
add_header_above(c(" ", "Engine" = 2 , "Other" = 2))
)
Trying to make the same, now with a function where different calls can use different arguments for caption and added headers. The caption part works fine, it's the added headers I'm struggling with.
# Call kableExtra with a function
print_kable <- function(df, caption, header) {
kbl(data, booktabs = T, linesep = "", digits = 2,
# Caption works fine
caption = caption) |>
# I'm unable to develop code that uses a string argument here
add_header_above(c(header)) # 1 col instead of 5
# add_header_above(c({header})) # Same here
# add_header_above(c({{header}})) # and here
# add_header_above(c("{header}")) # and here
# add_header_above(c("{{header}}")) # and here
# add_header_above(c(glue({header}))) # and here
# add_header_above(c(paste(header))) # and here
}
Kable should print with the code below
print_kable(mtcars, caption = "kbl() called with a function",
header = ' " ", "Engine" = 2 , "Other" = 2 ')
Here is a related question:
How to evaluate a glue expression in a nested function?
Placing the function c() in the function call rather than in the function itself works fine. Is this what you're looking for?
print_kable <- function(df, caption, header) {
kbl(data, booktabs = T, linesep = "", digits = 2,
caption = caption) |>
add_header_above(header)
}
print_kable(mtcars, caption = "kbl() called with a function",
header = c(" ", "Engine" = 2 , "Other" = 2))

Convert list object to unordered list in markdown

I have a list containing character vectors. I would like to create an unordered list in an RMarkdown document. I have tried to accomplish this by looping through the list and pasting the output in an markdown list. In knitr in print the results 'asis'. Here is a toy example.
test <- list(x = c('a', 'b', 'c'), y = c('d', 'e'))
I would like to create an unordered list like this:
- x
- a
- b
- c
- y
- d
- e
I have tried to do this using a for loop in conjunction with cat and paste0.
cols <- names(test)
for (columns in names(test)) {
cat(paste0("- ", names(test[columns]), '\n', ' ',
"- ", test[[cols[columns]]], '\n'))
}
Which outputs"
- x
-
- y
-
I would appreciate some help to get the desired unordered list I have described above.
Here's a solution where you don't need loops. List is very similar to yaml document, therefore you can convert it to yaml (modify a little bit) and cat.
test <- list(A = c("a", "b", "c"), B = c("d", "e"), C = 1:5)
cat(gsub("^!omap\n|:", "", yaml::as.yaml(test, omap = TRUE)))
Explanation:
convert list to ordered yaml using as.yaml function from yaml package.
Remove omap header using gsub.
cat result.
You can also put it in a custom function so you wouldn't flood code:
catList <- function(inputList) {
cat(gsub("^!omap\n|:", "", yaml::as.yaml(inputList, omap = TRUE)))
}
catList(test)
Try this:
---
title: "SO Answer"
author: "duckmayr"
date: "September 14, 2018"
output: html_document
---
```{r unordered_list, echo=FALSE, results='asis'}
test <- list(x = c('a', 'b', 'c'), y = c('d', 'e'))
for (name in names(test)) {
cat("-", name, '\n', paste(' -', test[[name]], '\n'))
}
```
For me, this yields:
The way you were trying it before had two issues:
You should have been subsetting by test[[columns]] rather than test[[cols[columns]]], and
Even after you fix that, you can see that paste was causing some issues for you:
for (columns in names(test)) {
cat(paste0("- ", names(test[columns]), '\n', ' ',
"- ", test[[columns]], '\n'))
}
- x
- a
- x
- b
- x
- c
- y
- d
- y
- e

merge columns every other row using Sweave/R/Latex

I am writing a conference abstract booklet using R/Sweave. I have already made the program booklet for printing that contains the id, author, title only.
Now I want to modify it to include the abstract (not for print). But abstracts are lengthy. My thought is to take the cell with the abstract info, and have it display below the row with the author info - expanded across the full width of the page.
ID--author--------title--------------------------------
abstract-----------------------------------------------
So every other row has only one column spanning the width of the entire table.
Is there a way to add multicolmn{x} to every other row?
If a solution can't be figured out, advice for how to print full abstracts in a nice way would be welcome. (Something other than "just use landscape" or "adjust column widths")
Also, it doesn't have to be PDF. I could switch to markdown/html - and make it look closer to real conference program schedules that have full abstracts on them. Again, one I figure out how to print a table where every other row has only one column that is the width of the entire table.
If you want to try - Here is a complete MWE for what I have working now. Note that it uses the R package lipsum which has to be installed via devtools/github.
\documentclass{article}
\usepackage{booktabs, multicol, array}
\usepackage[margin=0.75in]{geometry}
%%%%%%%%%%% Let tables to span entire page
\newcolumntype{L}[1]{>{\raggedright\let\newline\\\arraybackslash\hspace{0pt}}m{#1}}
<<echo=FALSE, warning=FALSE, message=FALSE>>=
# devtools::install_github("coolbutuseless/lipsum")
library(lipsum)
library(xtable)
knitr::opts_chunk$set(echo = FALSE, warning=FALSE, message=FALSE)
options(xtable.comment = FALSE)
tblalign <- "lL{0.5cm}|L{4cm}L{6cm}L{8cm}"
# fake data setup
dat <- data.frame(ID = c(1:3), author = substr(lipsum[1:3], 1, 40),
title = substr(lipsum[4:6], 1, 100),
abstract = lipsum[7:9])
names(dat)=c("\\multicolumn{1}{c}{\\textbf{\\large{ID}}}",
"\\multicolumn{1}{c}{\\textbf{\\large{Author List}}}",
"\\multicolumn{1}{c}{\\textbf{\\large{Title}}}",
"\\multicolumn{1}{c}{\\textbf{\\large{Abstract}}}")
#
\begin{document}
<<results='asis'>>=
print(
xtable(x = dat
, align = tblalign)
, table.placement = "H"
, sanitize.colnames.function=function(x){x}
, include.rownames = FALSE
, include.colnames = TRUE
, size = "small"
, floating = FALSE
, hline.after = c(0,1:nrow(dat))
)
#
\end{document}
Split data from abstract manually
out <- dat[,-4]
ab.list <- dat$abstract
then add.to.row
, add.to.row = list(pos = as.list(1:nrow(out)),
command = paste0("\\multicolumn{3}{L{15cm}}{\\textbf{Abstract: }", ab.list, "} \\\\"))
One approach using my package huxtable. I couldn't install lipsum for some reason, so just hacked it. This is in a .Rmd file with output pdf_document.
```{r, results = 'asis'}
lipsum <- rep(do.call(paste, list(rep('blah ', 100), collapse = '')), 10)
dat <- data.frame(ID = c(1:3), author = substr(lipsum[1:3], 1, 40),
title = substr(lipsum[4:6], 1, 100),
abstract = lipsum[7:9], stringsAsFactors = FALSE)
library(huxtable)
# shape data
datmat <- matrix(NA_character_, nrow(dat) * 2, 3)
datmat[seq(1, nrow(datmat), 2), ] <- as.matrix(dat[, c('ID', 'author', 'title')])
datmat[seq(2, nrow(datmat), 2), 1] <- dat$abstract
# print as PDF
ht <- as_huxtable(datmat)
colspan(ht)[seq(2, nrow(ht), 2), 1] <- 3
wrap(ht) <- TRUE
col_width(ht) <- c(.2, .2, .6)
number_format(ht) <- 0
ht
```

How can I subscript names in a table from kable()?

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

Simple example of using tables + knitr for latex

I'm struggling with a tables package, all the examples in the packable docs are so complex and none of them works for me with knitr and latex. Could somebody help be out and display a simple table with some formulas and multiline labels in the header? 
Something like this:
df <- data.frame(matrix(1:9, nrow = 3))
colnames(df) <- c("first column", "second \\frac{1}{2}", "third column first line \\ second line")
Thank you in advance
It is possible to create multi-line headers for tables in LaTeX using the xtable package. These can be compiled from either .Rmd or .Rnw files. Building on the example by mattw and using add.to.row in the print method for xtable:
df <- data.frame(matrix(1:50, nrow = 10))
print(
xtable(df),
include.rownames = FALSE,
include.colnames = FALSE,
add.to.row = list(
pos = list(0),
command = c(
"& \\multicolumn{4}{c}{4 column header} \\\\
\\cline{2-5}
col1 & col2 & col3 & col4 & col5 \\\\ "
)
)
)
Note that add.to.row requires a list with two elements: pos and command. The first must be a list, the second a character string or vector, see ?print.xtable. pos gives the row number for the LaTeX insertion, and command is the insertion. Be a bit careful with formatting this, as it is will run directly into the next cell of the first column if you don't put in spaces or \n.
There are lots of options for customisation, allowing you to create quite complex tables with a bit of tweaking.
print(
xtable(df),
include.rownames = FALSE,
include.colnames = FALSE,
hline.after = c(-1,0),
add.to.row = list(
pos = list(0,5,10),
command = c(
"& \\multicolumn{4}{c}{4 column header} \\\\
\\cline{2-5}
col1 & col2 & col3 & col4 & col5 \\\\ ",
"\\hline \\multicolumn{5}{l}{Separator in table.} \\\\ \\hline",
"\\hline \\multicolumn{5}{l}{Notes at end of table.} \\\\ "
)
)
)
In this example I change the default settings for where xtable puts \hline, allowing me to add the last \hline above the notes - useful for explaining superscripts in the table.
Note also the use of \cline{2-5} giving me a line over columns 2 - 5.
See gist for fully reproducible example.
I don't think that this is possible with RMarkdown if you want the table to be in LaTeX style. However, you can easily do this with the xtable package when you write your code in an .Rnw file:
\documentclass{article}
\begin{document}
<<>>=
library("xtable")
df <- data.frame(matrix(1:9, nrow = 3))
colnames(df) <- c("first column", "second $\\frac{1}{2}$",
"third column")
#
<<xtable, results = "asis">>=
print(xtable(df), floating = TRUE,
sanitize.colnames.function = identity, type = "latex")
#
\end{document}

Resources