Dynamic Number of Calls to a Chunk with Knitr

Dynamic number of calls to a chunk with knitr

Below is the RMarkdown solution analogous to https://github.com/yihui/knitr-examples/blob/master/020-for-loop.Rnw, using knit_child(). As my solution, it requires two files, but it is much more clear.

mainfile.Rmd:

```{r, echo=FALSE}
J <- length(lSlopes)
```

```{r runall, include=FALSE}
out <- NULL
for (i in 1:J) {
out <- c(out, knit_child('stepfile.Rmd'))
}
```

`r paste(out, collapse = '\n')`

Nice!

stepfile.Rmd:

```{r, echo=FALSE}
lSlopes[[i]]
```

Generate Dynamic R Markdown Blocks

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

Create RMarkdown chuncks in a loop

If you want to create headings + outputs within a loop, you can do:

```{r species_loop, results='asis'}
for(i in c("setosa", "versicolor", "virginica")) {

cat(paste0("\n\n## ", i, "\n"))

p <- df %>%
dplyr::filter(Species == i) %>%
ggplot2::ggplot(ggplot2::aes(Sepal.Length, Petal.Length)) +
ggplot2::geom_point()
print(p)
}
```

So:

  • Using results='asis' to allow output that you cat() to be interpreted as Markdown syntax
  • cat()ing the required markdown syntax to produce the headers (surrounded by some newlines to make sure it's interpreted properly)
  • Explicitly print()ing the plot within the loop.

R markdown: Accessing variable from code chunk (variable scope)

Yes. You can simply call any previously evaluated variable inline.

e.g. If you had previously created a data.frame in a chunk with df <- data.frame(x=1:10)

`r max(df$x)`

Should produce

10

R knitr: Possible to programmatically modify chunk labels?

For anyone else who comes across this post, I wanted to point out that @Yihui has provided a formal solution to this question in knitr 1.0 with the introduction of the knit_expand() function. It works great and has really simplified my workflow.

For example, the following will process the template script below for every level of mtcars$cyl, each time replacing all instances of {{ncyl}} (in the template) with its current value:

# My report

```{r}
data(mtcars)
cyl.levels <- unique(mtcars$cyl)
```

## Generate report for each level of cylinder variable
```{r, include=FALSE}
src <- lapply(cyl.levels, function(ncyl) knit_expand(file = "template.Rmd"))
```

`r knit(text = unlist(src))`

Template:

```{r, results='asis'}
cat("### {{ncyl}} cylinders")
```

```{r mpg-histogram-{{ncyl}}cyl}
hist(mtcars$mpg[mtcars$cyl == {{ncyl}}],
main = paste({{ncyl}}, "cylinders"))
```

```{r weight-histogam-{{ncyl}}cyl}
hist(mtcars$wt[mtcars$cyl == {{ncyl}}],
main = paste({{ncyl}}, "cylinders"))
```

How to set knitr chunk output width on a per chunk basis?

Though not a solution to the overall question, your first complaint with your code is that it gums up the global environment with your .width variable. This can be resolved using local() as a closure mechanism, encapsulating your variable so that you get no collisions in global var space.

So, if you replace your knit_hooks$set call with:

knit_hooks$set(width=local({
.width <- 0
function(before, options, envir) {
if (before) .width <<- options(width=options$width)
else options(.width)
}
}))

it produces the same results without the problem of forcing .width into the global environment. The rest of your code above works as before with identical output.

More can be read at help(local), in Advanced R Programming (Hadley Wickham), and there are several examples of it in the wild, such as @JeroenOoms' OpenCPU.

R markdown rerun the same section of report with different parameter

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 Topics



Leave a reply



Submit