Reputation: 1513
My question is similar to this SO question but instead of creating a plot, I want to evaluate code chunk. I wanted to know if it's possible to programmatically generate headers and evaluate code chunks following their respective headers? My attempt at this is below.
I have a list
of data frames that I want to create a header and evaluate code chunk for each list
element. The example below is just to calculate the number of rows of each data frame using nrow()
.
```{r data}
data("iris","airquality","mtcars")
my_list <- list(iris,airquality,mtcars)
names(my_list) <- c("iris","airquality","mtcars")
```
```{r headers, results = 'asis'}
for (i in seq_along(my_list)) {
cat('#', names(my_list)[i], '\n')
cat('```{r}', '\n')
cat('nrow(mylist[[i]])') #evaluate any other code here
cat('\n')
cat('```')
cat('\n')
}
```
Any insight would be much appreciated!
Upvotes: 3
Views: 905
Reputation: 160687
Here's one way that I tackle a problem similar to this. I was given the hint here on SO but cannot find the answer ...
Two documents: one parent, one child. Think of the child document as a subroutine that can be called any number of times, and its output is saved (and concatenated to the character
vector out
). When I'm done in that loop, I use an inline `r code`
block.
The main document, the "parent" I guess:
---
output: md_document
---
```{r data}
data("iris","airquality","mtcars")
my_list <- list(iris,airquality,mtcars)
names(my_list) <- c("iris","airquality","mtcars")
```
```{r headers, echo = FALSE, include = FALSE, results = 'asis'}
out <- NULL
for (i in seq_along(my_list)) {
out <- c(out, knitr::knit_child("somedoc-child.Rmd"))
}
```
`r paste(out, collapse="\n")`
The child document, same directory, called as somedoc-child.Rmd
above, called by the parent doc. It sees the environment exactly as the parent leaves it, so my_list
and i
are seen perfectly.
# `r names(my_list[i])`
```{r, results="asis"}
nrow(my_list[[i]])
```
When I knit to markdown, I get:
data("iris","airquality","mtcars")
my_list <- list(iris,airquality,mtcars)
names(my_list) <- c("iris","airquality","mtcars")
iris
====
nrow(my_list[[i]])
\[1\] 150
airquality
==========
nrow(my_list[[i]])
\[1\] 153
mtcars
======
nrow(my_list[[i]])
\[1\] 32
Upvotes: 2
Reputation: 416
This behavior is because R cannot find your mylist
object within the new environment that is getting created by all the cat
calls. Also since mylist[[i]]
is an expression we would have to parse and evaluate it to get the final result.
For example -
```{r headers, results = 'asis'}
for (i in seq_along(my_list)) {
cat('#', names(my_list)[i], '\n')
cat('```{r}', '\n')
cat(eval(parse(text = 'i')))
cat('\n')
cat('```')
cat('\n')
}
```
The above code will give a result as follows (this is the result we need)-
# iris
```{r}
1
```
# airquality
```{r}
2
```
# mtcars
```{r}
3
```
But to do that with mylist
we would need to somehow make sure that mylist object is within the new environment before it can be evaluated. Also please note that people discourage the use of parse()
since it can lead to behaviors such as this. More here - Assigning and removing objects in a loop: eval(parse(paste(
This answer will work for your example but please do consider what I have mentioned and the discussion in the SO question -
```{r data}
data("iris","airquality","mtcars")
my_list <- list(iris,airquality,mtcars)
names(my_list) <- c("iris","airquality","mtcars")
```
```{r headers, results = 'asis'}
for (i in seq_along(my_list)) {
cat('#', names(my_list)[i], '\n')
cat('```{r}', '\n')
# somehow get the data or any other objects in here
# this works for trivial examples please be carefule before using it on actual code chunks
cat(eval(parse(text = paste(nrow(my_list[[i]])))))
cat('\n')
cat('```')
cat('\n')
}
```
# iris
```{r}
150
```
# airquality
```{r}
153
```
# mtcars
```{r}
32
```
Upvotes: 3