I am developing a Shiny app to process temperature data. Sometimes, temperatures loggers are set up in the lab and start measuring before actually being deployed in the field. Therefore, I need to allow the user to crop the data to the actual on-site measurements.
The upload and cropping are both triggered by actionButton
s because they require other inputs (time format, delimiter etc.) I have not included in the MWE.
To avoid mixing up datasets, I would like the previous cropped data to be hidden (or better, set to NULL
) if a new raw dataset is uploaded.
I tried the following:
ui
library("tidyverse")
library("magrittr")
library("DT")
library("xts")
library("shiny")
library("shinydashboard")
ui <- dashboardPage(
skin = "green",
dashboardHeader(title = "MWE"),
dashboardSidebar(
sidebarMenu(
menuItem("Upload data", tabName = "upload")
)
),
dashboardBody(
tabItems(
tabItem(tabName = "upload",
fluidRow(
box(
title = "Upload settings",
width = 5,
fileInput("fname", "Data", buttonLabel = "Browse..."),
actionButton("uploadbtn", "Upload")
),
box(
title = "Raw data",
width = 7,
DTOutput("raw_table")
)
),
fluidRow(
uiOutput("crop_box"),
box(
width = 8,
h4(strong("Do you wish to crop your data to the selected times?")),
br(),
actionButton("cropbtn", "Crop")
)
),
fluidRow(
box(
title = "Cropped dataset",
width = 12,
DTOutput("cropped_table")
)
)
)
)
)
)
server
server <- function(input, output, session) {
uploaded <- reactiveVal(FALSE)
observeEvent(input$uploadbtn, {
uploaded(TRUE)
})
# Upload raw data
raw <- bindEvent(reactive({
req(input$fname)
# Read in data as zoo object the convert to xts
read.delim.zoo(file = input$fname$datapath,
sep = "\t",
skip = 0,
header = TRUE,
dec = ".",
drop = FALSE,
FUN = as.POSIXct,
tz = "",
format = "%Y.%m.%d %H:%M:%S") %>% as.xts
}),
input$uploadbtn # Only upload once button is clicked
)
# Display raw data
output$raw_table <- renderDT({
req(raw())
datatable(as.data.frame(raw()), options = list(scrollX = TRUE))
})
# Select beginning and end of dataset
first_data <- reactive({
raw() %>% first("1 days") %>% zoo
})
last_data <- reactive({
raw() %>% last("1 days") %>% zoo
})
output$crop_box <- renderUI({
box(
width = 4,
h4(strong("Select the start and end time of on-site measurements.")),
sliderInput("onsite_start", "Start of on-site measurements",
min = as.POSIXct(min(index(first_data()))),
max = as.POSIXct(max(index(first_data()))),
value = min(index(first_data())),
timeFormat = "%F %R"),
sliderInput("onsite_end", "End of on-site measurements",
min = as.POSIXct(min(index(last_data()))),
max = as.POSIXct(max(index(last_data()))),
value = max(index(last_data())))
)
})
cropped <- bindEvent(reactive({
req(raw(), uploaded())
start_indx <- index(raw()) >= as.POSIXct(input$onsite_start) # Get start
end_indx <- index(raw()) <= as.POSIXct(input$onsite_end) # Get end
raw()[which(start_indx & end_indx), , drop = FALSE]
}),
input$cropbtn # Only compute once button is clicked
)
output$cropped_table <- renderDT({
req(cropped())
cropped_data <- isolate(cropped())
uploaded(FALSE)
datatable(as.data.frame(cropped_data))
})
observeEvent(input$uploadbtn, {
updateSliderInput(session = session, "first", value = 1)
updateSliderInput(session = session, "last", value = 1)
updateSliderInput(session = session, "onsite_start", value = as.POSIXct(min(index(first_data()))))
updateSliderInput(session = session, "onsite_end", value = as.POSIXct(max(index(last_data()))))
})
}
shinyApp(ui, server)
My plan was to use uploaded <- reactiveVal(TRUE)
as a flag, and to set it to FALSE
once the dataset had been cropped, so that it could be set to TRUE
again with a new upload. Obviously this doesn't work as the cropped dataset still shows after a new upload.
Note, however, that the updateSliderInputs
work as expected when the upload button is clicked, so I gather the error must be in my strategy rather than purely my syntax.
I also tried
observeEvent(input$uploadbtn, {
cropped <<- reactive(NULL)
})
but this obviously just results in nothing being displayed at all, even after clicking input$cropbtn
. I struggle to see how to build a condition that fits my needs.
I have looked at Resetting data in R shiny app when file upload fields change, Shiny resetting and updating reactiveValues dataframe with two different buttons, r - How to reset reactiveValues, and shiny - How to invalidate ractive observer using code?. Unfortunately, they did not allow me to find a solution.
Please find sample data here and here (the same data with different dates so you can tell them apart easily).
CodePudding user response:
Try with a reactiveValues
object that is set to NULL
when a new data file is uploaded.
server <- function(input, output, session) {
croppedd <- reactiveValues(data=NULL)
# uploaded <- reactiveVal(FALSE)
# observeEvent(input$uploadbtn, {
# uploaded(TRUE)
# })
# Upload raw data
raw <- bindEvent(reactive({
req(input$fname)
# Read in data as zoo object the convert to xts
read.delim.zoo(file = input$fname$datapath,
sep = "\t",
skip = 0,
header = TRUE,
dec = ".",
drop = FALSE,
FUN = as.POSIXct,
tz = "",
format = "%Y.%m.%d %H:%M:%S") %>% as.xts
}),
input$uploadbtn # Only upload once button is clicked
)
# Display raw data
output$raw_table <- renderDT({
req(raw())
datatable(as.data.frame(raw()), options = list(scrollX = TRUE))
})
# Select beginning and end of dataset
first_data <- reactive({
raw() %>% first("1 days") %>% zoo
})
last_data <- reactive({
raw() %>% last("1 days") %>% zoo
})
output$crop_box <- renderUI({
box(
width = 4,
h4(strong("Select the start and end time of on-site measurements.")),
sliderInput("onsite_start", "Start of on-site measurements",
min = as.POSIXct(min(index(first_data()))),
max = as.POSIXct(max(index(first_data()))),
value = min(index(first_data())),
timeFormat = "%F %R"),
sliderInput("onsite_end", "End of on-site measurements",
min = as.POSIXct(min(index(last_data()))),
max = as.POSIXct(max(index(last_data()))),
value = max(index(last_data())))
)
})
cropped <- bindEvent(reactive({
req(raw(), uploaded())
start_indx <- index(raw()) >= as.POSIXct(input$onsite_start) # Get start
end_indx <- index(raw()) <= as.POSIXct(input$onsite_end) # Get end
raw()[which(start_indx & end_indx), , drop = FALSE]
}),
input$cropbtn # Only compute once button is clicked
)
observe({
croppedd$data <- cropped()
})
output$cropped_table <- renderDT({
req(cropped())
# cropped_data <- isolate(cropped())
# uploaded(FALSE)
datatable(as.data.frame(croppedd$data))
})
observeEvent(input$uploadbtn, {
croppedd$data <- NULL
# updateSliderInput(session = session, "first", value = 1)
# updateSliderInput(session = session, "last", value = 1)
updateSliderInput(session = session, "onsite_start", value = as.POSIXct(min(index(first_data()))))
updateSliderInput(session = session, "onsite_end", value = as.POSIXct(max(index(last_data()))))
})
}