EJJ
EJJ

Reputation: 1513

programmatically create headers and evaluate following code chunks in markdown

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

Answers (2)

r2evans
r2evans

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

Suhas Hegde
Suhas Hegde

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

Related Questions