I'm familiar with R markdown "parameters".
However, say I want to generate the same report (same chart, same table) but for 5 different regions.
Is there a way to do this elegantly in a loop or lapply or do I need to make several sections. So in pseudo code I want to do something like:
for(i in 1:5):
Bunch of text
table[i]
plot[i]
Instead of
bunch of text
table[1]
plot[1]
bunch of text
table[2]
plot[2]
...
Put another way, I want to functionalize a "section" of the report, and then I can call
for(i in 1:5):
makeReport(i)
And it will go in, put in the text, figures, etc associated with index i.
You have to call print explicitly if inside for loop:
```{r}
for(i in 1:2) {
print(summary(cars[,-i]))
plot(cars[,-i])
}
```
or
```{r}
makeReport <- function(i) {
print(summary(cars[,-i]))
plot(cars[,-i])
}
for(i in 1:2) {
makeReport(i)
}
```
Update
As Stéphane Laurent already demonstrated in Dynamic number of calls to a chunk with knitr
you can define a child .rmd:
test_section.rmd
Header: `r i`-th cars
```{r}
print(summary(cars[,-i]))
plot(cars[,-i])
```
and in the main rmd file concatenate the results:
```{r runall, include=FALSE}
out <- NULL
for (i in 1:2) {
out <- c(out, knitr::knit_child('test_section.rmd'))
}
```
`r paste(out, collapse = '\n')`
Related
This is what Im doing to generate a markdown so that all the things should be in one place.
How can i put these output into a datatable form which are more readable and easier to search.The list which is made are of different length. Each list has a series of table under it.
If there a way to convert these differing length list to data table format that would be really helpful
The table looks like this
## Prepare for analyses
```{r,warning=FALSE,message=FALSE}
set.seed(1234)
library(europepmc)
library(tidypmc)
library(tidyverse)
#library(dplyr)
```
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
##Cytarabine cytogenetically normal aml adult clinical trial Randomized Controlled Trial. 828 records found, showing 10
```{r,include=FALSE}
b <-epmc_search(query = 'cytarabine cytogenetically normal aml adult clinical trial Randomized Controlled Trial OPEN_ACCESS:Y',limit = 10)
pmcids <- b$pmcid[b$isOpenAccess=="Y"]
docs <- map(pmcids, epmc_ftxt)
my_tables <- map(docs, pmc_table)
```
```{r}
names(my_tables) <- pmcids
```
The code chunk input and output is then displayed as follows:
```{r basicconsole}
source("flat.R")
L1 <- flattenlist(my_tables)
l.f <- Filter(function(a) any(!is.na(a)), L1)
l.f
#tibble:::print.tbl_df(head(df))
#n <- paste0("Valporic_", names(l.f), ".txt")
for (i in 1:length(l.f)) {
write.table(l.f[i], sep = "\t",row.names = FALSE,col.names = TRUE,file=paste0(names(l.f)[i], ".txt"))
}
UPDATE
I have manged to covert those tibble into dataframe
using this solution
##Outout
```{r}
abc <- mapply(cbind, l.f)
abc
But when it is rendered in the markdown the column formatting is gone. Now i have now dataframe inside list.
But still im not sure how to put that into a data table
**UPDATE 2.0 **
The better approach is to read those saved output as list of files into data table and then use it as markdown but so far it is taking only one ID only. My code.
tbl_fread <-
list.files(pattern = "*.txt") %>%
map_df(~fread(.))
knitr::kable(head(tbl_fread), "pipe")
Is it possible to put these files as such.
if a list of file are from one PMCID then those would be all in one column such as if PMCID one has 3 output then all of them should be one the same row. Then the next PMCID in the second one etc etc.
UPDATE new
I have managed to align the output into more readable format. But It seems that by default all the files assigned to multiple columns which would be the case given that im reading all the files together since my idea of using the list to data table didn't work.
If i can push or stack each unique PMCID over one another instead of all in one after another that would be. Good
knitr::kable(tbl_fread, align = "lccrr")
This may be something you can adapt for R Markdown. I'm not sure what the rationale is to save and load the tables. Instead, you could obtain the tables and show in html directly.
As you are using HTML, make sure to have results='asis' in your chunk. You can use a for loop and seq_along to show each table. You can include information in your table caption, such as the PMCID as well as table number.
---
title: "test13121"
author: "Ben"
date: "1/31/2021"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# Libraries
```{r}
library(tidypmc)
library(tidyverse)
library(europepmc)
library(kableExtra)
```
# Get Articles
```{r, echo = FALSE}
b <-epmc_search(query = 'cytarabine aml OPEN_ACCESS:Y',limit = 6)
pmcids <- b$pmcid[b$isOpenAccess=="Y"]
docs <- map(pmcids, epmc_ftxt)
my_tables <- map(docs, pmc_table)
names(my_tables) <- pmcids
```
# Show Tables
```{r, echo=F, results='asis'}
for (i in seq_along(my_tables)) {
for (j in seq_along(my_tables[[i]])) {
print(kable(x = my_tables[[i]][[j]], caption = paste0(names(my_tables)[i], ": Table ", j)))
}
}
```
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.
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')))`
For example if my data frame was:
exampledf <- data.frame(column = c("exampletext1", "exampletext2", "exmapletext3"))
I would like the first page to have "exampletext1", the second page to have
"exampletext2", etc.
\pagebreak works:
```{r, echo=FALSE}; exampledf[1,]```
\pagebreak
```{r, echo=FALSE} exampledf[2,]```
But my data frame is too large to make it practical.
I really need to loop through all my values:
for(i in 1:NROW(exampledf)) {
single <- exampledf[i]
strwrap(single, 70))
}
It's a weird question I realize.
You can put \\newpage inside the cat function to add a page break after each iteration of the loop. The chunk also needs to have the parameter results="asis". For example, does something like this work for you:
```{r echo=FALSE, results="asis", warning=FALSE}
library(xtable)
exampledf <- data.frame(column = c("exampletext1", "exampletext2", "exmapletext3"))
for (i in 1:nrow(exampledf)) {
# Just added this as another way of displaying a row of data
print(xtable(exampledf[i, , drop=FALSE]), comment=FALSE)
print(strwrap(exampledf[i,], 70))
cat("\n\\newpage\n")
}
```
In my dataset, I have 60 groups that I want to analyze in put into an HTML report using R Markdown. Because I want to apply the same analysis to each group, I am hoping that there is a way I can dynamically generate the code blocks/analysis.
Simply, I want to avoid replicating the block 60 times.
I came across this this question which uses children in knitr. I have attempted to replicate this with the iris dataset. In my example below, all I wanted to do was generate three H4 titles, one for each species.
It's worth noting that I am not married to this approach, it just appears to be related to what I am looking to do.
Here are the files I used:
parent.RMD file. This would be my "master" report.
Automate Chunks of Analysis in R Markdown
========================================================
```{r setup, echo=FALSE}
library(knitr)
```
```{r run-numeric-md, include=FALSE}
out = NULL
for (i in as.character(unique(iris$Species))) {
out = c(out, knit_child('child.Rmd'))
}
```
And here is child.Rmd.
#### Species = `r [i]`
Try knit_expand():
Automate Chunks of Analysis in R Markdown
========================================================
```{r setup, echo=FALSE}
library(knitr)
```
```{r run-numeric-md, include=FALSE}
out = NULL
for (i in as.character(unique(iris$Species))) {
out = c(out, knit_expand(text='#### Species = {{i}}'))
}
```
`r paste(knit(text = out), collapse = '\n')`
You can also create a template file like 'child.rmd' and put this in your for loop so you don't have to put a complicated analysis in quotes:
out = c(out, knit_expand('template.rmd'))
Then have your 'template.rmd' be:
#### Species = {{i}}
Taking #sam's solution, I made the following generic function. Say you have a data frame called grfDf with DiagrammeR graph objects in the column graph. The following is all you need to plot all the graphs in Rmd : r require(DiagrammeR); renderHtmlWidgetList(grfDf$graph, render_graph). See the code for caveats.
```
require(knitr)
#' Render a list of htmlWidgets using various tricks
#'
#' #param widgetList A list of htmlWidget objects to be rendered
#' #param renderFunction The function to render individual widgets. It can be either a name
#' of the rendering function, e.g., "render_graph" in DiagrammeR, or the actual function to
#' be passed to this call.
#' #return The knitted string. This is to be included in the output by using `r renderHtmlWidgetList(...)`;
#' #details This is a collection of various tricks. See the URL citations in the code.
#' Note that this code does alliterate global variables starting with "renderHtmlWidgetList_".
#' You may want to delete them using rm(list = ls(pattern="renderHtmlWidgetList_*")).
#' #examples Inlcude the following in the Rmd directly
#' `r require(DiagrammeR); renderHtmlWidgetList(grfDf$graph, render_graph)`
#'
#' #export
renderHtmlWidgetList <- function(widgetList, renderFunction){
# error checking
stopifnot(is.list(widgetList))
# handles if the renderFunction is actually a function
# http://stackoverflow.com/questions/10520772/in-r-how-to-get-an-objects-name-after-it-is-sent-to-a-function
if(is.function(renderFunction)) {
# convert back to string, because we need to knit it later
renderFunction <- deparse(substitute(renderFunction))
}
stopifnot(is.character(renderFunction) & length(renderFunction)==1)
stopifnot(exists(renderFunction, mode = "function"))
# inject global vars; make sure we have a unique global var name
gVarName<- paste0("renderHtmlWidgetList_", sample(1:10000, 1))
while (exists(gVarName)) {
gVarName<- paste0("renderHtmlWidgetList_", sample(1:10000, 1))
}
# assigning widgetList to a global temp var
# http://stackoverflow.com/questions/5510966/create-a-variable-name-with-paste-in-r
assign(gVarName, widgetList, envir = .GlobalEnv)
# solution from https://gist.github.com/ReportMort/9ccb544a337fd1778179
out <- NULL
knitPrefix <- "\n```{r results='asis', cache=FALSE, echo=FALSE}\n\n"
knitSuffix <- "\n\n```"
for (i in 1:length(widgetList)) {
knit_expanded <- paste0(knitPrefix, renderFunction, "(", gVarName, "[[", i, "]])")
out = c(out, knit_expanded)
}
#invisible(out)
paste(knitr::knit(text = out), collapse = '\n')
}
```