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 youcat()
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
Standard Deviation on Dataframe Does Not Work
How to Do Histograms of This Row-Column Table in R Ggplot
Trouble with Strings with <U+0092> Unicode Characters
Pipe in Magrittr Package Is Not Working for Function Load()
R Find Overlap Among Time Periods
R: How to Count How Many Points Are in Each Cell of My Grid
Get Plot() Bounding Box Values
How to Manually Set Colours to a Categorical Variables Using Ggplot()
Error: Attempt to Use Zero-Length Variable Name
Processing The Input File Based on Range Overlap
Make a Column with Duplicated Values Unique in a Dataframe
R Package Conflict Between Gam and Mgcv