RMarkdown: ggplot into a table - r

There are already a few questions considering ggplots in RMarkdown but none has answered my question as how to put a ggplot into a table with kable() by knitr.
I ve tried this link:
How can I embed a plot within a RMarkdown table?
But have not had any luck so far. Any ideas?
The idea was to put all plots into a list with
a<-list(p1,p2,p3...)
and then having the table with
{r}kable(a)
Additional text should also be able to be included
b<-("x","y","z",...)
kable (c(a,b),col.names=c())
Thanks for your help
Frieder

I experimented some with this and the following is the best I could come up with. This is a complete markdown document you should be able to paste into RStudio and hit the Knit button.
Two relevant notes here.
Setting the file links directly into kable doesn't work as it is wrapped in html such that it is interpreted as text, so we need to gsub() it in. An alternative is to set kable(..., escape = FALSE), but it is a risk that other text might cause problems.
Also, the chunk option results = 'asis' is necessary to have the print(kab) return raw html.
I don't know if these are problems for the real application.
---
title: "Untitled"
author: "me"
date: "02/06/2020"
output: html_document
---
```{r, results = 'asis'}
library(ggplot2)
library(svglite)
n <- length(unique(iris$Species))
data <- split(iris, iris$Species)
# Create list of plots
plots <- lapply(data, function(df) {
ggplot(df, aes(Sepal.Width, Sepal.Length)) +
geom_point()
})
# Create temporary files
tmpfiles <- replicate(n, tempfile(fileext = ".svg"))
# Save plots as files, get HTML links
links <- mapply(function(plot, file) {
# Suit exact dimensions to your needs
ggsave(file, plot, device = "svg", width = 4, height = 3)
paste0('<figure><img src="', file, '" style = "width:100%"></figure>')
}, plot = plots, file = tmpfiles)
# Table formatting
tab <- data.frame(name = names(plots), fig = paste0("dummy", LETTERS[seq_len(n)]))
kab <- knitr::kable(tab, "html")
# Substitute dummy column for figure links
for (i in seq_len(n)) {
kab <- gsub(paste0("dummy", LETTERS[i]), links[i], kab, fixed = TRUE)
}
print(kab)
```

I have found my way around it as described in the link I posted.
I. Saved my plot as a picture
II. Used sprintf() to insert picture into table with this command from Rmarkdown:
![](path/to/file)
Poor, but it works. If anybody finds a solution, I will always be interested in smart coding.

Related

Split dataset into clusters and save each cluster on a separate pdf document in R

Using the 'mtcars' dataset, how can one split the dataset into clusters using the 'Carb' field and output each grid on a separate pdf document with the Carb value being the name of the pdf document. I am new in R and the solutions I have found enable one to save each cluster on a different page of a pdf document. Have not found one where its possible to save each cluster as a separate document.
You can create pdfs for each part of dataset using approach of parameterized reports in Rmarkdown and not just creating tables, you can create a whole report for each clusters of the dataset.
So to do that, we need to first create a template rmarkdown file containing code for printing data as table where we also need to specify params in yaml of the file.
---
title: "Untitled"
author: "None"
date: '2022-07-26'
output: pdf_document
params:
carb: 1
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
## R Markdown table
```{r, echo=FALSE}
data(mtcars)
df <- mtcars[mtcars$carb %in% params$carb,]
knitr::kable(df, caption = paste("mtcars table for carb", params$carb))
```
Then from a separate R file (r script) or from console run this code which will create six pdfs for each value of carb
lapply(unique(mtcars$carb), function(carb_i) {
rmarkdown::render("tables.Rmd",
params = list(carb = carb_i),
output_file = paste0("table_for_carb",carb_i, ".pdf"))
})
So, for example, table_for_carb1.pdf looks like this
To know more how to create parameterized report with rmarkdown, see here
Here is an option with package gridExtra.
library(gridExtra)
sp <- split(mtcars, mtcars$carb)
lapply(seq_along(sp), \(i) {
carb <- names(sp)[i]
filename <- sprintf("carb%s.pdf", carb)
pdf(filename)
grid.table(sp[[i]])
dev.off()
})
To write the clusters to the same PDF file, one table per page, start by exporting the first table, then, in the lapply loop go to next page and export the next table. The new pages must be between the tables and there must already exist a page (the 1st) before starting a new one for the next table.
And since the filename doesn't depend on the number of carburetors, the code can be simplified and rewritten without the need for seq_along.
library(grid)
library(gridExtra)
sp <- split(mtcars, mtcars$carb)
pdf("carb.pdf")
grid.table(sp[[1]])
lapply(sp[-1], \(x) {
grid.newpage()
grid.table(x)
})
dev.off()

Write multiple PDF object in one file using magick in R

I am working to stick two PNGs side by side and then convert the concatenated object to a PDF using the code below. Now how can I write all the PDF objects as pages in a single file? I tried to save all the objects in a list and then pass the list to image_write(), but it did not work.
library(magick)
vec_out <- list()
for(i in 1:length(all_stims)){
stim <- all_stims[i]
img1 <- image_read(file.path(figures_folder, "across_cluster_heatmaps", paste0("bendall_",stim,".png")))
img2 <- image_read(file.path(figures_folder, "across_cluster_heatmaps", paste0("farmer_", stim,".png")))
imgs <- c(img1, img2)
imgs <- image_append(imgs)
imgs_pdf <- image_convert(imgs)
vec_out[[i]] <- imgs_pdf
}
image_write(vec_out, path = file.path(figures_folder, "test.pdf"), format = "pdf")
Any suggestions would be helpful. Thanks.
Couldn't you write a knitr-document instead, or as rmarkdown-file?
I could not run your code, since it's not reproducible.
A mini example in rmarkdown:
The following code produces 3 plots in a for-loop.
By choosing result = 'asis' as option and by inserting
cat("\n\n\\pagebreak\n") in the for-loop, every output is printed on a separate page.
---
title: "Test page break between two figures"
output: pdf_document
---
```{r, echo=FALSE, results='asis'}
for (i in 1:3) {
print(plot(1:i, rnorm(i)))
cat("\n\n\\pagebreak\n")
}
```
I suggest doing something as mentioned above, instead of creating in a first step all pdf-files separately and glue them together in a second step.

How to remove display of strange characters in R-Markdown chunk output?

I am getting frequencies from a few rasters and am doing so in R-Markdown. I am using lapply to get the frequencies from the rasters in a list. When I store those frequencies in a list of data.frames, the chunk output displays some unexpected non-numeric characters.
Example rasters:
```{r}
require(raster)
r1 <- setValues(raster(nrows = 10, ncols = 10), sample(1:10, 100, replace = TRUE))
r2 <- setValues(raster(nrows = 10, ncols = 10), sample(1:10, 100, replace = TRUE))
rList <- list(r1, r2)
```
Getting the frequencies:
```{r}
lapply(rList, function(ras) {
data.frame(freq(ras))
})
```
Output from the above chunk:
If I display only the data frame itself, those characters are not displayed:
```{r}
lapply(rList, function(ras) {
data.frame(freq(ras))
})[[2]]
```
The correct values are also shown if do not use data.frame:
```{r}
lapply(rList, function(ras) {
freq(ras)
})
```
I've tried saving the Rmd with UTF-8 encoding and am on RStudio 1.2.5019. Any ideas on how to get the list of data frames to display properly would be appreciated.
Edit: Just a note that the characters do not display in any scenario in the generated html file, only in the specific chunk in the R Notebook file itself.
Edit 2:
The full code and YAML header for the notebook that generates the strange characters is below:
---
title: "R Notebook"
output: html_notebook
---
```{r}
require(raster)
r1 <- setValues(raster(nrows = 10, ncols = 10), sample(1:10, 100, replace = TRUE))
r2 <- setValues(raster(nrows = 10, ncols = 10), sample(1:10, 100, replace = TRUE))
rList <- list(r1, r2)
```
```{r}
lapply(rList, function(ras) {
data.frame(freq(ras))
})
```
```{r}
lapply(rList, function(ras) {
data.frame(freq(ras))
})[[2]]
```
Check out this code is properly showing the output. You can use print
On previewing the html notebook output is not showing any UTF characters
If you want to use the chunk output than you can use
as.data.frame()
I've had this experience before with that same "ΓΏ" character, but haven't thought about it a lot (Mac RStudio). I have been able to re-run my code and haven't been able to reproduce it on the same chunk, even in the same session.
I do use the formattable package often, and since folks are mentioning the formatting aspect, perhaps that's at play? Although it's hard for me to envision exactly how.
I did find these related items about Pandoc rendering Unicode artifacts on certain character inputs, and about Pandoc converting to UTF-8. Since the workflow involves knitr feeding Pandoc, maybe that's a piece?
https://github.com/jgm/pandoc/issues/844
https://yihui.org/en/2018/11/biggest-regret-knitr/
Can't quite connect the dots, but hope it helps someone else put it together.
I don't remember it happening recently, so potentially one avenue is to update pandoc and knitr (or even RStudio altogether -- I'm on 1.2.5019)?

Import and manipulate R Scripts programmatically / Convert .R to .Rmd

I have an R Script that I would like to import from within a different R-script, manipulate it's content (search and replace) and save with a different extension (.rmd).
This is how the example.R File would look before manipulation:
# A title
# chunkstart
plot(1,1)
# chunkend
and this is how example.Rmd it would look after manipulation: replaced "# chunkstart" and "# chunkend" with ```{r} and ```, respectively.
# A title
```{r}
plot(1,1)
```
I've been searching for methods to do this, but so far have found none. Any ideas?
I'm sure that you can do it using regex with less lines of code.
However its should solve your problem.
library(magrittr)
readLines('example.R') %>%
stringr::str_replace("# chunkstart", "```{r}") %>%
stringr::str_replace("# chunkend", "```") %>%
writeLines("example.Rmd")
With the following lines of code you will be able to apply this "operation" in every .R file inside /path_to_some_directory
lapply(list.files('/path_to_some_directory', pattern = ".R$",
full.names = TRUE), function(data) {
readLines(data) %>%
stringr::str_replace("# chunkstart", "```{r}") %>%
stringr::str_replace("# chunkend", "```") %>%
writeLines(paste0(data, "md"))
})
Hope it helps!
I think ?knitr::spin is a relevant answer to the question (specifically asking for ideas), or at least a useful alternative to consider.
You'd have to slightly reformat the input, but the benefits would be a built-in, much richer and versatile way to deal with chunk options and formatting.
Here's what an annotated R script might look like (with spin's default regexs),
#' ## A title
#' first chunk
#- fig.width=10
plot(1,1)
# some text
#' another chunk
plot(2,2)
and the output Rmd reads,
## A title
first chunk
```{r fig.width=10}
plot(1,1)
# some text
```
another chunk
```{r }
plot(2,2)
```

Generate both markdown comments and html wigets in a loop rmarkdown

I'm trying to generate a flexdashboard, creating each page from within a loop and with each of the generated pages containing a dygraph (although any HTML widget ought to behave the same).
I have looked extensively and it seems that rmarkdown comments can be generated using cat("title") (as per the solution here: generate markdown comments within for loop).
The HTML widgets on the other hand only behave nicely if you use htmltools::tagList() (as per the solution here:For loop over dygraph does not work in R).
I dont have working code to share, but this broadly gives the picture of what I am hoping to achieve:
for (i in 1:ncol(downloadedData)){
fund_NAVS <- downloadedData[,i] #this is an xts object
fund_NAVS <- fund_NAVS[!is.na(fund_NAVS)]
cat("pageTitle")
cat("===================================== \n")
cat("Row\n")
cat("------------------------------------- \n")
cat("### Page title")
dygraph(fund_NAVS)
}
I've been able to get this to partially work using pander::pandoc.header. However, getting content (plots and HTML objects) to actually show up in the final HTML file is a different story.
---
output:
flexdashboard::flex_dashboard:
orientation: columns
source: embed
vertical_layout: fill
---
```{r results='asis'}
library(pander)
pages <- 5
cols <- 2
sections <- 3
for (p in 1:pages) {
pandoc.header(paste("Page", p), level = 1)
for (c in 1:cols) {
pandoc.header(paste("Column", c), level = 2)
for (s in 1:sections) {
pandoc.header(paste("Section", s), level = 3)
cat("hi")
pandoc.p("")
}
}
}
```
I was able to autogenerate content by building r chunks explicitly then kniting them inline with r paste(knitr::knit(text = out)). This amazing line of code was found in an SO post.
In my case, I wanted to produce a series of graphs, each with a separate tab, with different content. Each graph was similar but there were numerous (about 15) and I didn't want to copy/paste all of the separate chunks.
Here is a gist you can download of a more simple example. (The code is also below but note that I add \ before each chunk so that it rendered as a single block of code so remove the \ before running.) I built a much more complicated function to build graphs, but the idea of the R chunks can be carried forward to any list object containing htmlwidgets as elements.
---
title: "Loop to Auto Build Tabs Containing htmlwidgets"
output: flexdashboard::flex_dashboard
---
\```{r setup, echo =FALSE, eval = TRUE}
library(tidyverse)
library(flexdashboard)
library(highcharter)
labels <- mtcars %>% names # these will serve as labels for each tab
# create a bunch of random, nonsensical line graphs
hcs <- purrr::map(.x = mtcars, ~highcharter::hchart(mtcars, y = .x, type = 'line')) %>%
setNames(labels) # assign names to each element to use later as tab titles
\```
Page
====================
Column {.tabset .tabset-fade}
-----------------------------
<!-- loop to build each tabs (in flexdashboard syntax) -->
<!-- each element of the list object `out` is a single tab written in rmarkdown -->
<!-- you can see this running the next chunk and typing `cat(out[[1]])` -->
\```{r, echo = FALSE, eval = TRUE}
out <- lapply(seq_along(hcs), function(i) {
a1 <- knitr::knit_expand(text = sprintf("### %s\n", names(hcs)[i])) # tab header, auto extracts names of `hcs`
a2 <- knitr::knit_expand(text = "\n```{r}") # start r chunk
a3 <- knitr::knit_expand(text = sprintf("\nhcs[[%d]]", i)) # extract graphs by "writing" out `hcs[[1]]`, `hcs[[2]]` etc. to be rendered later
a4 <- knitr::knit_expand(text = "\n```\n") # end r chunk
paste(a1, a2, a3, a4, collapse = '\n') # collapse together all lines with newline separator
})
\```
<!-- As I mentioned in the SO post, I don't quite understand why it has to be -->
<!-- 'r paste(knitr::knit(...)' vs just 'r knitr::knit(...)' but hey, it works -->
`r paste(knitr::knit(text = paste(out, collapse = '\n')))`

Resources