```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE, error = F, message = F, warning = F) library(shiny) library(emoji) library(shinyjs) ``` ```{css echo=FALSE} pre { max-height: 700px; overflow-y: auto; } pre[class] { max-height: 500px; } .scroll-100 { max-height: 500px; overflow-y: auto; background-color: inherit; } ``` ## Shiny app logic I {.build}
`ui` (**U**ser **I**nterface) The container, whatever sees the user
`server` It gets the work done
`server` and `ui` talks to each other in a flow: > `ui` (input) $\rightarrow$ `server` (elaborate and compute) $\rightarrow$ `ui` (output) > > - Define something in the `ui` and not using in the server: nothing happens, it just doesn't exist > - Calling something in the server that has not been defined in the `ui`: errors everywhere
## Shiny app logic II {.build} First things first: ```{r eval = FALSE} install.packages("shiny") # install library(shiny) # use it ``` And then: ```{r eval = FALSE} ui = fluidPage( # Define the ui [...] ) server = function(input, output){ # Define the server [...] } shinyApp(ui, server) # create the App ``` Yes... it's **that** easy ## Be tidy, be consistent, save time - Use `R` projects with the default for shiny app creations - 10 minutes coding $=$ 1 hour of debugging - If the app is particularly complex and long, it's better to save `ui` and `server` in two different scripts ## To each its own: Input in UI ```{r eval=FALSE, class.source="myClass"} sliderInput() # Slider input widget numericInput() # Numeric input control selectInput() # Select list input control checkboxInput() # Checkbox input control checkboxGroupInput() dateInput() # Date input fileInput() # File upload control radioButtons() # Radio buttons textInput() # Text input control passwordInput() # Password input control actionButton() # Action button dateInput() # Date input dateRangeInput() # Input a data range ``` [Complete list](https://shiny.rstudio.com/gallery/widget-gallery.html) (with code) ```{r eval=FALSE, class.source="myClass"} selectInput("mySelection", label = h3("Select box"), choices = list("Choice 1" = 1, "Choice 2" = 2, "Choice 3" = 3), selected = 1) ``` ## To each its own: Output in UI ```{r eval=FALSE} plotOutput() # Plot output element textOutput() # Text output element verbatimTextOutput() # Verbatim text output element tableOutput() # Table output element dataTableOutput() # Data table output element htmlOutput() # HTML output element uiOutput() # user interface element downloadButton() # Download button Progress() # Reporting progress (object oriented) withProgress() # Reporting progress (functional) outputOptions() # Set options for an output object ``` Labels: ```{r eval = FALSE} verbatimTextOutput( outputId = "myOutput" ) ``` ## To each its own: Output containers in server ```{r, eval =FALSE} renderPlot() # Plot output renderText() # Text output renderPrint() # Printable output renderTable() # Table output renderDataTable() # Data table output renderImage() # Image file output renderUI() # UI output downloadHandler() # File downloads ``` ```{r eval=FALSE} server = function(input, output) { output$myOutput = renderText({ paste(paste("This is my choice"), input$mySelection) }) } ``` ## App layout ```{r eval=FALSE} fluidPage() # rows with columns within 12-unit # wide grid fluidRow( # custom the page in two columns column(6, ), # of width 6 out of the 12-unit column(6) # on a single row ) sidebarLayout( # automatic layout with sidebarPanel(...), # sidebar and mainPanel(...) # main area ) # and many more! ``` [Complete list](https://shiny.rstudio.com/gallery/) (look for "Application layout") ## Let's start with an easy (ugly) one: Practice ```{r, eval = TRUE, echo=FALSE} shinyApp( ui = fluidPage( sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", # name of the input (for the server) label = "Choose a dataset:", # name of the input (for the users) choices = c("rock", "pressure", "cars")) # options (for both # users & server) ), mainPanel( plotOutput( # define the graphical output (we're telling R that this output "graph" # container must contain a plot) ), verbatimTextOutput( # define the graphical output (we're telling R that "summary" # this output container must contain a Verbatim output) ) ) ) ), server = function(input, output){ output$graph <- renderPlot({ if(input$dataset == "rock"){ # call the input and its options with their label data <- rock } else if (input$dataset == "pressure" ){ data <- pressure } else if (input$dataset == "cars") { data <- cars } plot(data[, c(1:2)]) }) output$summary <- renderPrint({ if(input$dataset == "rock"){ data <- rock } else if (input$dataset == "pressure" ){ data <- pressure } else if (input$dataset == "cars") { data <- cars } summary(data[, c(1:2)]) }) }, options = list(height = 800) ) ``` ## Let's start with an easy (ugly) one: Code ```{r, echo = TRUE, message = FALSE, eval = FALSE} shinyApp( ui = fluidPage( sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", # name of the input (for the server) label = "Choose a dataset:", # name of the input (for the users) choices = c("rock", "pressure", "cars")) # options (for both # users & server) ), mainPanel( plotOutput( # define the graphical output (we're telling R that this output "graph" # container must contain a plot) ), verbatimTextOutput( # define the graphical output (we're telling R that "summary" # this output container must contain a Verbatim output) ) ) ) ), server = function(input, output){ output$graph <- renderPlot({ if(input$dataset == "rock"){ # call the input and its options with their label data <- rock } else if (input$dataset == "pressure" ){ data <- pressure } else if (input$dataset == "cars") { data <- cars } plot(data[, c(1:2)]) }) output$summary <- renderPrint({ if(input$dataset == "rock"){ data <- rock } else if (input$dataset == "pressure" ){ data <- pressure } else if (input$dataset == "cars") { data <- cars } summary(data[, c(1:2)]) }) }, options = list(height = 800) ) ``` ## Don't repeat yourself, use the "shelf" solution ```{r, eval = FALSE} shinyApp( ui = fluidPage( # the UI didn't change at all sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = c("rock", "pressure", "cars")) ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() # create the shelf with reactive values dataInput <- reactive({ # create a reactive envrinoment (it reacts to whatever input receives) if(input$dataset == "rock"){ # this part is the same as before data <- rock } else if (input$dataset == "pressure" ){ data <- pressure } else if (input$dataset == "cars") { data <- cars } }) observe({ # ta-da, the shelf is in action. To make it works,telling R values$data <- data.frame(dataInput()) # it has to be called inside an observe function, }) # we're using a reactive object output$graph <- renderPlot({ plot(values$data[, c(1:2)]) # call the shelf and you're all set }) output$summary <- renderPrint({ summary(values$data) }) }, options = list(height = 800) ) ``` ## Take things slowly and use the action buttons There are two ways of using the action buttons: 1. Use `eventReactive()` 2. Use the shelf we have seen before and adding the action button (Throughout the course $\rightarrow$ solution number 2) ## Action button solution 1: Practice ```{r, echo = FALSE, eval= TRUE} shinyApp( ui = fluidPage( sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, "pressure" = 2, "cars" = 3)), actionButton("load", "Select dataset") ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" )))), server = function(input, output){ dataInput = eventReactive(input$load, { if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } } ) output$graph <- renderPlot({ data = dataInput() plot(data[, c(1:2)]) }) output$summary <- renderPrint({ data = dataInput() summary(data[, c(1:2)]) }) }, options = list(height = 800) ) ``` ## Action button solution 1: Code ```{r, echo = TRUE, eval= FALSE} shinyApp( ui = fluidPage( # same as before sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, # we add numbers to the choices "pressure" = 2, # (more convenient) "cars" = 3)), actionButton("load", "Select dataset") ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" )))), server = function(input, output){ # new shelf dataInput = eventReactive(input$load, { # here's the action for the button if(input$dataset == 1){ # call the input and its options with their label data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } } ) output$graph <- renderPlot({ data = dataInput() plot(data[, c(1:2)]) }) output$summary <- renderPrint({ data = dataInput() summary(data[, c(1:2)]) }) }, options = list(height = 800) ) ``` ## Action button solution 2: Practice ```{r, echo = FALSE, eval= TRUE} shinyApp( ui = fluidPage( sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, "pressure" = 2, "cars" = 3)), actionButton("load", "Upload data") ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } }) observeEvent(input$load, { values$data <- data.frame(dataInput()) }) output$graph <- renderPlot({ plot(values$data[, c(1:2)]) }) output$summary <- renderPrint({ summary(values$data) }) }, options = list(height = 800) ) ``` ## Action button solution 2: Code ```{r, echo = TRUE, eval= FALSE} shinyApp( ui = fluidPage( sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, "pressure" = 2, "cars" = 3)), actionButton("load", "Upload data") ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } }) observeEvent(input$load, { # Use the observeEvent function to make the UI and values$data <- data.frame(dataInput()) # server communicate again }) output$graph <- renderPlot({ plot(values$data[, c(1:2)]) }) output$summary <- renderPrint({ summary(values$data) }) }, options = list(height = 800) ) ``` ## Don't scare the users: Practice ```{r echo=FALSE, eval=TRUE} shinyApp( ui = fluidPage( # same as before sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, "pressure" = 2, "cars" = 3)), actionButton("load", "Upload data") ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } }) observeEvent(input$load, { values$data <- data.frame(dataInput()) }) output$graph <- renderPlot({ validate( need(input$load > 0, "Waiting for data") ) plot(values$data[, c(1:2)]) }) output$summary <- renderPrint({ validate( need(input$load > 0, "Waiting for data") ) summary(values$data) }) }, options = list(height = 800) ) ``` ## Don't scare the users: Code ```{r echo=TRUE, eval=FALSE} shinyApp( ui = fluidPage( # same as before sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, "pressure" = 2, "cars" = 3)), actionButton("load", "Upload data") ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } }) observeEvent(input$load, { values$data <- data.frame(dataInput()) }) output$graph <- renderPlot({ validate( # validate function: I need something to be validated for letting need(input$load > 0, # you see what's hidden behind me "Waiting for data") # If it's not validated, I'll show you this message ) plot(values$data[, c(1:2)]) }) output$summary <- renderPrint({ validate( need(input$load > 0, "Waiting for data") # Waiting message ) summary(values$data) }) }, options = list(height = 800) ) ``` ## Let the users use their own data: Practice ```{r echo = FALSE,eval=TRUE} shinyApp( ui = fluidPage( sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, "pressure" = 2, "cars" = 3, "I want to use my data!!" =4)), # we add an option # Let me introduce you Mr. Conditional Panel conditionalPanel( condition = "input.dataset == '4'", # What is the condition for which I should show up? # What should be displayed inside me once I show up? fileInput("example", # label for the server "Wow my data set!", accept = c("csv")) # what format do we accept? ), actionButton("load", "Upload data") ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" ) ) ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } else if (input$dataset == 4) { data <- read.csv(input$example$datapath) # we call the input for the data #import by its label and we ask for the data path selected by the user } }) observeEvent(input$load, # we load the data set (whatever it is only once the # user has hit the button) { values$data <- data.frame(dataInput()) }) output$graph <- renderPlot({ validate( need(input$load > 0, "Waiting for data") ) if (any(colnames(values$data) == "condition") ){ # I had to change the code #just for the new data set, which is different from all the others # check if there are characters if (any(sapply(values$data, is.character)) == TRUE) { values$data[, sapply(values$data, is.character) == T] = lapply(values$data[, sapply(values$data, is.character) == T], as.factor) } else { values$data = values$data } plot(values$data$tr ~ values$data$condition, xlab = "Condition", ylab = "TR") } else { plot(values$data[, c(1:2)]) } }) output$summary <- renderPrint({ validate( need(input$load > 0, "Waiting for data") ) if (any(colnames(values$data) == "condition") ){ summary(values$data[, c(2:3)]) } else { summary(values$data[, c(1:2)]) } }) }, options = list(height = 800) ) ``` ## Let the users use their data: Code ```{r echo = TRUE,eval=FALSE} shinyApp( ui = fluidPage( sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, "pressure" = 2, "cars" = 3, "I want to use my data!!" =4)), # we add an option # Let me introduce you Mr. Conditional Panel conditionalPanel( condition = "input.dataset == '4'", # What is the condition for which I should show up? # What should be displayed inside me once I show up? fileInput("example", # label for the server "", # no displayed label for users accept = c("csv")) # what format do we accept? ), actionButton("load", "Upload data") ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ # now we use the number associated with each dataset data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } else if (input$dataset == 4) { data <- read.csv(input$example$datapath) # we call the input for the data #import by its label and we ask for the data path selected by the user } }) observeEvent(input$load, # we load the data set (whatever it is only once the # user has hit the button) { values$data <- data.frame(dataInput()) }) output$graph <- renderPlot({ validate( need(input$load > 0, "Waiting for data") ) if (any(colnames(values$data) == "condition") ){ # I had to change the code #just for the new data set, which is different from all the others # check if there are characters if (any(sapply(values$data, is.character)) == TRUE) { values$data[, sapply(values$data, is.character) == T] = lapply(values$data[, sapply(values$data, is.character) == T], as.factor) } else { values$data = values$data } plot(values$data$tr ~ values$data$condition, xlab = "Condition", ylab = "TR") } else { plot(values$data[, c(1:2)]) } }) output$summary <- renderPrint({ validate( need(input$load > 0, "Waiting for data") ) if (any(colnames(values$data) == "condition") ){ summary(values$data[, c(2:3)]) } else { summary(values$data[, c(1:2)]) } }) }, options = list(height = 800) ) ``` ## Code has to be fluid and adapt to everything (like a cat) ```{r, out.width = "15%", fig.align='center', echo = FALSE} knitr::include_graphics("liquid-cats1.jpg") ``` We have the code for uploading a data frame. The code I provided for the previous app is specifically designed to work for that specific data frame If only we had a way to explore the data frame columns and choose the ones we would like to plot `r emoji("thinking")` ## Practice ```{r echo=FALSE, eval=TRUE} shinyApp( ui = fluidPage( sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, "pressure" = 2, "cars" = 3, "I want to use my data!!" =4)), conditionalPanel( condition = "input.dataset == '4'", fileInput("example", "", accept = c("csv")) ), actionButton("load", "Upload data"), # This is the button for uploading # the data conditionalPanel( # It appears only when the data are condition = "input.load >= '1'", # loaded uiOutput("var1"), # contains the name for variable 1 uiOutput("var2"), # contains the name for variable 2 actionButton("select", "Select & Display") # This is the button for ), # selecting the variables and actually see # something ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } else if (input$dataset == 4) { data <- read.csv(input$example$datapath) } }) observeEvent(input$load, { values$data <- data.frame(dataInput()) # check the characters if (any(sapply(values$data, is.character)) == TRUE) { values$data[, sapply(values$data, is.character) == T] = lapply(values$data[, sapply(values$data, is.character) == T], as.factor) } else { values$data = values$data } }) output$var1 <- renderUI({ # remember variable 1? here it is how we extract it nam <- colnames(values$data) # from the data set selectInput("var1", label = "Select x:", # create the input choices = c(nam), multiple = FALSE, selected = nam[1]) }) output$var2 <- renderUI({ nam2 <- colnames(values$data) # create the input for variable 2 selectInput("var2", label = "Select y:", choices = c(nam2), multiple = FALSE, selected = nam2[1]) }) newdata <- observeEvent(input$select, # use observe event so that the app will { # wait for you to decide before acting # Besides, you're creating a new (smaller) object values$df <- values$data[c(input$var1, input$var2)] }) output$graph <- renderPlot({ validate( need(input$select > 0, "Waiting for data") # I changed the validation from ) # load to select df <- values$df # store the new object into an R object plot(df[, c(1:2)]) # use it normally }) output$summary <- renderPrint({ validate( need(input$select > 0, "Waiting for data") ) df <- values$df # same summary(df[, c(1:2)]) }) }, options = list(height = 800) ) ``` ## Code ```{r echo=TRUE, eval=FALSE} shinyApp( ui = fluidPage( sidebarLayout( sidebarPanel( selectInput(inputId = "dataset", label = "Choose a dataset:", choices = list("rock" = 1, "pressure" = 2, "cars" = 3, "I want to use my data!!" =4)), conditionalPanel( condition = "input.dataset == '4'", fileInput("example", "", accept = c("csv")) ), actionButton("load", "Upload data"), # This is the button for uploading # the data conditionalPanel( # It appears only when the data are condition = "input.load >= '1'", # loaded uiOutput("var1"), # contains the name for variable 1 uiOutput("var2"), # contains the name for variable 2 actionButton("select", "Select & Display") # This is the button for ), # selecting the variables and actually see # something ), mainPanel( plotOutput( "graph" ), verbatimTextOutput( "summary" ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } else if (input$dataset == 4) { data <- read.csv(input$example$datapath) } }) observeEvent(input$load, { values$data <- data.frame(dataInput()) # check the characters if (any(sapply(values$data, is.character)) == TRUE) { values$data[, sapply(values$data, is.character) == T] = lapply(values$data[, sapply(values$data, is.character) == T], as.factor) } else { values$data = values$data } }) output$var1 <- renderUI({ # remember variable 1? here it is how we extract it nam <- colnames(values$data) # from the data set selectInput("var1", label = "Select x:", # create the input choices = c(nam), multiple = FALSE, selected = nam[1]) }) output$var2 <- renderUI({ nam2 <- colnames(values$data) # create the input for variable 2 selectInput("var2", label = "Select y:", choices = c(nam2), multiple = FALSE, selected = nam2[1]) }) newdata <- observeEvent(input$select, # use observe event so that the app will { # wait for you to decide before acting # Besides, you're creating a new (smaller) object values$df <- values$data[c(input$var1, input$var2)] }) output$graph <- renderPlot({ validate( need(input$select > 0, "Waiting for data") # I changed the validation from ) # load to select df <- values$df # store the new object into an R object plot(df[, c(1:2)]) # use it normally }) output$summary <- renderPrint({ validate( need(input$select > 0, "Waiting for data") ) df <- values$df # same summary(df[, c(1:2)]) }) }, options = list(height = 800) ) ``` ## Interactive plots: Practice ```{r echo = FALSE, eval=TRUE} library(shinyjs) shinyApp( ui = fluidPage( useShinyjs(), # Set up shinyjs (this is just for nice visual effects) sidebarLayout( sidebarPanel(style = "background-color: #e1e9f9;", a(id = "imp_det", h3("Choose a dataset", style = "font-style: normal; font-size: 14pt;"), href = "#"), shinyjs::hidden(div( id = "details_import", helpText( h5("You can also upload your data!") ) )), selectInput(inputId = "dataset", label = "", choices = list("rock" = 1, "pressure" = 2, "cars" = 3, "I want to use my data!!" =4)), conditionalPanel( condition = "input.dataset == '4'", fileInput("example", "", accept = c("csv")) ), actionButton("load", "Upload data"), conditionalPanel( condition = "input.load >= '1'", uiOutput("var1"), uiOutput("var2"), actionButton("select", "Select & Display") ), ), mainPanel( plotOutput( "graph", click = clickOpts(id = "plot_click"), # when we click we select a point brush = brushOpts(id = "plot_brush") # when we highlight an area we select ), # many rows fluidRow( # it displays on the same row multiple arguments column(4, # first column with the summary verbatim output verbatimTextOutput( "summary" )), column(4, # second column with another verbatim output for the points verbatimTextOutput( "points" ) ), column(4, verbatimTextOutput( "brush" )) ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } else if (input$dataset == 4) { data <- read.csv(input$example$datapath) } }) observeEvent(input$load, { values$data <- data.frame(dataInput()) # check the characters if (any(sapply(values$data, is.character)) == TRUE) { values$data[, sapply(values$data, is.character) == T] = lapply(values$data[, sapply(values$data, is.character) == T], as.factor) } else { values$data = values$data } }) shinyjs::onclick("imp_det", # here the nice visual effect shinyjs::toggle(id = "details_import", anim = TRUE)) output$var1 <- renderUI({ nam <- colnames(values$data) selectInput("var1", label = "Select x:", choices = c(nam), multiple = F, selected = nam[1]) }) output$var2 <- renderUI({ nam2 <- colnames(values$data) selectInput("var2", label = "Select y:", choices = c(nam2), multiple = F, selected = nam2[1]) }) newdata <- observeEvent(input$select, { # wait for you to decide before acting # Besides, you're creating a new (smaller) object values$df <- values$data[c(input$var1, input$var2)] }) output$graph <- renderPlot({ validate( need(input$select > 0, "Waiting for data") ) # load to select df = values$df plot(df[, c(1:2)]) # use it normally }) output$summary <- renderPrint({ validate( need(input$select > 0, "Waiting for data") ) df <- values$df # same summary(df[, c(1:2)]) }) output$points <- renderPrint({ df <- values$df # store the data frame in an object pointID <- nearPoints(df, # the data frame input$plot_click, # the command for a reaction xvar = names(df)[colnames(df) == input$var1], # xvar of the graph yvar = names(df)[colnames(df) == input$var2], # yvar of the graph, addDist = FALSE) validate( need(nrow(pointID) != 0, "Click on a point") # Waiting message ) pointID }) output$brush <- renderPrint({ df <- values$df # store the data frame in an object brushID <- brushedPoints(df,# the data frame input$plot_brush, # the command for a reaction xvar = names(df)[colnames(df) == input$var1], # xvar of the graph yvar = names(df)[colnames(df) == input$var2], # yvar of the graph ) validate( need(nrow(brushID) != 0, "Highlight Area") # Waiting message ) brushID }) }, options = list(height = 800) ) ``` ## Interactive plots: Code ```{r echo = TRUE, eval=FALSE, message=FALSE, warning=FALSE} library(shinyjs) shinyApp( ui = fluidPage( useShinyjs(), # Set up shinyjs (this is just for nice visual effects) sidebarLayout( sidebarPanel(style = "background-color: #e1e9f9;", a(id = "imp_det", h3("Choose a dataset", style = "font-style: normal; font-size: 14pt;"), href = "#"), shinyjs::hidden(div( id = "details_import", helpText( h5("You can also upload your data!") ) )), selectInput(inputId = "dataset", label = "", choices = list("rock" = 1, "pressure" = 2, "cars" = 3, "I want to use my data!!" =4)), conditionalPanel( condition = "input.dataset == '4'", fileInput("example", "", accept = c("csv")) ), actionButton("load", "Upload data"), conditionalPanel( condition = "input.load >= '1'", uiOutput("var1"), uiOutput("var2"), actionButton("select", "Select & Display") ), ), mainPanel( plotOutput( "graph", click = clickOpts(id = "plot_click"), # when we click we select a point brush = brushOpts(id = "plot_brush") # when we highlight an area we select ), # many rows fluidRow( # it displays on the same row multiple arguments column(4, # first column with the summary verbatim output verbatimTextOutput( "summary" )), column(4, # second column with another verbatim output for the points verbatimTextOutput( "points" ) ), column(4, verbatimTextOutput( "brush" )) ) ) # display output ) ), server = function(input, output){ values <- reactiveValues() dataInput <- reactive({ if(input$dataset == 1){ data <- rock } else if (input$dataset == 2 ){ data <- pressure } else if (input$dataset == 3) { data <- cars } else if (input$dataset == 4) { data <- read.csv(input$example$datapath) } }) observeEvent(input$load, { values$data <- data.frame(dataInput()) # check the characters if (any(sapply(values$data, is.character)) == TRUE) { values$data[, sapply(values$data, is.character) == T] = lapply(values$data[, sapply(values$data, is.character) == T], as.factor) } else { values$data = values$data } }) shinyjs::onclick("imp_det", # here the nice visual effect shinyjs::toggle(id = "details_import", anim = TRUE)) output$var1 <- renderUI({ nam <- colnames(values$data) selectInput("var1", label = "Select x:", choices = c(nam), multiple = F, selected = nam[1]) }) output$var2 <- renderUI({ nam2 <- colnames(values$data) selectInput("var2", label = "Select y:", choices = c(nam2), multiple = F, selected = nam2[1]) }) newdata <- observeEvent(input$select, { # wait for you to decide before acting # Besides, you're creating a new (smaller) object values$df <- values$data[c(input$var1, input$var2)] }) output$graph <- renderPlot({ validate( need(input$select > 0, "Waiting for data") ) # load to select df <- values$df # store the new object into an R object plot(df[, c(1:2)]) # use it normally }) output$summary <- renderPrint({ validate( need(input$select > 0, "Waiting for data") ) df <- values$df # same summary(df[, c(1:2)]) }) output$points <- renderPrint({ df <- values$df # store the data frame in an object pointID <- nearPoints(df, # the data frame input$plot_click, # the command for a reaction xvar = names(df)[colnames(df) == input$var1], # xvar of the graph yvar = names(df)[colnames(df) == input$var2], # yvar of the graph, addDist = FALSE) validate( need(nrow(pointID) != 0, "Click on a point") # Waiting message ) pointID }) output$brush <- renderPrint({ df <- values$df # store the data frame in an object brushID <- brushedPoints(df,# the data frame input$plot_brush, # the command for a reaction xvar = names(df)[colnames(df) == input$var1], # xvar of the graph yvar = names(df)[colnames(df) == input$var2], # yvar of the graph ) validate( need(nrow(brushID) != 0, "Highlight Area") # Waiting message ) brushID }) }, options = list(height = 800) ) ``` ## Useful things - [Shiny Gallery](https://shiny.rstudio.com/gallery/): You can find a gallery of different apps developed and deployed with `shiny` - [Shiny widgets](https://shiny.rstudio.com/gallery/widget-gallery.html): All the possible inputs you can use in `shiny` - [Shiny server](https://rstudio.com/products/shiny/shiny-server/): How to deploy your `shiny` app - [Shiny server for dummies](https://deanattali.com/2015/05/09/setup-rstudio-shiny-server-digital-ocean/#shiny-git) How to deploy your `shiny` app **for free** on your own server (but not necessarily in an easy way, sorry) - [shinjs](https://deanattali.com/shinyjs/example): Animate your `shiny` app with some Javascript without knowing **anything** about Java - [CSS for dummies](https://www.w3schools.com/css/): Make your app looks better with css