Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

offline plots in for loops #273

Closed
chriddyp opened this issue Aug 22, 2015 · 15 comments
Closed

offline plots in for loops #273

chriddyp opened this issue Aug 22, 2015 · 15 comments
Labels

Comments

@chriddyp
Copy link
Member

a current workaround is:

```{r, results="asis"}
for(i in 1:3) {
    fig <- list(data=list(list(x=rnorm(100), type="histogram")), layout=list(title=paste(c('plot', i, sep=" "))))
    cat(knit_print.offline(fig %>% offline))
}
```

maybe there is an easier way?

@cpsievert
Copy link
Collaborator

It looks like ggvis suffers from the same problem. I have a feeling it's because knitr can only call knit_print() once per chunk. I suppose my advice would be to use subplot() instead

```{r}
n <- 10
df <- data.frame(
  x = c(replicate(3, rnorm(n))),
  plot = rep(1:3, each = n)
)
df %>%
  plot_ly(x = x, group = plot, xaxis = paste0("x", plot), 
          type = "histogram") %>% 
  subplot() %>% offline()
```

@cpsievert
Copy link
Collaborator

Looks like @yihui will provide a solution for us 😊

@yihui
Copy link

yihui commented Feb 5, 2016

Here is the easy way:

```{r}
res <- lapply(1:3, function(i) {
    fig <- list(data=list(list(x=rnorm(100), type="histogram")), layout=list(title=paste(c('plot', i, sep=" "))))
    fig %>% offline
})
htmltools::tagList(res)
```

@cpsievert
Copy link
Collaborator

cpsievert commented Mar 12, 2016

offline() was deprecated when plotly.js went open source.

Here is the current way to do this:

```{r}
library(plotly)
htmltools::tagList(lapply(1:3, function(x) { plot_ly(x = rnorm(10)) }))
```

Or, if you prefer a for loop:

```{r}
l <- htmltools::tagList()
for (i in 1:3) {
  l[[i]] <- plot_ly(x = rnorm(10))
}
l
```

@GegznaV
Copy link

GegznaV commented May 4, 2016

Nice workarround. Unfortunately, I'm not able to use it in my current automated analysis where in each iteration of for loop plotly graphs are followed by tables, results of some statistical tests, etc. Printing all plots in one place would be confusing and not using for loop would mean, that analysis is not automated.
Now it is not clear for me is it a bug/limitation in knitr or in plotly, as I noticed that result of pander() also does not print from inside for loop in .Rmd file?

@timelyportfolio
Copy link
Collaborator

@GegznaV, what are you using to make the tables? Do you have a small code sample? This can still be made to work with a combination of plotly and tables.

@GegznaV
Copy link

GegznaV commented May 7, 2016

simple example of .Rmd document:

---
title: "Untitled"
output: html_document
---
library(ggplot2)
library(plotly)
library(magrittr)
library(pander)
for(i in c("vs", "am", "gear", "carb")){

     print(i)

    mtcars$am <- as.factor(mtcars[[i]])
    # plot 1
    {ggplot(mtcars,aes(x = mpg, y = disp, color = am)) + geom_point()}  %>%
        ggplotly

    # plot 2
    {ggplot(mtcars,aes(x = mpg, fill = am)) + geom_density(alpha = .3)}  %>%
        ggplotly

    # table 1
    summary(mtcars[,c("mpg","disp",i)]) %>% pander

    # table 2: Kruskal-Wallis test
    kruskal.test(mtcars$mpg, mtcars[[i]])  %>% pander

}

May be it's possible to save images as .html files and insert them as in this example (section "Slidify"):
https://github.com/garthtarr/pairsD3#slidify

@timelyportfolio
Copy link
Collaborator

timelyportfolio commented May 9, 2016

@GegznaV, this is a hack in the ugliest of forms, but it does work. Perhaps, @jcheng5, @ramnath_vaidy, or @yihui can demonstrate an easier way.

        ---
        title: "Untitled"
        output: html_document
        ---

        ```{r results='asis'}
        library(ggplot2)
        library(plotly)
        library(magrittr)
        library(htmltools)
        library(pander)

        # turn off pander auto asis
        pander::panderOptions('knitr.auto.asis', FALSE)

        # create a container for our output
        output <- list()

        # loop through columns
        for(i in c("vs", "am", "gear", "carb")){
          # get column
          mtcars$am <- as.factor(mtcars[[i]])

          # plot 1
          output[[length(output) + 1]] <- {
            ggplot(mtcars,aes(x = mpg, y = disp, color = am)) + geom_point()
          }  %>%  ggplotly %>%  as.widget

              # plot 2
          output[[length(output) + 1]] <- {
            ggplot(mtcars,aes(x = mpg, fill = am)) + geom_density(alpha = .3)
          }  %>% ggplotly %>%  as.widget

          output[[length(output) + 1]] <-  paste0(
            capture.output(summary(mtcars[,c("mpg","disp",i)]) %>% pander),
            collapse="\n"
          )
        }

        # now cat/print all of the content
        for(j in 1:length(output)){
          x <- output[[j]]
          #  we know only character and htmlwidget in this case
          #   if more need to handle appropriately
          if(inherits(x,"character")){
            # noquote critical here
            #  also turn off auto.asis very important
            cat(noquote(paste0(x,collapse="\n")))
          } else {
            # print the html piece of the htmlwidgets
            cat(renderTags(x)$html)
          }
        }

        ```

        ```{r echo=FALSE, messages=FALSE, warning=FALSE}
        # attach the Dependencies
        # since the do not get included with renderTags(...)$html
        deps <- lapply(
          Filter(function(x){inherits(x,"htmlwidget")},output),
          function(hw){
            renderTags(hw)$dependencies
          }
        )

        attachDependencies(
          tagList(),
          unlist(deps,recursive=FALSE)
        )
        ```

@Henry-E
Copy link

Henry-E commented Jul 12, 2016

#273 (comment)

@timelyportfolio For printing out html widgets and other things created in a for loop, this process is incredibly involved. Still, it is the closest fit to how I've been using rmarkdown to create dynamic documents. If I want to extend the example given to new options, like datatables or character output without a table in it, what's the best way to figure out what I should looking to put into the print statement? How do you debug such things when testing them out.

Edit: Ok, I think I have datatables figured out. They work the same as ggplotly objects when stored as widgets. By the way thanks for posting this solution.

@wetlandscapes
Copy link

#237 (comment)

Thank you @timelyportfolio for your solution. I was trying to get some ggplotly outputs rendered as tabsets from an rmarkdown to html_output, but couldn't figure it out. It was driving me mad. I was able to plot everything with modified form to your solution. Cheers!

@ghost
Copy link

ghost commented Sep 13, 2019

Quite an easy way to add tables and plots (and more plots and titles etc.) in a loop in RMarkdown is:

```{r, results='asis'}

htmltools::tagList(plot_ly(x = rnorm(10), type = "histogram"))

for (i in 1:3) {
  
  cat(summary(mtcars[,i]) %>% pander::pander())
 
  cat("\n\n")
  
  print(htmltools::tagList(plot_ly(x = rnorm(10), type = "histogram")))
  
  cat("\n\n")
}

You only need to watch out that the plotly libs get included. Therefore I first create a plot outside the loop.

@pczhang
Copy link

pczhang commented Mar 14, 2021

Dear all,

I am trying an approach combining solutions from @timelyportfolio and @ghost.

test.Rmd below

library(plotly)
plotlist = list()

for (i in 1:3) {
    cat("# Section ", i, "\n\n")
    p <- plot_ly(x = rnorm(10), type = "histogram")
    plotlist[[i]] = p
    print(htmltools::tagList(p))
    cat("\n\n")
}
deps <- lapply(
    Filter(function(x){inherits(x,"htmlwidget")}, plotlist),
    function(hw){
        htmltools::renderTags(hw)$dependencies
    }
)

htmltools::attachDependencies(
               htmltools::tagList(),
               unlist(deps,recursive=FALSE)
           )

The plots will be shown when I do render("test.Rmd") and open test.html from my browser. However, it won't appear when I put the Rmd file in the shiny server. I wonder what is the difference between these two. Any way to modify it so it will also work in the shiny server.

Thanks!

@mayank7j-shell
Copy link

Hi @yihui, @cpsievert and everyone,

Thank you for your quick fix with htmltools::tagList. This works as expected in the normal flow of things - creating plots within for loop and then using tagList to print them in reports.

However, when we combine this with native collapsible sections in markdown, the plotly plots don't render and just churn out empty div under the collapsible sections.

However, as proposed above, printing 1 plotly plot outside the collapsible sections renders them correctly. However, with our current implementation we want to have all the sections collapsible in the rmd report.

Any suggestions to solve this is highly appreciated. Thank you so much for your time.

@HDash
Copy link

HDash commented May 31, 2024

#273 (comment)

@mayank7j-shell Simply add the following chunk at the start of your RMarkdown document-

```{r include = FALSE}
htmltools::tagList(plot_ly(x = rnorm(10), type = "histogram"))
```‎ 

This ensures the Plotly JS dependencies are loaded before Plotly functions are called in a for loop.
(Source: StackOverflow)

@mayank7j-shell
Copy link

Hi @HDash

Thank you so much for your assistance and quick reply. It works.

Best Regards,
Mayank

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants