Convert list object to unordered list in markdown - r

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

Related

knitr kable formatting cell text as ordered list

I'm creating a table in a rmarkdown/html document and it's inadvertently changing cell values to an ordered list when in the format of for example (123). An example is below. Any help would be most welcomed.
---
output: html_document
---
df <-
data.frame(
col1 = c("a", "b"),
col2 = c("(123)", "(61)"),
stringsAsFactors = FALSE
)
knitr::kable(
df,
format = "html",
align = c('lr')
)
The values are recognized as markdown and pandoc thinks that (123) and (61) are list items. Try
df <- data.frame(col1 = c("a", "b"),
col2 = c("(123)", "(61)"),
stringsAsFactors = F)
df$col2 <- gsub("\\(", "&lpar;", df$col2)
df$col2 <- gsub("\\)", "&rpar;", df$col2)
to replace the parenthesis by their HTML entity code.
Note: stringsAsFactors = F is the new default in R > 4.0
Another approach is to disable the fancy_lists extension. This will apply globally to the document, so you might prefer #MartinSchmelzer's solution.
Just put this in your YAML:
output:
html_document:
md_extensions: "-fancy_lists"
After you've done this, you'll only get ordered lists with numbers (no letters or Roman numerals), and they have to be in the format 1., not (1) or 1), etc.

Loop in rmarkdown

I am relatively new to r and rmarkdown so I apologise in advance if this is a stupid question. This is a simple replication of a bigger dataset.
I have three columns in a dataframe:
df <- data.frame( c(a, b), c(c, d), c(e, NA))
names(df) <- c("X", "Y", "Z")
I want to show them in a rmarkdown file as follows:
I like a b.
This is c
This is e
This is d
I have written a function that includes
X <- 0
for (i in 1:nrow(df)) {
X[i] <- df$X[[i]] }
Y <- 0
for (i in 1:nrow(df)) {
Y[i] <- df$Y[[i]] }
X <- 0
for (i in 1:nrow(df)) {
Z[i] <- df$Z[[i]] }
And in the markdown file (the bit I'm struggling with)
I like `r X` ### This is fine
``` {r}
for (i in 1:nrow(df)) {
Y[i]
Z[i] } ### Doesn't work and I want to include text i.e. This is
```
I want to make some sort of loop so it prints the element in row 1 of column Y then Z, then the next row etc. and skip ifNA
Any help whatsoever would be majorly appreciated! :)
First, I'd give you some tips in your first loop. If you want to pass a data.frame column to a vector, you can vectorize it. I recommend you check this later. Hence, instead of:
X <- 0
for (i in 1:nrow(df)) {
X[i] <- df$X[[i]] }
try to do:
X <- vector("numeric", nrow(df)) #suggestion to create a empty numerical vector
X <- as.numeric(df$X)
Answering your main question, you can name your code chunk to keep the things organized. Use eval=FALSE if you desire only the output and not the code printed. Now, you have your vectors and can use #jason suggestion:
I like `r X`
```{r code_chunk1, eval=FALSE}
paste0("This is ", X)
paste0("This is ", Y)
paste0("This is ", paste(Z,collapse = " ")) # if you want them all in the same line
}
```
Avoid the operator, it can produce unexpected results and create problems without you noticing! Visit this.
There is no need to use loops. However, the elements of df need to be re-arranged to get printed row-wise.
The rmarkdown file below reproduces the expected result:
---
title: Loop in rmarkdown
output: html_document
---
```{r, echo=FALSE}
df <- data.frame( c("a", "b"), c("c", "d"), c("e", NA))
names(df) <- c("X", "Y", "Z")
```
I like `r df$X`
```{r, echo=FALSE, warning=FALSE}
library(magrittr) # use piping to improve readability
df[, 2:3] %>% # treat columns like a matrix
t() %>% # change from row first to column first order
as.vector() %>% # flatten into vector
na.omit() %>% # drop NAs
paste("This is", .) %>% # Prepend text
knitr::kable(col.names = NULL) # print as table
```
The output is
Note that knitr::kable(col.names = NULL) is used to create inline text, i.e., text output not wrapped in a verbatim element.
Alternatively, the chunk option results='asis' can be used:
```{r, echo=FALSE, warning=FALSE, results='asis'}
library(magrittr) # use piping to improve readability
df[, 2:3] %>% # treat columns like a matrix
t() %>% # change from row first to column first order
as.vector() %>% # flatten into vector
na.omit() %>% # drop NAs
paste("This is", ., collapse = " \n") %>% # Prepend text and collapse into one string
cat() # use cat() instead of print()
```
Note that the 2 blanks before \n are required to indicate a line break in rmarkdown.

Sanitize column names for Bold and Identity with xtable in knitr

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:

Programatically inserting headers and text from a data.frame

I am trying to make a reproducible "data dictionary" in RMarkdown to ease my job of describing the various undocumented data sets I work with. I've looked at the most related post here: Programmatically insert text, headers and lists with R markdown, but am running into problems. I have a data.frame that has the colnames from my dataset and a column of strings that describe each variable. When I knit my RMarkdown document, I get one formatted header for the first variable and the rest show up with the formatting hash marks (##) and the variable name, but not as a formatted header.
```{r, results = 'asis'}
varnames <- c("A", "B", "C")
vardesc <- c("A is this.", "B is this.", "C is this.")
df <- data.frame(varnames, vardesc)
for(i in 1:nrow(df)) {
cat("##", df$vars[i], " \n")
cat("Description: ", df$vardesc[i])
cat(" \n")
}
```
This gives me variable "A" is a formatted header only. It seems my rookie knowledge of functions could be to blame but I can't figure out what I am doing wrong.
My output is as follows (with A being formatted and the rest not formatted):
## A
Description:
A is this.
## B
Description:
B is this.
## C
Description:
C is this.
Any advice would be greatly appreciated. I'm open to other methods to do this if they exist.
Try this instead:
varnames <- c("A", "B", "C")
vardesc <- c("A is this.", "B is this.", "C is this.")
df <- data.frame(varnames, vardesc, stringsAsFactors = FALSE)
for(i in 1:nrow(df)) {
cat("##", df$varnames[i], "\n")
cat("Description:", "\n")
cat(df$vardesc[i], "\n")
cat("\n")
}
Output is:
## A
Description:
A is this.
## B
Description:
B is this.
## C
Description:
C is this.

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

Resources