Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shiny - use results of function call in observe in output

Tags:

r

shiny

I have a Shiny application where based on selected options (columns) dataset is recalculated dynamically. Result of recalculation is used for displaying another set of options to user and to create a plot.

Currently I'm using observe() to read user selection, recalculate dataset and update UI. However, when displaying output (plot), I have to do the recalculation again, as observe() does not return anything.

Is there a way how to recalculate the dataset only once?

I've created simple example to illustrate this:

library(devtools)
library(shiny)
runGist('7333949') 

In server.R I would like to have one call to custom function AggregateData in observe() and no call in renderUI().

App code:

server.R:

# shiny server side code for each call
shinyServer(function(input, output, session){
     #update variable and group based on dataset
     observe({

          require(sqldf)

          if (is.null(input$source_columns)) {
               obj <-TestData
          } else {
               obj<-AggregateData(TestData,Columns=input$source_columns)
          }

          var.opts<-namel(colnames(obj))
          var.opts.original.slicers <- namel(colnames(TestData))           
          measures <- c('m1','m2','m3','m4','m5')

          var.opts.slicers <- var.opts[!(var.opts %in% c(measures,'x'))]
          var.opts.original.slicers <- var.opts.original.slicers[!(var.opts.original.slicers %in% c(measures,'x'))]
          var.opts.measures <- var.opts[var.opts %in% measures]

          updateSelectInput(session, "source_columns", choices = var.opts.original.slicers, selected=var.opts.slicers)
          updateSelectInput(session, "xaxis", choices = var.opts.slicers,selected="x")
          updateSelectInput(session, "yaxis", choices = var.opts.measures,selected="m1")

     })

     output$plot <- renderUI({
          plotOutput("p")
     })

     #plotting function using ggplot2

     output$p <- renderPlot({
          require(ggplot2)
          obj <- AggregateData(TestData,Columns=input$source_columns)
          p <- PlotData(obj,x=input$xaxis, y=input$yaxis)
          print(p)
     })     
})

ui.R:

shinyUI(pageWithSidebar(
     # title
     headerPanel("Analysis setup"),

     #input
     sidebarPanel
     (
          selectInput("source_columns","Source Columns:", "Loading...",multiple=TRUE),
          selectInput("xaxis","X Axis:", "Loading..."),
          selectInput("yaxis","Y Axis:", "Loading...")
     ),     

     # output               
     mainPanel(
          #h3('Vintage Analysis'),
          uiOutput("plot") # depends on input 
     )
))

global.R:

#initialize
library(ggplot2) 

TestData <- data.frame(   a = rep(LETTERS[1:4],10),
                          b = rep(c('A','B'),20),
                          c = rep(LETTERS[1:5],each=8),
                          d = rep(c('A','B'),2,each=10),
                          m1 = rnorm(40),
                          m2 = rnorm(40),
                          m3 = rnorm(40),
                          m4 = rnorm(40),
                          m5 = rnorm(40),
                          x  = rep(1:5,each=8)
)

#helper function (convert vector to named list)
namel<-function (vec){
     tmp<-as.list(vec)
     names(tmp)<-as.character(unlist(vec))
     tmp
}

# Function to aggregate data based on selected columns (Source Columns)
AggregateData <- function(data,Columns=NA) {

     require(sqldf)
     if (all(is.na(Columns))) {
          sql <- "select 
                    sum(m1) as m1, sum(m2) as m2, sum(m3) as m3, sum(m4) as m4, sum(m5) as m5, x
                 from TestData group by x" 
          sqldf(sql)                
     } else {
          sql <- paste("select ", paste(Columns, collapse =','), ",
                    sum(m1) as m1, sum(m2) as m2, sum(m3) as m3, sum(m4) as m4, sum(m5) as m5, x
                 from TestData group by ",paste(Columns, collapse =','),", x")
          sqldf(sql)          
     }
}

# Function to plot data
PlotData <- function(data,x="x",y="m1") { 
     ggplot(data, aes_string(x=x, y=y)) + geom_line()
}
like image 790
Tomas Greif Avatar asked Nov 06 '13 10:11

Tomas Greif


People also ask

What does observe do in Shiny?

An observer is like a reactive expression in that it can read reactive values and call reactive expressions, and will automatically re-execute when those dependencies change. But unlike reactive expressions, it doesn't yield a result and can't be used as an input to other reactive expressions.

How do you show a message in shiny?

Simply call shinyalert() with the desired arguments, such as a title and text, and a modal will show up. In order to be able to call shinyalert() in a Shiny app, you must first call useShinyalert() anywhere in the app's UI.

Which function is used to create the shiny app?

Shiny uses the function fluidPage to create a display that automatically adjusts to the dimensions of your user's browser window. You lay out the user interface of your app by placing elements in the fluidPage function.

What is input in R shiny?

Arguments. inputId. The input slot that will be used to access the value. label. Display label for the control, or NULL for no label.


1 Answers

Use reactiveValues():

shinyServer(function(input, output, session){
     #update variable and group based on dataset

     values <- reactiveValues()

     observe({

          require(sqldf)

          if (is.null(input$source_columns)) {
               values$obj <-TestData
          } else {
               values$obj<-AggregateData(TestData,Columns=input$source_columns)
          }
             ...
     })

     output$plot <- renderUI({
          plotOutput("p")
     })

     #plotting function using ggplot2

     output$p <- renderPlot({
          require(ggplot2)
          obj <- values$obj
          p <- PlotData(obj,x=input$xaxis, y=input$yaxis)
          print(p)
     })     
})
like image 168
Alex Avatar answered Sep 25 '22 01:09

Alex