n. small
n. small

Reputation: 45

How to display a reactive plot in a shiny app and then pass it as a parameter to a markdown document?

My intention is to explore some data visually using a shiny app and then to pass the resulting plot to a Markdown document. My question is similar to these two I found on stackoverflow.

How to pass reactive data as a R markdown parameter?

How to pass table and plot in Shiny app as parameters to R Markdown?

Unfortunately I am not able to figure out how to use the answers provided to solve my problem. I assume I do not understand the functionality of reactive values enough.

In the MWE I use an input slider in order to create some random numbers that should be displayed in the shiny app. Once the plot has been created in the shiny app I need it to be embedded into the Markdown document. Passing the plot as a parameter to the Markdown does not create an error, however the parameter can not be accessed (seems not to exist) in the Markdown document. But if I cancel out the code in the shiny app to display the plot in the app directly, the plot can be passed to the Markdown document as a parameter and be displayed there.

I understand that I could recreate the plot in the Markdown document, as explained in How to pass a plot (renderPlot) from shiny app as parameter to R Markdown?. Still I would like to understand if there is a solution to pass the plot without recreating it.

In order to do so I must understand why displaying the plot in the shiny app prevents it to be passed to the Markdown document. What is it that I do not understand? Please help! Thank you!

MWE: server.R

library(shiny)

shinyServer(function(input, output) {

    # create a scatter plot of random numbers
    # based on value of input slicer
    chart <- reactive({
        plot(x = 1:input$dots, y =  rnorm(n = input$dots),
             xlab = "Numbers", ylab = "Random")
    })
    

    # Display the scatter plot in shiny app
    # in reaction to actionButton display
    observeEvent(eventExpr = input$display,
                 output$dotPlot <- renderPlot({
                 chart()
                  })
    )
    
    # Create R-Markdown Report with plot and
    # value of input slider as paramters
    output$report <- downloadHandler(
        filename = "Dot_Report.html",
        
        content = function(file) {
        tempReport <- file.path(tempdir(), "Dot_Report.Rmd")
        file.copy(from = "Dot_Report.Rmd", 
                  to = tempReport, 
                  overwrite = TRUE)
        
        pars <- list(n = input$dots,
                     random_plot = reactive(chart()))

        rmarkdown::render(input = tempReport, 
                          output_file = file,
                          params = pars,
                          envir = new.env(parent = globalenv()))
    })

})

MWE: ui.R

library(shiny)

# Define UI for application that displays a Scatter Plot
shinyUI(fluidPage(

    # Title
    titlePanel("Simple Plot Export"),

    # Sidebar with a slider input for number of random numbers
    sidebarLayout(
        sidebarPanel(
            
            sliderInput("dots",
                        "Number of Random dots:",
                        min = 1,
                        max = 50,
                        value = 30)
        ,
        actionButton("display", "Display Plot"),
        
        downloadButton("report", "Generate Report")
    ),
        mainPanel(
            plotOutput("dotPlot")
        )
    )
))

MWE: Dot_Report.Rmd

---
title: "Simple Scatter Plot"
output: html_document
params: 
  n: NA
  random_plot: NA
---


```{r, echo = F}

params$random_plot()

Upvotes: 1

Views: 979

Answers (1)

ismirsehregal
ismirsehregal

Reputation: 33540

The problem here is, that base plots directly draw on a device.

Please see this related answer for workarounds.

Your code is working fine once we switch to e.g. ggplot (only server.R needs to be modified):

library(shiny)
library(ggplot2)

shinyServer(function(input, output) {
  
  # create a scatter plot of random numbers
  # based on value of input slicer
  chart <- reactive({
    DF <- data.frame(x = 1:input$dots, y = rnorm(n = input$dots))
    ggplot(data = DF, aes(x = x, y = y)) + geom_point() + xlab("Numbers") + ylab("Random")
  })
  
  
  # Display the scatter plot in shiny app
  # in reaction to actionButton display
  observeEvent(eventExpr = input$display,
               output$dotPlot <- renderPlot({
                 chart()
               })
  )
  
  # Create R-Markdown Report with plot and
  # value of input slider as paramters
  output$report <- downloadHandler(
    filename = "Dot_Report.html",
    
    content = function(file) {
      tempReport <- file.path(tempdir(), "Dot_Report.Rmd")
      file.copy(from = "Dot_Report.Rmd", 
                to = tempReport, 
                overwrite = TRUE)
      
      pars <- list(n = input$dots,
                   random_plot = reactive(chart()))
      
      rmarkdown::render(input = tempReport, 
                        output_file = file,
                        params = pars,
                        envir = new.env(parent = globalenv()))
    })
  
})

Edit: Using base plot() along with recordPlot()

server.R:

 # create a scatter plot of random numbers
  # based on value of input slicer
  chart <- reactive({
    plot(x = 1:input$dots, y =  rnorm(n = input$dots),
    xlab = "Numbers", ylab = "Random")
    recordPlot()
  })

Some related information - see ?dev.new and ?dev.copy:

Only one device is the ‘active’ device: this is the device in which all graphics operations occur. There is a "null device" which is always open but is really a placeholder [...]

Upvotes: 1

Related Questions