2

I am creating a shiny app, a part of which is calculating a linear regression model based on data uploaded and modified by the user. I am doing everything step by step, so here I will show you only the code of a test app, concerning the main problem. The idea is:

  1. User uploads data from a .csv file using the fileInput in shiny
  2. Data shows up and then, after examining it, the user can modify variables of his choice
  3. Every selected and modified variable shows up with the old variables as a new reactive datatable - I managed to get through it with help from this one: Add values to a reactive table in shiny

Now, it all works fine with the code below, when the initial dataset is specified:

test_df <- data.frame(a = seq(1000, 21000, 1000), b = seq(1:21), c = seq(100, 300, 10))

library(shiny)

runApp(list(
  ui=pageWithSidebar(headerPanel("Adding entries to table"),
                 sidebarPanel(uiOutput("select1"),
                              selectInput("select2", "Choose modification",
                                          choices = c("log", "different"), 
                                          selected = NULL, multiple = F),
                              actionButton("update", "Update Table")),
                 mainPanel(tableOutput("table1"))),

server=function(input, output, session) {

values <- reactiveValues()

### specifing the dataset ###
values$df <- data.frame(test_df)
nr <<- nrow(test_df)

### this will contain the modified values ###
values$d <- data.frame(1:nr)

### selecting a variable to modify ###
output$select1 <- renderUI({
  nc <- ncol(values$df)
  nam <- colnames(values$df)
  selectInput("var", label = "Select var:",
              choices = c(nam), multiple = F,
              selected = nam[1])
})

### calculations needed for modifactions ###
newEntry <- observeEvent(input$update, {

  if(input$select2 == "log") {
    newCol <- isolate(
      c(log(values$df[input$var]))
    )
    newCol <- as.data.frame(newCol)
    colnames(newCol) <- paste0("Log of ", input$var)
  } 

  else if(input$select2 == "different") {
    newCol <- isolate(
      c(1-exp(-(values$df[input$var]/100)))
    )
    newCol <- as.data.frame(newCol)
    colnames(newCol) <- paste0(input$var, "Diff of", input$dr1)
  } 
  ### adding new modified columns to the dataframe ###
  isolate(
    values$d <- dplyr::bind_cols(values$d, newCol)
  )

})

output$table1 <- renderTable({
  d1 <- values$d
  nc <- ncol(d1)

  ### printing the whole dataframe (initial+modified) - skipping ###
  ### the first column of modified values, as it doesn't contain our data ###
  if(input$update == 0) {
    print(data.frame(test_df))
  } else {
    data.frame(values$df, d1[2:nc])
  }
})

}))

However, when I want to include the first step, which means uploading the dataset AFTER running the app, the app doesn't start, as now I am referring to reactive content that probably doesn't exist. Here is an updated code, with the feature of uploading the data from your own source:

library(shiny)

runApp(list(
ui=pageWithSidebar(headerPanel("Adding entries to table"),
                 sidebarPanel(fileInput("file1", "Choose file to upload", accept = c("text/csv", "text/comma-separated-values", "text/tab-separated-values", "text/plain", ".csv",".tsv")), 
                              checkboxInput("header", "Header", TRUE), 
                              radioButtons("sep", "Separator",c(Comma=",",Semicolon=";",Tab="\t"),","), 
                              radioButtons("dec", "Decimal",c(Comma=",",Dot="."),","), 
                              actionButton("Load", "Load the File"),
                              uiOutput("select1"),
                              selectInput("select2", "Choose modification",
                                          choices = c("log", "different"), 
                                          selected = NULL, multiple = F),
                              actionButton("update", "Update Table")),
                 mainPanel(tableOutput("table1"))),

server=function(input, output, session) {

values <- reactiveValues()

### uploading data from external source ###
data1 <- reactive({
  if(input$Load == 0){return()}
  inFile <- input$file1
  if (is.null(inFile)){return(NULL)}

  isolate({ 
    input$Load
    my_data <- read.csv(inFile$datapath, header = input$header, sep = input$sep, stringsAsFactors = FALSE, dec = input$dec)
  })
  my_data
})

### specifing the dataset ###
values$df <- data.frame(data1())
nr <<- nrow(data1())

### this will contain the modified values ###
values$d <- data.frame(1:nr)

### selecting a variable to modify ###
output$select1 <- renderUI({
  nc <- ncol(values$df)
  nam <- colnames(values$df)
  selectInput("var", label = "Select var:",
              choices = c(nam), multiple = F,
              selected = nam[1])
})

### calculations needed for modifactions ###
newEntry <- observeEvent(input$update, {

  if(input$select2 == "log") {
    newCol <- isolate(
      c(log(values$df[input$var]))
    )
    newCol <- as.data.frame(newCol)
    colnames(newCol) <- paste0("Log of ", input$var)
  } 

  else if(input$select2 == "different") {
    newCol <- isolate(
      c(1-exp(-(values$df[input$var]/100)))
    )
    newCol <- as.data.frame(newCol)
    colnames(newCol) <- paste0(input$var, "Diff of", input$dr1)
  } 
  ### adding new modified columns to the dataframe ###
  isolate(
    values$d <- dplyr::bind_cols(values$d, newCol)
  )

})

output$table1 <- renderTable({
  d1 <- values$d
  nc <- ncol(d1)

  ### printing the whole dataframe (initial+modified) - skipping the first ###
  ### column of modified values, as it doesn't contain our data ###
  if(input$update == 0) {
    print(data.frame(test_df))
  } else {
    data.frame(values$df, d1[2:nc])
  }
})

}))

The error I get:

Error in .getReactiveEnvironment()$currentContext: Operation not allowed >without an active reactive context. (You tried to do something that can only be >done from inside a reactive expression or observer.)

For sure, I'm missing something about Shiny's reactivity, but I thoroughly read a lot of different articles, questions etc., and can't find the answer to this one. It's not the problem with uploading the file itself, because this block of code works perfectly in a different app, when I'm writing the reactive dataset to a normal variable, before referring to its contents. But how can I do this here, when writing data to values$...? Or maybe there's another solution for working with reactive data from external source, in this kind of way? Hope I made everything clear.

Community
  • 1
  • 1
Maria D.
  • 41
  • 5
  • Does it help if you put `req(input$file1)` at the start of the `data1` `reactive`? – John Paul Dec 15 '16 at 18:22
  • @JohnPaul No, it doesn't, the app shuts down with the same error. – Maria D. Dec 16 '16 at 07:43
  • Try to put your `values$xxx` inside an observer when they are note in a reactive context, for example `observe({values$df <- data1()})`. Here `data1()` is reactive, that's why your code doesn't work. – Stéphane Laurent Dec 18 '16 at 08:54

1 Answers1

1

Thanks @StéphaneLaurent for your help! Still had some problems with the global variable nr, but the following changes made it work.

First I specify the test data, which will be shown before the user loads any data (so the app has some base for calculations, not nulls): test_df <- data.frame(a = seq(1000, 21000, 1000), b = seq(1:21), c = seq(100, 300, 10)).

Then I use it when specifying the reactive dataframe, in case there isn't any user input:

  data1 <- reactive({
  if(input$Load == 0){return(test_df)}
  inFile <- input$file1
  if (is.null(inFile)){return(test_df)}

  isolate({ 
    input$Load
    my_data <- read.csv(inFile$datapath, header = input$header, sep = input$sep, stringsAsFactors = FALSE, dec = input$dec)
  })
  my_data
})

And finally I use the observe function for my variables that are dependent on some other reactive variables:

observe({
  values$df <- data.frame(data1())
  nr <- nrow(data1())
  values$d <- data.frame(1:nr)
})

The rest of the code doesn't require any further changes and the app works perfectly fine now.

Maria D.
  • 41
  • 5