edgebundle doesn't render plot when in loop in markdown - r

I'm trying to create an automated report where I create a series of chord graphs using edgebundleR.
I have a function that does a bunch of stuff and has more or less this form:
plot_chords <- function(x,t,pos) {
...
stuff I do with the data
...
g <- graph.adjacency(mydata, mode="upper", weighted=TRUE, diag=FALSE)
return(edgebundle(g))
}
This function works properly if I don't use it inside a loop. It doesn't if it is in a loop like this:
```{r echo = FALSE,message=FALSE, warning = FALSE,results = "asis"}
for (c in unique(df$Group)) {
cat("\n\n## ",c," - Negative Correlations (min r=",t_neg," - only significative)\n\n")
plot_chords(subset(df, Group == c),0.5,0)
}
```
I found that in general, this doesn't work inside loops unless I use print:
for (c in unique(df$Group)) {
temp=df[df$Group == c,]
print(plot_chords(temp,0.5,0))
}
But print doesn't work in markdown.
How can I render the plot?
Thanks.

The edgebundle call returns an htmlwidget and works, as you noted, well when not in a loop. A solution to your situation would be use the for loop to generate several specific R code chunks in a temporary file and then evaluate that that temporary file as a child file in your primary .Rmd file.
For example, in a .Rmd file these two chunks will load the needed packages and define a function foo which creates and shows a random edgebundle.
```{r}
set.seed(42)
library(edgebundleR)
library(igraph)
```
## test the function
```{r}
foo <- function() {
adjm <- matrix(sample(0:1, 100, replace = TRUE, prob = c(0.6, 0.4)), nc = 10)
g <- graph.adjacency(adjm)
edgebundle(g)
}
```
Calling foo twice in a chunk will work as expected in the output .html document.
```{r}
foo()
foo()
```
To generate several edgebudles in a for loop try this. Write a for loop to populate a temp.Rmd file with the needed R chunks. You will need to modify this as needed for your application.
## test the function in a for loop
```{r}
tmpfile <- tempfile(fileext = ".Rmd")
for(i in 1:3) {
cat("### This is edgebundle", i, "of 3.\n```{r}\nfoo()\n```\n",
file = tmpfile, append = TRUE)
}
```
The contents of tmpfile look like this:
### This is edgebundle 1 of 3.
```{r}
foo()
```
### This is edgebundle 2 of 3.
```{r}
foo()
```
### This is edgebundle 3 of 3.
```{r}
foo()
```
To display the widgets in your primary output file use a chunk like this:
```{r child = tmpfile}
```
The full .Rmd file and result:
example.Rmd:
# edgebundleR and knitr
Answer to https://stackoverflow.com/questions/47926520/edgebundle-doesnt-render-plot-when-in-loop-in-markdown
```{r}
set.seed(42)
library(edgebundleR)
library(igraph)
```
## test the function
```{r}
foo <- function() {
adjm <- matrix(sample(0:1, 100, replace = TRUE, prob = c(0.6, 0.4)), nc = 10)
g <- graph.adjacency(adjm)
edgebundle(g)
}
foo()
foo()
```
## test the function in a for loop
```{r}
tmpfile <- tempfile(fileext = ".Rmd")
for(i in 1:3) {
cat("### This is edgebundle", i, "of 3.\n```{r}\nfoo()\n```\n",
file = tmpfile, append = TRUE)
}
```
```{r child = tmpfile}
```
```{r}
print(sessionInfo(), local = FALSE)
```

Related

Error in plot.new() : figure margins too large in Rmarkdown

I am having a strange problem within Rmarkdown. The following code works flawlessly if it has to plot for only 3-4 datasets (tested) but as soon the number of datasets increase to 9 or more. I get a strange error.
code:
```{r scRNALoadData, echo=FALSE, message=FALSE, warning=FALSE, include=TRUE, warnings=FALSE, eval=TRUE, results="asis"}
# Loading of libraries
suppressPackageStartupMessages(library(scater))
suppressPackageStartupMessages(library(mvoutlier))
suppressPackageStartupMessages(library(Rtsne))
suppressPackageStartupMessages(library(limma))
suppressPackageStartupMessages(library(ggplot2))
suppressPackageStartupMessages(library(repr))
suppressPackageStartupMessages(library(cowplot))
suppressPackageStartupMessages(library(knitr))
suppressPackageStartupMessages(library(Matrix))
suppressPackageStartupMessages(library(rmarkdown))
suppressPackageStartupMessages(library(SingleCellExperiment))
suppressPackageStartupMessages(library(scran))
suppressPackageStartupMessages(library(scRNAseq))
#suppressPackageStartupMessages(library(iSEE))
suppressPackageStartupMessages(library(SCopeLoomR))
options(stringsAsFactors = FALSE)
# Loading expression data
loadSCE <- function(path){
sce <- scater::read10XResults(path)
#sce <- normalize(sce) # Data normalization based on scran
mitochondrialGenes <- as.character(rowData(sce)[startsWith(rowData(sce)$symbol, "mt-"),]$id)
isSpike(sce, "mt") <- rownames(sce) %in% mitochondrialGenes
sce <- calculateQCMetrics(sce,
feature_controls = list(
MT = isSpike(sce, "mt")
))
}
# Read-in expression sample scRNA matrix from directory
paths <- list.dirs(path = "/Abhi/test/test2/", recursive = FALSE)
for (i in 1:length(paths))
assign(paste0("sce_",i), loadSCE(paths[i]))
sce=0
for (i in 1:length(paths))
sce[i]<-print(noquote(paste0("sce_",i)))
t_list <- list()
t_list <- mget(ls(pattern="sce_\\d+"))
for(i in seq_along(t_list))
{
metadata(t_list[[i]])["name"] <- paste0("iMates-",i)
}
```
### Library size
``` {r scRNALibrarySize, echo=FALSE, message=FALSE, warning=FALSE, include=TRUE, warnings=FALSE, eval=TRUE, results="asis"}
# Single Cell RNA plot library histogram
plotLibrarySize <- function(sce, cutoffPoint){
options(repr.plot.width=4, repr.plot.height=4)
hist(
sce$total_counts,
breaks = 100
)
abline(v = cutoffPoint, col = "red")
}
# Single Cell RNA plot library box
plotLibrarySize.Box <- function(sce, cutoffPoint){
options(repr.plot.width=4, repr.plot.height=4)
(sce$total_counts)
}
# Summary stats
for (i in 1:length(paths))
print(sum(get(sce[i])$total_counts))
# Plot histograms library size
l<-length(paths)
ll<-round(l/2, digits=0)
par(mfrow=c(ll,2))
for (i in 1:length(paths)) {
plotLibrarySize(get(paste0("sce_",i)), 2500) }
# Plot box library size
l<-length(paths)
par(mfrow=c(ll,2))
for (i in 1:length(paths)) {
b<-plotLibrarySize.Box(get(paste0("sce_",i)),2500)
boxplot(b)
}
```
The error that I get is
Quitting from lines 48-79 (tq.Rmd)
Error in plot.new() : figure margins too large
Calls: <Anonymous> ... hist -> hist.default -> plot -> plot.histogram -> plot.new
Execution halted
Where line 48 is beginning of the second block of R code.
Can anyone help me in fixing this error.
I have seen other posts as well
"Error in plot.new() : figure margins too large"
I have executed the code in both console and within rstudio.
Many thanks in advance.

Create sections through a loop with knitr

See this reproducible example :
---
title: "test"
output: html_document
---
## foo
```{r}
plot(1:3)
```
## bar
```{r}
plot(4:7)
```
## baz
```{r}
plot(8:12)
```
I want to be able to automate the creation of these sections as I can't know how many they will be before going further in my analysis.
My input to get this would be :
my_list <- list(foo = 1:3, bar = 4:7, baz = 8:12)
my_fun <- plot
my_depth <- 2
And the ideal answer (though I'm welcoming any improvement) would help me build a mdapply function so that I could just run:
```{r}
mdapply(X = my_list, FUN = my_fun, title_depth = my_depth)
```
And get the same output.
R package pander can generate Pandoc's markdown on the fly.
The key is to use the chunk option results='asis' to tell R Markdown to render pander's output as Markdown.
You just need to be careful to generate valid Markdown!
Try this:
---
title: "Test sections"
output: html_document
---
## A function that generates sections
```{r}
library(pander)
create_section <- function() {
# Inserts "## Title (auto)"
pander::pandoc.header('Title (auto)', level = 2)
# Section contents
# e.g. a random plot
plot(sample(1000, 10))
# a list, formatted as Markdown
# adding also empty lines, to be sure that this is valid Markdown
pander::pandoc.p('')
pander::pandoc.list(letters[1:3])
pander::pandoc.p('')
}
```
## Generate sections
```{r, results='asis'}
n_sections <- 3
for (i in seq(n_sections)) {
create_section()
}
```
It still looks hackish, but Markdown has its limits...
It seems like I found a way!
The whole idea is to pass what would be typed by hand as a string inside of knit(text=the_string) used in inline code.
So the function basically pastes a bunch of strings together, with a bit of substitute magic to have a function that feels like it's part of the apply family.
Parameter depth decides how many # you want.
Parameter options contains the chunk options, as a vector.
A vector shouldn't be able to contain logical and characters together but here it doesn't matter as it will all be coerced to character anyway, so c(echo= FALSE, results="hide") is fine.
I expect that it's easy to break but seems to work fine when treated gently.
---
title: "test"
output: html_document
---
```{r setup, include = FALSE}
library(knitr)
mdapply <- function(X, FUN, depth, options=""){
FUN <- as.character(substitute(FUN))
list_name <- as.character(substitute(X))
if(options != "")
options <- paste(",",names(options),"=",options,collapse="")
build_chunk <- function(nm)
{
paste0(
paste0(rep("#",depth), collapse=""),
" ",
nm,
"\n\n```{r", options, "}\n",
FUN,
"(", list_name, "[['", nm, "']])\n```")
}
parts <- sapply(names(X), build_chunk)
whole <- paste(parts, collapse="\n\n")
knit(text=whole)
}
```
```{r code}
my_list <- list(foo = 1:3, bar = 4:7, baz = 8:12)
```
`r mdapply(my_list, plot, 2, c(echo=FALSE))`
I would actually suggest a solution that works a little bit different, i.e. create the R-Markdown file from an R-script and then render it from the same R-script:
# function that creates the markdown header
rmd_header <- function(title){
paste0(
"---
title: \"", title, "\"
output: html_document
---
"
)
}
# function that creates the Rmd code for the plots
rmd_plot <- function(my_list, my_fun){
paste0(
"
## ", names(my_list), "
```{r}
", deparse(substitute(my_fun)), "(", deparse(substitute(my_list)), "[[", seq_along(my_list), "]])
```
"
)
}
# your objects
my_list <- list(foo = 1:3, bar = 4:7, baz = 8:12)
my_fun <- plot
my_depth <- 2 # I actually don't get what this is for
# now write everything into an rmd file
cat(rmd_header("Your Title")
, rmd_plot(my_list, plot)
, file = "test.rmd")
# and then create the html from that
rmarkdown::render("test.rmd", output_file = "test.html")
One thing to mention here: the indentation in the Rmd file does matter and when you copy the code here, make sure that R-Studio inserts it in the R-script as intended (because often it doesn't).
Taking a similar approach to #Georgery... but in a somewhat over-engineered fashion (also somewhat more general?). Anyway, here it goes.
make_template <- function(my_list, my_fun, my_depth, my_title, my_output_type, my_template_file){
require(glue)
n <- length(my_list)
# --- Rmd header ---
make_header <- function(my_title, my_output_type){
#
my_header <- glue(
"---", "\n",
"title: ", deparse({my_title}), "\n",
"output: ", deparse({my_output_type}), "\n",
"---", "\n",
"\n",
"\n"
)
return(my_header)
}
# --- one section only ---
make_section <- function(i){
one_section <- glue(
"\n",
"\n",
paste0(rep("#", times = {my_depth}), collapse = ""), " ", names({my_list})[[i]], "\n",
"\n",
"```{{r}}", "\n",
paste0({my_fun}, "(", deparse({my_list}[[i]]), ")"), "\n",
"```", "\n",
"\n",
"\n"
)
return(one_section)
}
# --- produce whole template ---
my_header <- make_header(my_title, my_output_type)
all_my_sections <- ""
for (i in seq_along(my_list)) {
all_my_sections <- paste0(all_my_sections, make_section(i))
}
my_template <- paste0(my_header, "\n", "\n", all_my_sections)
# --- write out
cat(my_template, file = my_template_file)
}
# --- try it
make_template(my_list = list(foo = 1:3, bar = 4:7, baz = 8:12, glop = 1:7),
my_fun = "plot",
my_depth = 4,
my_title = "super cool title",
my_output_type = "html_document",
my_template_file = "my_template_file.Rmd"
)

Set number of decimal places to show in output

I am wanting to get more into using R markdown to perform analyses and generate output. Maybe I'm missing something simple, but I just want to be able to set the number of decimal places to show either 2 or 3 digits, depending on the output (e.g. t-statistic vs p-value).
I have previously used r options(digits=2), which works until the last digit you want to include is 0. I have gotten around this with the sprintf function, but having to specify for each number.
Is there a way to set a 'global' sprintf option so that for all numbers following, the same number of decimal places are shown?
Thank you,
Paul
Defining a format for inline code output is feasible with a knitr inline hook (hooks are the hidden gems of knitr).
Example #1
With this Rmd file, the number of decimals is controlled without using sprintf() in all inline codes:
---
title: "Use an inline hook"
---
```{r setup, include=FALSE}
# Register an inline hook:
knitr::knit_hooks$set(inline = function(x) {
x <- sprintf("%1.2f", x)
paste(x, collapse = ", ")
})
```
Now, get 3.14 with just writing `r pi`.
Example #2
Want to change the inline output format in some part of the report?
This Rmd file does the job:
---
title: "Use a closure and an inline hook"
---
```{r setup, include=FALSE}
# Register an inline hook
knitr::knit_hooks$set(inline = function(x) {
paste(custom_print(x), collapse = ", ")
})
# Define a function factory (from #eipi10 answer)
op <- function(d = 2) {
function(x) sprintf(paste0("%1.", d, "f"), x)
}
# Use a closure
custom_print <- op()
```
Now, get 3.14 with `r pi`...
```{r three-decimals, include=FALSE}
custom_print <- op(d = 3)
```
...and now 3.142 with `r pi`.
```{r more-decimals, include=FALSE}
custom_print <- op(d = 10)
```
Finally, get 3.1415926536 with `r pi`.
Example #3
Want to display different formats for t-statistic and p-value?
One can use S3 objects and an inline hook as in this Rmd file:
---
title: "Use S3 methods and an inline hook"
---
```{r setup, include=FALSE}
# Register an inline hook
knitr::knit_hooks$set(inline = function(x) {
paste(custom_print(x), collapse = ", ")
})
# Define a generic
custom_print <- function(x, ...) {
UseMethod("custom_print", x)
}
# Define a method for p-values
custom_print.p.value <- function(x, ...) paste(sprintf("%1.2f", x), collapse = ", ")
# Define a method for t-statistics
custom_print.t.stat <- function(x, ...) paste(sprintf("%1.1f", x), collapse = ", ")
```
Estimate models...
```{r fake-results, include=FALSE}
t <- c(2.581, -1.897)
class(t) <- "t.stat"
p <- c(0.025, 0.745)
class(p) <- "p.value"
```
Want to show T-stats: `r t` (get 2.6, -1.9).
And p-values: `r p` (get 0.03, 0.74).
Who said knitr is a wonderful package?
I don't know of a way to set a global option (though there may be one). But you can write a convenience output function to reduce the amount of typing. For example, put this function at the beginning of your document:
op = function(x, d=2) sprintf(paste0("%1.",d,"f"), x)
Then, later in your document, when you want to output numbers, you can, for example, do:
op(mtcars$mpg)
Or if you want 3 digits instead of the default 2, you can do:
op(mtcars$mpg, 3)
As found in the tutorial here by Yihui, this is how I've successfully implemented it in my Rmd file.
{r setup, include=FALSE, cache=FALSE}
options(scipen = 1, digits = 2) #set to two decimal

Rmarkdown Chunk Name from Variable

How can I use a variable as the chunk name? I have a child document which gets called a number of times, and I need to advance the chunk labels in such a manner than I can also cross reference them.
Something like this:
child.Rmd
```{r }
if(!exists('existing')) existing <- 0
existing = existing + 1
myChunk <- sprintf("myChunk-%s",existing)
```
## Analysis Routine `r existing`
```{r myChunk,echo = FALSE}
#DO SOMETHING, LIKE PLOT
```
master.Rmd
# Analysis Routines
Analysis for this can be seen in figures \ref{myChunk-1}, \ref{myChunk-2} and \ref{myChunk-3}
```{r child = 'child.Rmd'}
```
```{r child = 'child.Rmd'}
```
```{r child = 'child.Rmd'}
```
EDIT POTENTIAL SOLUTION
Here is one potential workaround, inspired by SQL injection of all things...
child.Rmd
```{r }
if(!exists('existing')) existing <- 0
existing = existing + 1
myChunk <- sprintf("myChunk-%s",existing)
```
## Analysis Routine `r existing`
```{r myChunk,echo = FALSE,fig.cap=sprintf("The Caption}\\label{%s",myChunk)}
#DO SOMETHING, LIKE PLOT
```
A suggestion to preknit the Rmd file into another Rmd file before knitting&rendering as follows
master.Rmd:
# Analysis Routines
Analysis for this can be seen in figures `r paste(paste0("\\ref{", CHUNK_NAME, 1:NUM_CHUNKS, "}"), collapse=", ")`
###
rmdTxt <- unlist(lapply(1:NUM_CHUNKS, function(n) {
c(paste0("## Analysis Routine ", n),
paste0("```{r ",CHUNK_NAME, n, ", child = 'child.Rmd'}"),
"```")
}))
writeLines(rmdTxt)
###
child.Rmd:
```{r,echo = FALSE}
plot(rnorm(100))
```
To knit & render the Rmd:
devtools::install_github("chinsoon12/PreKnitPostHTMLRender")
library(PreKnitPostHTMLRender) #requires version >= 0.1.1
NUM_CHUNKS <- 5
CHUNK_NAME <- "myChunk-"
preknit_knit_render_postrender("master.Rmd", "test__test.html")
Hope it helps. Cheers!
If you're getting to this level of complexity, I suggest you look at the brew package.
That provides a templating engine where you can dynamically create the Rmd for knitting.
You get to reference R variables in the outer brew environment, and build you dynamic Rmd from there.
Dynamic chunk names are possible with knitr::knit_expand(). Arguments are referenced in the child document, including in the chunk headers, using {{arg_name}}.
So my parent doc contains:
```{r child_include, results = "asis"}
###
# Generate a section for each dataset
###
species <- c("a", "b")
out <- lapply(species, function(sp) knitr::knit_expand("child.Rmd"))
res = knitr::knit_child(text = unlist(out), quiet = TRUE)
cat(res, sep = "\n")
```
And my child doc, which has no YAML header, contains:
# EDA for species {{sp}}
```{r getname-{{sp}}}
paste("The species is", "{{sp}}")
```
See here in the RMarkdown cookbook.

R knitr Markdown: Output Plots within For Loop

I would like to create an automated knitr report that will produce histograms for each numeric field within my dataframe. My goal is to do this without having to specify the actual fields (this dataset contains over 70 and I would also like to reuse the script).
I've tried a few different approaches:
saving the plot to an object, p, and then calling p after the loop
This only plots the final plot
Creating an array of plots, PLOTS <- NULL, and appending the plots within the loop PLOTS <- append(PLOTS, p)
Accessing these plots out of the loop did not work at all
Even tried saving each to a .png file but would rather not have to deal with the overhead of saving and then re-accessing each file
I'm afraid the intricacies of the plot devices are escaping me.
Question
How can I make the following chunk output each plot within the loop to the report? Currently, the best I can achieve is output of the final plot produced by saving it to an object and calling that object outside of the loop.
R markdown chunk using knitr in RStudio:
```{r plotNumeric, echo=TRUE, fig.height=3}
suppressPackageStartupMessages(library(ggplot2))
FIELDS <- names(df)[sapply(df, class)=="numeric"]
for (field in FIELDS){
qplot(df[,field], main=field)
}
```
From this point, I hope to customize the plots further.
Wrap the qplot in print.
knitr will do that for you if the qplot is outside a loop, but (at least the version I have installed) doesn't detect this inside the loop (which is consistent with the behaviour of the R command line).
Wish to add a quick note:
Somehow I googled the same question and get into this page.
Now in 2018, just use print() in the loop.
for (i in 1:n){
...
f <- ggplot(.......)
print(f)
}
I am using child Rmd files in markdown, also works in sweave.
in Rmd use following snippet:
```{r run-numeric-md, include=FALSE}
out = NULL
for (i in c(1:num_vars)) {
out = c(out, knit_child('da-numeric.Rmd'))
}
```
da-numeric.Rmd looks like:
Variabele `r num_var_names[i]`
------------------------------------
Missing : `r sum(is.na(data[[num_var_names[i]]]))`
Minimum value : `r min(na.omit(data[[num_var_names[i]]]))`
Percentile 1 : `r quantile(na.omit(data[[num_var_names[i]]]),probs = seq(0, 1, 0.01))[2]`
Percentile 99 : `r quantile(na.omit(data[[num_var_names[i]]]),probs = seq(0, 1, 0.01))[100]`
Maximum value : `r max(na.omit(data[[num_var_names[i]]]))`
```{r results='asis', comment="" }
warn_extreme_values=3
d1 = quantile(na.omit(data[[num_var_names[i]]]),probs = seq(0, 1, 0.01))[2] > warn_extreme_values*quantile(na.omit(data[[num_var_names[i]]]),probs = seq(0, 1, 0.01))[1]
d99 = quantile(na.omit(data[[num_var_names[i]]]),probs = seq(0, 1, 0.01))[101] > warn_extreme_values*quantile(na.omit(data[[num_var_names[i]]]),probs = seq(0, 1, 0.01))[100]
if(d1){cat('Warning : Suspect extreme values in left tail')}
if(d99){cat('Warning : Suspect extreme values in right tail')}
```
``` {r eval=TRUE, fig.width=6, fig.height=2}
library(ggplot2)
v <- num_var_names[i]
hp <- ggplot(na.omit(data), aes_string(x=v)) + geom_histogram( colour="grey", fill="grey", binwidth=diff(range(na.omit(data[[v]]))/100))
hp + theme(axis.title.x = element_blank(),axis.text.x = element_text(size=10)) + theme(axis.title.y = element_blank(),axis.text.y = element_text(size=10))
```
see my datamineR package on github
https://github.com/hugokoopmans/dataMineR
As an addition to Hugo's excellent answer, I believe that in 2016 you need to include a print command as well:
```{r run-numeric-md, include=FALSE}
out = NULL
for (i in c(1:num_vars)) {
out = c(out, knit_child('da-numeric.Rmd'))
}
`r paste(out, collapse = '\n')`
```
For knitting Rmd to HTML, I find it more convenient to have a list of figures. In this case I get the desirable output with results='hide' as follows:
---
title: "Make a list of figures and show it"
output:
html_document
---
```{r}
suppressPackageStartupMessages({
library(ggplot2)
library(dplyr)
requireNamespace("scater")
requireNamespace("SingleCellExperiment")
})
```
```{r}
plots <- function() {
print("print")
cat("cat")
message("message")
warning("warning")
# These calls generate unwanted text
scater::mockSCE(ngene = 77, ncells = 33) %>%
scater::logNormCounts() %>%
scater::runPCA() %>%
SingleCellExperiment::reducedDim("PCA") %>%
as.data.frame() %>%
{
list(
f12 = ggplot(., aes(x = PC1, y = PC2)) + geom_point(),
f22 = ggplot(., aes(x = PC2, y = PC3)) + geom_point()
)
}
}
```
```{r, message=FALSE, warning=TRUE, results='hide'}
plots()
```
Only the plots are shown and the warnings (which you can switch off, as well).

Resources