Create sections through a loop with knitr - r

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

Related

wrong order of mixed textual and graphical output in the final output document

Within a for loop I decide to create some graphics and related tables.
The tables are created with writeLines() and print().
For creating the graphics I use plot(), boxplot(), mtext(), text(), axis().
So one graphic is created in many steps. Each graphic for it self is complete and nice.
My problem is:
when I knit the markdown document in Rstudio the graphics and tables are not in the correct place.
The first graphic is shown at the place, where the second should be or a little before, after cor.test.default() tells me a warning. Definitively it is shown during the next pass of the for loop.
Conclusion of the Problem
Creating textual output in adition to graphics is mixed up under special circumstances
For reproducing the problem I create some data
All the computations are collected in the "workingChunk"
For demonstrating the problem I use the chunk "loops" at the end.
So read the last chunk first
In the second loop the FIRST graphic is placed in the output
during the SECOND passage of the loop, after the function cor.test() comes up with the warning.
As well the SECOND graphic is placed in the output
during the THIRD passage of the loop, after the function cor.test() comes up with the warning.
and so on.
I found a workarround for this problem but it is not really handy:
When I replace the for loop with single chunk-calls, then the output is in the correct order.
So I'm sure that the reason for the problem is the interaction of the for loop and the function cor.test()
Here is the Example-code (about 140 lines):
---
title: "Test graphic & textual output"
output:
pdf_document: default
word_document:
html_document:
df_print: paged
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r creatingData, echo=TRUE}
### {r creatingData, echo=TRUE}
# creating some data
a.df <- data.frame(height=c(1:19),
width=c(21:39)*10,
depht=c(41:59)*20,
group=c(1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3))
a.df$groupF <- as.factor(a.df$group)
Vars <- names(a.df)[c(1:3)]
```
```{r workingChunk, echo=TRUE, eval=FALSE}
cat("\n===================================================================\n",
"following the calculations for:\n",
"YVar:",YVar, "\n Group:", "group", "\n")
# Daten für Auswertung extrahieren
selvar <- c("group","groupF", YVar)
# Datensätze mit Fehlwerten in der Zielvariablen ausschließen!
a.sub <- a.df[ complete.cases(a.df[ , selvar]), selvar]
# print(str(a.sub))
## Tabelle für gruppierte Zielgrößen
mT <- table(a.sub[ , c("groupF", YVar)])
print(ftable(addmargins(mT))) ## absolute Häufigkeiten
writeLines("\n")
if (runCorTest) {
## calculating Spearmans correlation
myCorTest <- try(cor.test(a.sub[["group"]], a.sub[[YVar]],
method = "spearman", alternative = "two.sided" ))
print(myCorTest)
writeLines("\n")
}
## preparing the grafic
GL.x1 <- levels(a.sub[["groupF"]]) ## grouplabels
# Calculating the range of Y
my.ylim <- c(min(a.sub[[YVar]], na.rm = TRUE), max(a.sub[[YVar]], na.rm = TRUE))
at.x <- c(1:length(GL.x1)) ## Labelpositionen anlegen
G.data <- vector("list", length(GL.x1)) ## Vektoren für die Daten der Gruppen anlegen
# Daten der Gruppen herausziehen
G.data <- split(a.sub[[YVar]], a.sub["groupF"])
# print(str(G.data))
## drawing emtpy plot
cat("\n\n>>> Here should be placed the Grafik for:",YVar, "<<<\n")
plot( x = NA, y = NA, type = "n",
main = YVar,
xlim = c( 1, length( GL.x1)) + c( -0.6, 0.6),
ylim = my.ylim,
xlab = NA, ylab = NA,
axes = FALSE, frame.plot = TRUE)
# X-axis
axis( 1, las = 1, labels = FALSE)
mtext(GL.x1, at = at.x, cex=0.8, side = 1, line = .3)
# Y-axis
axis( 2, las = 1)
## drawing the data
for (i in 1:length(G.data)){
boxplot(G.data[i], # col = "white",
at = at.x[i], range = 0, add = TRUE,
boxwex = 0.6, yaxs = "i", axes = FALSE)
}
```
```{r, loops, echo=FALSE}
cat("\n===================================================================",
"\n===================================================================\n",
"calling the workingChunk within a for loop without executing cor.test()",
"\n works fine!!",
"\n===================================================================",
"\n===================================================================\n")
runCorTest <- FALSE
for ( YVar in Vars) {
<<workingChunk>>
}
cat("\n===================================================================",
"\n===================================================================\n",
"calling the workingChunk within a for loop with executing cor.test() ",
"\n mixes up the textual output and the graphics!!",
"\n===================================================================",
"\n===================================================================\n")
runCorTest <- TRUE
for ( YVar in Vars) {
<<workingChunk>>
}
cat("\n===================================================================",
"\n===================================================================\n",
"calling the workingChunk with executing cor.test() ",
"\n workarround without a for loop works fine!!",
"\n===================================================================",
"\n===================================================================\n")
runCorTest <- TRUE
YVar <- Vars[1]
<<workingChunk>>
YVar <- Vars[2]
<<workingChunk>>
YVar <- Vars[3]
<<workingChunk>>
```
Not a real answer but a much shorter test case and a workaround:
---
title: "Test graphic & textual output"
output:
html_document:
df_print: paged
word_document: default
pdf_document:
keep_tex: yes
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r}
foo <- function(letter, warn) {
cat("Doing", letter, "\n")
print(letter)
if (warn) warning(letter)
cat("Graphic for", letter, "goes here", "\n")
plot(1, main = letter)
}
```
# with loop and with warning
```{r}
for (letter in letters[1:3])
foo(letter, TRUE)
```
# with loop and without warning
```{r}
for (letter in letters[1:3])
foo(letter, FALSE)
```
# without loop and with warning
```{r}
foo("a", TRUE)
foo("b", TRUE)
foo("c", TRUE)
```
# with loop and with suppressed warning
```{r warning=FALSE}
for (letter in letters[1:3])
foo(letter, TRUE)
```
Besides removing all of the data processing, I have also switched from a named chunk to a function, which I find more idiomatic in R. That does not change the strange ordering, though.
If the warning message is not important in your case, you can use the workaround shown in the end: Suppressing the warning with the chunk option warning=FALSE restores the ordering.

flextable in Rmarkdown docx not printing within if statement if other text

I'm trying to use the package flextable to get some nicely formatted tables in my Rmarkdown (going to word file). The tables work just fine in general but if I put it within an if statement, if there is anything else being printed from the if statement I don't see the table. Any ideas what's going on?
Update Jan 2020 for any people still looking at this
As of version 0.5.5 of flextable there is a new function docx_value to address this, I have updated the answer to reflect this so that other people don't use the complicated workarounds now there is a simple solution.
My example (run all together) :
---
title: "Testing"
output:
word_document:
reference_docx: styles.docx
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
## R Markdown
```{r defaults}
library(pander)
library(knitr)
library(flextable)
```
1st test works fine - no if statement and new lines either side of table
## test 1 table no if statemnets
```{r test1, echo = FALSE, results = 'asis'}
test <- data.frame (c = 1:5, x = 6:10)
testft <- flextable(test)
testft
```
2nd test has an if statement with no other text and works fine
## test 2 if statement no other text
```{r test2, echo = FALSE, results = 'asis'}
RunTable <- TRUE
if(RunTable){
testft
}
```
But if I try and add other outputs in my if statement, either with or without new line breaks I don't get any table in my output
## test 3 if statement with other text
```{r test3, echo = FALSE, results = 'asis'}
#Hack so dat works up to year 2047 as cpp functions in padr can't handle data beyond 2038
#Get Daily Values
RunTable <- TRUE
if(RunTable){
print("before ")
testft
print("after ")
}
```
## test 4 if statement with other text and newlines
```{r test4, echo = FALSE, results = 'asis'}
RunTable <- TRUE
if(RunTable){
print("if with linebreak before ")
cat(" \n")
knit_print(testft)
cat(" \n")
print("if with linebreak after ")
}
```
Output:
You can use chunk option results = 'asis' and write the openxml content with format as following
## test 4 if statement with other text and newlines
```{r test4, echo = FALSE, results = 'asis'}
RunTable <- TRUE
if(RunTable){
print("if with linebreak before ")
cat(" \n")
cat(
paste(
"\n```{=openxml}",
format(testft, type = "docx"),
"```\n", sep = "\n")
)
cat(" \n")
print("if with linebreak after ")
}
```
Not sure if you would consider a different package, but this seems to work:
---
title: "Testing"
output:
word_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, fig.height=1.5, fig.width=3, fig.align='right', fig.align = "center")
```
## R Markdown
```{r defaults}
library(pander)
library(knitr)
library(flextable)
library(tableHTML)
```
## test 1 table no if statemnets
```{r test1, echo = FALSE}
test <- data.frame (c = 1:5, x = 6:10)
tab <- tableHTML(test, widths = c(60, 60), rownames = FALSE) %>% add_theme('scientific')
tab %>% tableHTML_to_image()
```
## test 2 if statement no other text
```{r test2, echo = FALSE}
RunTable <- TRUE
if(RunTable){
tab %>% tableHTML_to_image()
}
```
```{r test3, echo = FALSE}
#Hack so dat works up to year 2047 as cpp functions in padr can't handle data beyond 2038
#Get Daily Values
RunTable <- TRUE
if(RunTable){
print("before ")
tab %>% tableHTML_to_image()
print("after ")
}
```
## test 4 if statement with other text and newlines
```{r test4, echo = FALSE}
RunTable <- TRUE
if(RunTable){
print("if with linebreak before ")
cat(" \n")
tab %>% tableHTML_to_image()
cat(" \n")
print("if with linebreak after ")
}
For example, you can see test 4 as an output:
A couple of notes:
You can format the table in the exact way you want.
The code produces an image.
I presume your problem is related to this issue.
Changing the problematic chunks like this seems to work:
## test 3 if statement with other text
```{r test3, echo = FALSE}
RunTable <- TRUE
if(RunTable){
text <- c(
"before ",
knit_print(testft),
"after "
)
asis_output(paste(text, collapse = "\n"))
}
```
## test 4 if statement with other text and newlines
```{r test4, echo = FALSE}
RunTable <- TRUE
if(RunTable){
text <- c(
"if with linebreak before ",
" \\newline",
knit_print(testft),
" \\newline\n",
"if with linebreak after "
)
asis_output(paste(text, collapse = "\n"))
}
```
Regarding the last one:
I had to use \\newline to actually insert an extra blank line before the table.
I don't know why an extra \n is needed for the blank line after, it wouldn't work for me otherwise.
Just to test, I tried adding several \\newline entries, both before and after, but one blank line was the most I could get.
Update Jan 2020 for any people still looking at this
As of version 0.5.5 of flextable there is a new function docx_value to address this, as described in the package news:
flextable 0.5.5
new features
new function docx_value to let display flextables from non top level
calls inside R Markdown document.

Italicising the headings of a dataframe in rmarkdown

I would like to apply some latex-style formatting to column headings in a pander table in rmarkdown, knitting to pdf.
Notice in the toy document below the latex commands that work for the elements of the dataframe do not work for the headings. Specifically I would like (1) to be able to italicise some headings, (2) to be able to have headings with spaces between the letters (at the moment R automatically adds a .). However I am generally interested in how to get the headings in a dataframe to accept the same latex commands as the elements of the dataframe.
---
title: "Chapter 12: Adding to the Discrete Time Hazard Model"
output:
pdf_document: default
html_document: null
word_document: null
toc: yes
linestretch: 1.3
classoption: fleqn
header-includes:
- \setlength{\mathindent}{0pt}
- \setlength\parindent{0pt}
- \usepackage{amssymb}
---
```{r global_options, include=FALSE, echo = FALSE}
#this sets global knit options (i.e. options for the entire document. The following supresses any warnings from being include in the output and sets plot parameters. Note that setting dev to pdf allows us to set size of graphs easily
rm(list = ls())
knitr::opts_chunk$set(fig.width=12, fig.height=8, fig.path='Figs/',
echo=FALSE, warning=FALSE, message=FALSE, dev = 'pdf')
```
``` {r table p 446}
abC <- 0.3600344
bC <- 0.2455304
intC <- 0.4787285
dfTrans <- data.frame("Prototype" = c("$\\textit{Left/not Blue}$", "Left/Blue", "Right/not Blue", "Right/Blue"),
"$LEFT$" = c(0,1,0,1),
"$\\textit{BLUE}$" = c(0,0,1,1),
`Combined Parameter Estimates` = c(paste("0 x ", round(abC,4), "+ 0 x", round(bC,4), "+ 0 x", round(intC, 4), sep = " "), 8, 9, 0))
library(pander)
panderOptions('table.split.table', 300) # this forces the table to the width of the page.
pander(dfTrans, justify = "left")
```
I'm not sure how to do this with pander, but here is a method using the kable function from knitr and kableExtra functions for detailed table formatting. I haven't changed the yaml markup, but the updated code chunks are pasted in below, followed by the output.
```{r global_options, include=FALSE, echo = FALSE}
#this sets global knit options (i.e. options for the entire document. The following supresses any warnings from being include in the output and sets plot parameters. Note that setting dev to pdf allows us to set size of graphs easily
knitr::opts_chunk$set(fig.width=12, fig.height=8, fig.path='Figs/',
echo=FALSE, warning=FALSE, message=FALSE, dev = 'pdf')
# rm(list = ls()) This is unnecessary. knitr runs the rmarkdown document in a clean session.
library(knitr)
library(kableExtra)
options(knitr.table.format = "latex") # latex output (instead of default html)
library(tidyverse) # For dplyr pipe (%>%) and mutate
```
```{r table p 446}
abC <- 0.3600344
bC <- 0.2455304
intC <- 0.4787285
# I've removed the latex formatting from the data frame code
dfTrans <- data.frame(Prototype = c("Italic_Left/not Blue", "Left/Blue", "Right/not Blue", "Right/Blue"),
LEFT = c(0,1,0,1),
BLUE = c(0,0,1,1),
`Combined Parameter Estimates` = c(paste("0 x ", round(abC,4), "+ 0 x", round(bC,4), "+ 0 x", round(intC, 4), sep = " "), 8, 9, 0))
# Remove periods in column names
names(dfTrans) = gsub("\\.", " ", names(dfTrans))
# Two other options:
# 1. Use the data_frame function from tidyverse, rather than the base data.frame function.
# data_frame doesn't add periods, so you won't need to fix the column names afterwards.
# 2. Set check.names=FALSE in data.frame
# Use kableExtra cell_spec function to format on a cell-by-cell basis
dfTrans = dfTrans %>%
mutate(Prototype = cell_spec(Prototype, color=c("black","blue"),
align=rep(c("l","r"), each=2)))
# Format each of the column names using kableExtra text_spec
names(dfTrans)[1] = text_spec(names(dfTrans)[1], italic=TRUE)
names(dfTrans)[2] = text_spec(names(dfTrans)[2], align="l")
names(dfTrans)[3] = text_spec(names(dfTrans)[3], align="r", italic=TRUE, color="blue")
names(dfTrans)[4] = text_spec(names(dfTrans)[4], align="r")
# Output the table
kable(dfTrans, booktabs=TRUE, escape=FALSE)
```
One thing I'm not sure how to do yet is to format just the first value of dfTrans$Prototype as italic. cell_spec seems to use only the first value of an italic logical vector, so the following italicizes the whole column:
dfTrans = dfTrans %>%
mutate(Prototype = cell_spec(Prototype, color=c("black","blue"),
align=rep(c("l","r"), each=2),
italic=c(TRUE, rep(FALSE, n()-1))))
Here is a huxtable-based solution (my package):
abC <- 0.3600344
bC <- 0.2455304
intC <- 0.4787285
dfTrans <- data.frame(Prototype = c("Italic_Left/not Blue", "Left/Blue", "Right/not Blue", "Right/Blue"),
LEFT = c(0,1,0,1),
BLUE = c(0,0,1,1),
`Combined Parameter Estimates` = c(paste("0 x ", round(abC,4), "+ 0 x", round(bC,4), "+ 0 x", round(intC, 4), sep = " "), 8, 9, 0))
library(huxtable)
huxTrans <- hux(dfTrans, add_colnames = TRUE) # column names become first row
huxTrans[1, 4] <- 'Combined Parameter Estimates' # get rid of the dots
align(huxTrans)[4:5, 1] <- 'right'
text_color(huxTrans)[c(3, 5), 1] <- 'blue'
text_color(huxTrans)[1, 3] <- 'blue'
italic(huxTrans)[1, c(1, 3)] <- TRUE
huxTrans # will automatically become LaTeX in RMarkdown
quick_pdf(huxTrans)
Which looks like this in the terminal:
And this in PDF output:
You can add borders as well if you want.

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

edgebundle doesn't render plot when in loop in markdown

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

Resources