34

i want to create an app using shiny that dynamically add plots to the page. it could be 10 plots and it could be only one. i'm using this tutorial in the shiny homepage for dynamic UI.

this is a simplified example. the function showme is ploting the graph

server.r

shinyServer(function(input, output) {
  # Create an environment for storing data
  symbol_env <- new.env()
  # Make a chart for a symbol, with the settings from the inputs
  make_chart <- function(symbol) {
    showme(symbol)
  }

  display <- c("1083484" , "1101732")

  output$MyList <- renderUi({ 
    for (i in i:nrow(display))
       renderPlot({make_chart(display[i])})
    })
})

ui.r

shinyUI(pageWithSidebar(
  headerPanel("My Plots !"),
  sidebarPanel(
    wellPanel(
      p(strong("Scan1"))))
 ,mainPanel(
      uiOutput("MyList")
)))

i'm getting the following error

Listening on port 8100
Error in .subset2(x, "impl")$defineOutput(name, value, deparse(substitute(value))) : 
  Unexpected character output for display

if this is not the way - i would appreciate any guidance. Thanks.

> sessionInfo()
R version 2.15.3 (2013-03-01)
Platform: i386-w64-mingw32/i386 (32-bit)
haki
  • 9,389
  • 15
  • 62
  • 110

3 Answers3

31

Perhaps this example due to Winston Chang is helpful:

Shiny example app with dynamic number of plots

Here is the full example just in case of linkrot:

server.R

max_plots <- 5

shinyServer(function(input, output) {

# Insert the right number of plot output objects into the web page
output$plots <- renderUI({
  plot_output_list <- lapply(1:input$n, function(i) {
     plotname <- paste("plot", i, sep="")
     plotOutput(plotname, height = 280, width = 250)
   })

   # Convert the list to a tagList - this is necessary for the list of items
   # to display properly.
   do.call(tagList, plot_output_list)
})

# Call renderPlot for each one. Plots are only actually generated when they
# are visible on the web page.
for (i in 1:max_plots) {
    # Need local so that each item gets its own number. Without it, the value
    # of i in the renderPlot() will be the same across all instances, because
    # of when the expression is evaluated.
    local({
        my_i <- i
        plotname <- paste("plot", my_i, sep="")

        output[[plotname]] <- renderPlot({
        plot(1:my_i, 1:my_i, xlim = c(1, max_plots), ylim = c(1, max_plots), main = paste("1:", my_i, ".  n is ", input$n, sep = ""))
        })
    })
}
})

ui.R

shinyUI(pageWithSidebar(

  headerPanel("Dynamic number of plots"),

    sidebarPanel(
      sliderInput("n", "Number of plots", value=1, min=1, max=5)
    ),

    mainPanel(
      uiOutput("plots") # This is the dynamic UI for the plots
    )
))
Matthew Plourde
  • 43,932
  • 7
  • 96
  • 113
Rahul Savani
  • 872
  • 1
  • 10
  • 24
  • 1
    Thanks man - this is what i was looking for. i managed to get a dynamic list of plots drawn but, i want to print out a list of objects - each object contains a header , a plot and a table. do you know how can i do that ? – haki May 25 '13 at 09:03
  • 1
    Do you mean that a) for each chosen object you want to plot all three things (header, plot, and table), or b) for each chosen object, you want to then choose which of those three to plot (or do you mean something else)? – Rahul Savani May 25 '13 at 10:09
  • a - for each object i want a header, a plot and a summary table. the dynamic UI should by a container of some sort and not just a plot. i've tried adding tables to the taglist and to the output using `renderTable` but it's displaying only the last one added - in my case, the table. – haki May 25 '13 at 14:40
  • 3
    Hmm this doesn't seem to work with ggplot, any ideas? – evolvedmicrobe May 06 '14 at 21:46
  • 1
    @RahulSavani: Can you make `max_plots` a reactive value instead of hard coded? Thanks – TTT Nov 14 '14 at 15:16
  • for ggplot2 plotting, – Zhilong Jia Jan 04 '15 at 22:46
  • @RahulSavani I need b. (asked a dedicated question here: http://stackoverflow.com/questions/29790915/using-variable-number-of-input-fields-in-shiny-page ) – Uri Barenholz May 03 '15 at 14:07
  • @tao.hong Instead of using `max_plots` you can put the whole `for` loop inside an `observe` and use `input$max_plots` for reactivity – Rodrigo Zepeda Dec 17 '15 at 02:30
6

To answer the question in the comment above, about how to dynamically return a list of objects (for instance, a plot, a table and a text), you can generate a list of appropriate outputs contained in a div tag in the renderUI you then match with the appropriate render functions in the for loop. For instance:

max_plots <- 5

shinyServer(function(input, output) {

# Insert the right number of plot output objects into the web page
output$plots <- renderUI({
  plot_output_list <- lapply(1:input$n, function(i) {
     plotname <- paste("plot", i, sep="")
     plottitle <- paste("plottitle", i, sep="")
     tablename <- paste("tablename", i, sep="")
     tags$div(class = "group-output",
       textOutput(plottitle, container = h3),
       plotOutput(plotname, height = 280, width = 250),
       tableOutput(tablename)
     )
   })

   # Convert the list to a tagList - this is necessary for the list of items
   # to display properly.
   do.call(tagList, plot_output_list)
})

# Call renderPlot for each one. Plots are only actually generated when they
# are visible on the web page.
for (i in 1:max_plots) {
    # Need local so that each item gets its own number. Without it, the value
    # of i in the renderPlot() will be the same across all instances, because
    # of when the expression is evaluated.
    local({
        my_i <- i
        plotname <- paste("plot", my_i, sep="")
        plottitle <- paste("plottitle", my_i, sep="")
        tablename <- paste("tablename", my_i, sep="")

        output[[plotname]] <- renderPlot({
        plot(1:my_i, 1:my_i, xlim = c(1, max_plots), ylim = c(1, max_plots), main = paste("1:", my_i, ".  n is ", input$n, sep = ""))
        })
        output[[plottitle]] <- renderText({paste("1:", my_i, ".  n is ", input$n, sep = "")
        })
        output[[tablename]] <- renderTable({table(x = 1:my_i, y = 1:my_i)
        })
    })
}
})

I hope that helps!

skasch
  • 101
  • 1
  • 6
1

Dynamically add N plots as you want using shiny:

runApp(
list(
ui = fluidPage(
  headerPanel('Tittle'),

  sidebarPanel(
    fileInput('file1', 'Choose File:'),
    textInput("target", label="Target", value="Choose target"),
    actionButton("addButton", "Go!"),
    p('The button start the code server'),
    p("This is UI")

  ),

  mainPanel(

    uiOutput("plots")
  )),
#SERVER
server = function(input, output) {     
  dataset <- eventReactive(input$addButton, {

    #Obtain the file and textinput
    inFile <- input$file1
    df <- read.csv(inFile$datapath)
    df$target <- input$target
    return(df)

  })

  output$plots <- renderUI({


    df <- dataset()
    n <- df$target[1]

    plot_output_list <- lapply(1:n, function(i) {

      plotname <- paste("plot", i, sep="")
      plotOutput(plotname, height = 580, width = 550)


    })


    do.call(tagList, plot_output_list)


  })
  observe({             

    for (i in 1:length()) {
      local({ 


        plotname <- paste("plot", i, sep="")

        output[[plotname]] <- renderPlot({

          #function_plot is the function generate plot
          function_plot()

        })
      })#endlocal
    }

  })
}
  )
)

https://github.com/ericbellet/recomendacion-modelos/blob/master/shiny/generate_rocSHINY.R