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:
- User uploads data from a .csv file using the
fileInput
in shiny - Data shows up and then, after examining it, the user can modify variables of his choice
- 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.