1

I have a shiny app that reads from 3 excel files: "file_1.xlsx","file_2.xlsx" and "file_3.xlsx". But it only reads when I choose the options on the selectInput widget.

The wwwfolder has a data folder that I inlcude this 3 files.

The code to read theses folder is:

file_1 <- read_xlsx('www/data/file_1.xlsx')
file_2 <- read_xlsx('www/data/file_2.xlsx')
file_3 <- read_xlsx('www/data/file_3.xlsx')

file_1_new <- file_1 %>% mutate(new_column = 'time series')
file_2_new <- file_1 %>% mutate(new_column = 'time series')
file_3_new <- file_1 %>% mutate(new_column = 'time series')

And this is my app file:

library(shiny)
library(tidyverse)

file_1 <- read_xlsx('www/data/file_1.xlsx')
file_2 <- read_xlsx('www/data/file_2.xlsx')
file_3 <- read_xlsx('www/data/file_3.xlsx')

ui <- fluidPage(
  selectInput(inputId = 'choices',label = "Files",choices = c('File1','File2','File3')),
  
  plotOutput('chart')
)

server <- function(input, output, session) {
  
  observeEvent(input$choices,{
  
    if(input$choices == 'File1'){
      output$chart <- renderPlot({ggplot(file_1, aes(x=x,y=y)) + geom_line(color = 'blue')})
    }else{
      if(input$choices == 'File2'){
        output$chart <-renderPlot({ggplot(file_2, aes(x=x,y=y)) + geom_line(color = 'green')})
      }else{
        output$chart <-renderPlot({ggplot(file_3, aes(x=x,y=y)) + geom_line(color = 'red')})
      }
    }
      
  }
  )

  
}

shinyApp(ui, server)

In other words how can i make shiny reads the first file and then AFTER this run the app.

Laura
  • 675
  • 10
  • 32
  • 1
    One possible example here: https://stackoverflow.com/questions/70027673/r-shiny-how-to-read-different-csv-file-based-of-user-input or https://stackoverflow.com/questions/49556329/using-drop-down-bottom-in-shiny-to-loaed-files-from-a-folder. Make the data source reactive so that you load the data in the server function, not before. You can have your dropdown box choose the file name and then read in the data from that file name into a reactive object. – MrFlick Oct 28 '22 at 15:42
  • @MrFlick If I need this reactive object to create others object in my R file? The file_1 object is an input for others objects in R. I would read file_1 as reactive and then I would need to "come back" to my R code and use this reactive object as input for others R object. It will work? – Laura Oct 28 '22 at 16:03
  • I changed the code. Once I read file_1 as reactive I need to create file_1new, file_2_new, file_3new using the file_* reactive objects. – Laura Oct 28 '22 at 16:06
  • 1
    Where is it suggested (in docs, in tutorials, etc) to nest `observe`/`reactive` blocks? I've never seen or done it in production, generally believing that it is a mistake in architecture. For your `output$chart` block, I suggest a _single_ `output$chart <- renderPlot(...)` block at the top-level within your `server` component, where inside you do something like `renderPlot({ dat <- switch(input$choices, File1=file_1, File2=file_2, file_3); ggplot(dat, ...); })`. – r2evans Oct 28 '22 at 16:13
  • @r2evans I edited/fixed the code. I thinks my main problem is if I need to use this new reactive object to computate others r objects I will need to inlcude my entire R code inside the server? making everything reactive? – Laura Oct 28 '22 at 16:21
  • Yes, in general, if your shiny app needs to react to different datasets, you have two options: (1) precompute everything for all datasets, this can be done outside of the `server` block; or (2) calculate on-the-fly, which allows you to adapt to dynamic (updated) data. This second part can only be done from within the `server` block, whether literal code blocks or functions defined (possibly elsewhere). – r2evans Oct 28 '22 at 16:51
  • If you only ever use the data from one dataset at a time, you just need a single reactive object. You don't want one for each file. It's not clear to me why you are trying to read only one before the app starts. – MrFlick Oct 28 '22 at 16:58

1 Answers1

1

Option 1: precompute all

library(shiny)
library(dplyr)

allfiles <- c(File1="www/data/file_1.xlsx", File2="www/data/file_2.xlsx", File3="www/data/file_3.xlsx")
alldata <- lapply(files, function(f) readxl::read_xlsx(f) %>% mutate(new_column = "time series"))

ui <- fluidPage(
  selectInput(inputId = "choices", label = "Files", choices = names(allfiles)),
  plotOutput("chart")
)

server <- function(input, output, session) {
  mydata <- reactive({
    req(input$choices)
    alldata[[ input$choices ]]
  })
  output$chart <- renderPlot({
    req(mydata())
    col <- switch(isolate(input$choices),
                  File1 = "blue", File2 = "green",
                  File3 = "red", "black")
    ggplot(mydata(), aes(x=x, y=y)) +
      geom_line(color = col)
  })
}

Option 2: reactively load and compute

library(shiny)
library(dplyr)

allfiles <- c(File1="www/data/file_1.xlsx", File2="www/data/file_2.xlsx", File3="www/data/file_3.xlsx")
# alldata <- lapply(files, function(f) readxl::read_xlsx(f) %>% mutate(new_column = "time series"))

ui <- fluidPage(
  selectInput(inputId = "choices", label = "Files", choices = names(allfiles)),
  plotOutput("chart")
)

server <- function(input, output, session) {
  mydata <- reactive({
    req(input$choices)
    req(file.exists(allfiles[ input$choices ]))
    readxl::read_xlsx(allfiles[ input$choices ]) %>%
      mutate(new_column = "time series")
  })
  output$chart <- renderPlot({
    req(mydata())
    col <- switch(isolate(input$choices),
                  File1 = "blue", File2 = "green",
                  File3 = "red", "black")
    ggplot(mydata(), aes(x=x, y=y)) +
      geom_line(color = col)
  })
}

Depending on your calculation needs, I've seen utility in making mydata just the raw data (only read_xlsx(..)), and having another such as mydata_upd <- eventReactive(mydata(), { mydata() %>% mutate(...) ... }), where output$chart would then only depend on mydata2() (but something else might rely on mydata()). This separate use-case makes sense when this calculation filters/rearranges or somehow changes the data in a way that cannot be easily inferred from looking at mydata2().


The req blocks could definitely be improved to use validate/need, doing a better job of informing the user if/when something is not right.

r2evans
  • 141,215
  • 6
  • 77
  • 149