0

I am new in R-Markdown and I have problem with rendering plotly objects in HTML report. Problem is with for loop. When I create graphs inside for loop they are not visible in final report. I found solution for single loop: LINK What about situation in double loop? I can't convert the solution from a single loop into, for example, 2 nested loops. To sum up:

  1. Single FOR LOOP
  • plot invisible:

    plots <- list()
    for(i in 1:3) {
       l[[i]] <- plot_ly(x = rnorm(10))
    }
    l
    
  • plot visible:

    l <- htmltools::tagList()
    for (i in 1:3) {
      l[[i]] <- plot_ly(x = rnorm(10))
    }
    l
    
  1. Double FOR LOOP
  • plot invisible:

    for(j in 1:3){
    # some text, tables depends of j
    # transform dataset based on `j`
      l <- htmltools::tagList()
      for (i in 1:3) {
         l[[i]] <- plot_ly(x = rnorm(10))
      }
      l # <-plot on the end of each j loop, not on the end of whole loop like was in single for loop
    }
    
  • plot visible: ??

Any idea how to solve that problem? I will be very grateful for any help!

EDIT: example of code

#' ---
#' title: "Sample Document"
#' output:
#'   html_document:
#'   toc: true
#' theme: united
#' ---

#' R Markdown report of graphs generated with a double (nested) for loop

df <- data.frame(
  j = numeric(),
  desc = character()
)

for(j in c(1:3)){
  cat(paste0("# below we present visualizations for iteration no.: ", j, "\n"))
  
  #' Random table
  #+ echo=FALSE, results = 'asis'
  df <- rbind(df, data.frame(j = j, desc = paste0("iteration no.: ", j)))
  print(knitr::kable(df, row.names = F))
  
  l <- htmltools::tagList()
  for(i in (1:3)){
    l[[i]] <- plot_ly(x = j*rnorm(i*10))
    print(l[[i]])
  }
}
tomsu
  • 371
  • 2
  • 16
  • The reason why the first piece of code works is because of R's auto-printing feature: top-level values are automatically printed, i.e. having `l` at the top level inside code is as if you had written `print(l)`. And `htmltools::tagList` overrides `print` to display the plot. The second piece of code does not work because `l` is *not* at the top level, hence it isn't auto-printed. – Konrad Rudolph Apr 12 '23 at 07:18
  • @KonradRudolph hmm, then how to make the plots print after each iteration of `j`? Because from what I understand they will never be top level, does that mean it can't be done? – tomsu Apr 12 '23 at 07:26
  • You can always call `print(l)` (or `plot(l)`, which makes more sense!) explicitly. However, stefan's answer shows a potentially better approach. You can extend that approach to also include text and tables, you just have to construct the entire output progressively. – Konrad Rudolph Apr 12 '23 at 07:32
  • I can't call `plot(l)` because I get error: `Error in xy.coords(x, y, xlabel, ylabel, log) : 'x' is a list, but does not have components 'x' and 'y'` – tomsu Apr 12 '23 at 07:55
  • Ah, right, you need to call `plot` on the individual *element* of the list. If you do that you don't need `l` at all (since you don't need to store the plot objects after plotting them). – Konrad Rudolph Apr 12 '23 at 08:09
  • you mean: `l <- htmltools::tagList() l[[1]] <- plot_ly(x = rnorm(10)) plot(l[[1]])` If yest, i get the same error – tomsu Apr 12 '23 at 08:22
  • Ah. Apparently 'plotly' does not define an appropriate `plot` function. That's an unfortunate decision on their part, it might even be worth reporting a bug. So you need to use `print` instead of `plot`. – Konrad Rudolph Apr 12 '23 at 08:44
  • @KonradRudolph look at main post, I added example of code with problem that I want to solve. – tomsu Apr 12 '23 at 09:00

1 Answers1

2

Basically you could achieve this with the same approach by wrapping the outer loop in a second tagList:

---
title: "Untitled"
output: html_document
date: "2023-04-12"
---

```{r echo=FALSE, message=FALSE}
library(plotly)
```

# Using `lapply`
```{r message=FALSE}
htmltools::tagList(
  lapply(1:2, function(j) {
    l <- htmltools::tagList()
    for (i in 1:2) {
      l[[i]] <- plot_ly(x = rnorm(10)) |>
        layout(title = paste(j, i, sep = "."))
    }
    l
  })
)
```

# Using a `for` loop

```{r message=FALSE}
ll <- htmltools::tagList()

for (j in 1:2) {
  l <- htmltools::tagList()
  for (i in 1:2) {
    l[[i]] <- plot_ly(x = rnorm(10)) |>
      layout(title = paste(j, i, sep = "."))
  }
  ll[[j]] <- l
}
ll
```

enter image description here

EDIT Adapting my answer on this post to your case you could achieve your desired result like so. The important step is to make sure that the JS dependencies are included. Afterwards you could print the plots created in the inner loop using print.

---
title: "Untitled"
output: 
  html_document:
    toc: true
    theme: united
date: "2023-04-12"
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(message = FALSE)
```


```{r echo=FALSE}
library(plotly)
```

```{r include=FALSE}
df <- data.frame(
  j = numeric(),
  desc = character()
)
```

```{r, include=FALSE}
# Init Step to make sure that the dependencies are loaded
htmltools::tagList(plot_ly(x = rnorm(10), type = "histogram"))
```

```{r results='asis', echo=FALSE}
for (j in 1:2) {
  cat(paste0("# below we present visualizations for iteration no.: ", j, "\n"))

  df <- rbind(df, data.frame(j = j, desc = paste0("iteration no.: ", j)))
  print(knitr::kable(df, row.names = F))

  l <- htmltools::tagList()
  for (i in 1:2) {
    l[[i]] <- plot_ly(x = j * rnorm(i * 10))
  }
  print(l)
}
```

enter image description here

stefan
  • 90,330
  • 6
  • 25
  • 51
  • Thank you for your response. My situation is a bit different because inside loop `j` I have text, tables etc and graphs generated in loop `i`. To sum up, I would like to achieve a situation where I display graphs with each iteration 'j', and not as a summary of all interactions at the end, as in the solution proposed by you. – tomsu Apr 12 '23 at 07:23
  • Already guessed that. (: Perhaps you could create a minimal example of what you are trying to achieve. Makes it easier to figure out a solution for your use case. (; – stefan Apr 12 '23 at 07:51
  • look at main post, I added example of code with problem that I want to solve – tomsu Apr 12 '23 at 08:59
  • See my edit for an approach to achieve your desired result. – stefan Apr 12 '23 at 09:18