I have an app that reads data (it can be further developed to be able to read excel,csv data) and then based on unique values by every columns(user input) i can filter my data but the problem here is that sometimes the list of values is large .is it possible to be able to just write some letters or numbers (depends on nature of column)in the user input to get only values that match what we write there exactly like in pivot excel.below is my code without this characteristic.
library(shiny)
library(dplyr)
ui <- shinyUI(fluidPage(
titlePanel("Old Faithful Geyser Data"),
sidebarLayout(
sidebarPanel(
selectInput("df",
label="Upload files here",
choices = list.files("C:/Users/zbensmid/Documents")),
uiOutput('choose_cyl'),
uiOutput('choose_vs'),
uiOutput('choose_am')
),
mainPanel(
tableOutput("table")
)
)
))
server <- shinyServer(function(input, output) {
mydata<-reactive({
fread(input$df)
})
output$choose_cyl<-renderUI({selectInput("cyl", "cyl", unique(mydata()$cyl), multiple = TRUE)})
output$choose_vs<- renderUI({selectInput("vs", "vs", unique(mydata()$vs), multiple = TRUE)})
output$choose_am<- renderUI({selectInput("am", "am", unique(mydata()$am), multiple = TRUE)})
output$table <- renderTable({
df<-mydata()
cyl_sel <- if (is.null(input$cyl)) unique(mydata()$cyl) else as.numeric(input$cyl)
vs_sel <- if (is.null(input$vs)) unique(mydata()$vs) else as.numeric(input$vs)
am_sel <- if (is.null(input$am)) unique(mydata()$am) else as.numeric(input$am)
filter(df, cyl %in% cyl_sel, vs %in% vs_sel, am %in% am_sel)
})
})
shinyApp(ui = ui, server = server)
Below is the data you can put it as excel file.
I have a dataframe with a time series as index. The data in the data frame are updated by a dashboard action (e.g. a download button) and therefore the dataframe is reactive. With a slider I want to be able to select only certain rows of the dataframe. The min max values of the slider therefore refer to the rownames of the reactive data frame. So far I am not able to get this implemented. Below the code. The if(0) part in the SERVER section is the one I am talking about. Any help appreciated.
require(shiny)
AquireData <- function(){
# In this function the data are created
df <- data.frame(replicate(3,sample(0:50,1000,rep=TRUE)))
rownames(df) <- seq(from = as.POSIXct("2012-05-15 07:00"),
to = as.POSIXct("2019-05-17 18:00"), by = "min")[0:dim(df)[1]]
names(df) <- c('A','B','C')
return (df)
}
ui <- fluidPage(
# App title
titlePanel("my dashboard"),
# define stuff for the sidebar (buttons, selectlists etc.). These items will
# be displayed for all panels
sidebarLayout(
sidebarPanel(
actionButton("Button_GetAndUpdate", "Update data"),
sliderInput("start_end_dates", "Date range", min =0, max=0, value=1)
),
# Main panel. Here you can display your graphs, plots and tables
mainPanel("observed data", tableOutput("rawdata"))
)
)
server <- function(input, output,session) {
# When the app is called an update of the data is drawn
df_data <- reactive({AquireData()})
# Check what the update button is doing. If its getting pressed pull and update
observeEvent (input$Button_GetAndUpdate,{df_data <<- reactive({AquireData()})})
# set date range slider values using the dates from the data frame index
if(0){
updateSliderInput(session, "start_end_dates",
label = "Date range",
min = as.POSIXct(min(rownames(df_data())),"%Y-%m-%d %H:%M:%S",tz=""),
max = as.POSIXct(max(rownames(df_data())),"%Y-%m-%d %H:%M:%S",tz="")
)
}
# get the head of the dataframe
data_head <- reactive({
input$Button_GetAndUpdate
isolate({
head(df_data())
})
})
output$rawdata <- renderTable({
data_head()
})
}
shinyApp(ui = ui, server = server)
runApp("Header_dashboard")
You could use shinyWidgets::sliderTextInput and shinyWidgets::updateSliderTextInput respectively instead of sliderInputfor this:
shinyWidgets::updateSliderTextInput(
session, "start_end_dates",
choices = rownames(df_data())
)
That means for your app:
require(shiny)
AquireData <- function(){
# In this function the data are created
df <- data.frame(replicate(3,sample(0:50,1000,rep=TRUE)))
rownames(df) <- seq(from = as.POSIXct("2012-05-15 07:00"),
to = as.POSIXct("2019-05-17 18:00"), by = "min")[0:dim(df)[1]]
names(df) <- c('A','B','C')
return (df)
}
ui <- fluidPage(
# App title
titlePanel("my dashboard"),
# define stuff for the sidebar (buttons, selectlists etc.). These items will
# be displayed for all panels
sidebarLayout(
sidebarPanel(
actionButton("Button_GetAndUpdate", "Update data"),
shinyWidgets::sliderTextInput(
"start_end_dates",
label = "Time range",
choices = c(as.POSIXct("2019-01-01 12:00:00"), as.POSIXct("2019-12-31 14:00:00")),
)
),
# Main panel. Here you can display your graphs, plots and tables
mainPanel("observed data", tableOutput("rawdata"))
)
)
server <- function(input, output,session) {
# When the app is called an update of the data is drawn
df_data <- reactive({AquireData()})
# Check what the update button is doing. If its getting pressed pull and update
observeEvent (input$Button_GetAndUpdate,{df_data <<- reactive({AquireData()})})
# set date range slider values using the dates from the data frame index
observe({
shinyWidgets::updateSliderTextInput(
session, "start_end_dates",
choices = rownames(df_data())
)
})
# get the head of the dataframe
data_head <- reactive({
input$Button_GetAndUpdate
isolate({
head(df_data())
})
})
output$rawdata <- renderTable({
data_head()
})
}
shinyApp(ui = ui, server = server)
I am writing a Shiny app that populates the UI based on the values of a data and preprocessing of the same data. This preprocessing also provides some objects to the server.R. This app works fine as long as the data is loaded and preprocessed before initiating the ui.R and server.R. The current structure is
data_preprocessing.R loads data from local machine
source(data_preprocessing.R) to load both into ui.R and server.R
run app.R
This toy code exemplifies this scenario:
# Scenario A
# run on local machine
df <- mtcars
# processe
min.y <- min(df$mpg)
max.y <- max(df$mpg)
mean.y <- mean(df$mpg)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput(
inputId = "y.value",
label = "Filter mpg",
min = min.y,
max = max.y,
value = c(mean.y - 1, mean.y + 1),
step = 0.5
)
),
mainPanel(
plotOutput("plot")
)
)
))
server <- function(input, output) {
filtered_df <- reactive({
df[which(df$mpg >= input$y.value[1] & df$mpg <= input$y.value[2]), ]
})
output$plot <- renderPlot({
ggplot(filtered_df(), aes(x = hp, y = mpg)) + geom_line()
})
}
shinyApp(ui, server)
My problem arises now when I want to generalize this approach to a scenario where the user uploads a dataset once in one rudimentary user interface (e.g. a tab in ui.R), and only then the main user interface in ui.R launches. Additionally, the preprocessing provides several objects for the server. Structure of the code would look something like the following (this does not actually work...):
# Scenario B
# run in the Internet
# df <- mtcars
# # processe
# min.y <- min(df$mpg)
# max.y <- max(df$mpg)
# mean.y <- mean(df$mpg)
ui <- shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
checkboxInput("header", "Header", TRUE),
actionButton(inputId = "go",
label = "Process this data")
#actionButton("submit", label = "Submit")
),
mainPanel(
tableOutput("contents")
)
),
sidebarLayout(
sidebarPanel(
sliderInput(
inputId = "y.value",
label = "Filter mpg",
min = min.y,
max = max.y,
value = c(mean.y - 1, mean.y + 1),
step = 0.5
)
),
mainPanel(
plotOutput("plot")
)
)
)
)
server <- function(input, output) {
mydata <- eventReactive(input$go, {
inFile <- input$file1
if (is.null(inFile))
return(NULL)
# read this file in via a browser!
#df <- read.csv(inFile$datapath, header = input$header)
# for this example load mtcars
df <- mtcars
# process
min.y <- min(df$mpg) # SHOULD BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
max.y <- max(df$mpg) # SHOULD BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
mean.y <- mean(df$mpg) # SHOULD BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
df # PREFERABLY, SHOULD ALSO BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
})
filtered_df <- reactive({
df1 <- mydata()
df1[which(df1$mpg >= input$y.value[1] & df1$mpg <= input$y.value[2]), ]
})
output$plot <- renderPlot({
ggplot(filtered_df(), aes(x = hp, y = mpg)) + geom_line()
})
}
shinyApp(ui, server)
I could probably store all preprocessed objects as reactive objects, but that will quickly make the code unwieldy.
An "easy" solution is if I could somehow make all these preprocessing objects available in the global environment. Many of them are only needed for calculation once. I tried using "<<-" for the relevant objects but that does not work. R protests with "Error in <<-: cannot change value of locked binding for 'df'".
Accordingly, ideas of how to solve this problem?
UPDATE (2019-07-19) based on #MrGumble input:
The global environment is global to all users on the same app. So if user 1 uploads a data set on his jellybean consumption and saves min.y, max.y and mean.y to the global environment, followed by user 2 starting the app, then both users will be presented with these data! When user 2 then uploads her data set on student performances, it overwrites user 1's data! What a mess!
You are right! So instead of the global environment it should be saved in the session environment so that all functions within that session can use it.
A) What does the session argument do? I Google around but could not find a clear answer.
B) Using “<<-”
Using “<<-“ is not working. I have tried defining it within the server function as well as outside of it. But none works. Do you see what is wrong?
ui <- shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
# fileInput("file1", "Choose CSV File",
# accept = c(
# "text/csv",
# "text/comma-separated-values,text/plain",
# ".csv")
# ),
# tags$hr(),
# checkboxInput("header", "Header", TRUE),
actionButton(inputId = "go",
label = "Process this data")
#actionButton("submit", label = "Submit")
),
mainPanel(
tableOutput("contents")
)
),
sidebarLayout(
sidebarPanel(
sliderInput(
inputId = "y.value",
label = "Filter mpg",
min = 1,
max = 30,
value = c(10 - 1, 15 + 1),
step = 0.5
)
),
mainPanel(
plotOutput("plot")
)
)
)
)
server <- function(input, output, session) {
df2 <- NA
# # define reactivevalues
# min.y <- reactiveVal()
# max.y <- reactiveVal()
mydata <- eventReactive(input$go, {
df # PREFERABLY, SHOULD ALSO BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
})
# update min max when the data loads
observeEvent(input$go, {
df2 <<- mydata()
})
output$plot <- renderPlot({
ggplot(df2, aes(x = hp, y = mpg)) + geom_line()
})
}
shinyApp(ui, server)
C) Does it matter for what is displayed in the user interface if we run the server function or the ui function first? I guess not but I have a lurking feeling that it might.
D) Lastly, and most importantly, based on your comments I have updated my code. The ui fails to capture min.y and max.y
# Scenario C
# run in the Internet
server <- function(input, output, session) {
# define reactivevalues
min.y <- reactiveVal()
max.y <- reactiveVal()
mydata <- eventReactive(input$go, {
inFile <- input$file1
if (is.null(inFile))
return(NULL)
# read this file in via a browser!
#df <- read.csv(inFile$datapath, header = input$header)
# for this example load mtcars
df <- mtcars
# # process
# min.y <- min(df$mpg) # SHOULD BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
# max.y <- max(df$mpg) # SHOULD BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
# mean.y <- mean(df$mpg) # SHOULD BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
#
df # PREFERABLY, SHOULD ALSO BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
})
# update min max when the data loads
observeEvent(mydata, {
min.y(min(mydata()$mpg))
max.y(max(mydata()$mpg))
})
observe({
updateSliderInput(session, "go", min=min.y(), max=max.y())
})
filtered_df <- reactive({
df1 <- mydata()
df1[which(df1$mpg >= input$y.value[1] & df1$mpg <= input$y.value[2]), ]
})
output$plot <- renderPlot({
ggplot(filtered_df(), aes(x = hp, y = mpg)) + geom_line()
})
}
ui <- shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Choose CSV File",
accept = c(
"text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
checkboxInput("header", "Header", TRUE),
actionButton(inputId = "go",
label = "Process this data")
#actionButton("submit", label = "Submit")
),
mainPanel(
tableOutput("contents")
)
),
sidebarLayout(
sidebarPanel(
sliderInput(
inputId = "y.value",
label = "Filter mpg",
min = min.y,
max = max.y,
value = c(10 - 1, 15 + 1),
step = 0.5
)
),
mainPanel(
plotOutput("plot")
)
)
)
)
shinyApp(ui, server)
I'll start by "attacking" your assumptions on
# process
min.y <- min(df$mpg) # SHOULD BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
max.y <- max(df$mpg) # SHOULD BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
mean.y <- mean(df$mpg) # SHOULD BE MADE AVAILABLE IN THE GLOBAL ENVIROMENT SO ui CAN USE IT!
The global environment is global to all users on the same app. So if user 1 uploads a data set on his jellybean consumption and saves min.y, max.y and mean.y to the global environment, followed by user 2 starting the app, then both users will be presented with these data! When user 2 then uploads her data set on student performances, it overwrites user 1's data! What a mess!
So, data in global environment stays constant is shared across all sessions! It's useful for pre-loading data that is constant throughout all uses.
If you want to share data within your session, put the variable within the server-function:
constant.var <- readRDS('some-precalculation.rds')
server <- function(input, output) {
my_users_name <- ''
observeEvent(input$txtName, {
my_users_name <<- input$txtName
})
}
In your code mydata is unique to the session. It's defined within server.
When you want to use min.y in the UI, the definition of ui does not change throughout the usage of the app. I believe it is only execute once, when runApp() starts. After that, you can change min.y all you want and the UI doesn't change. (In my example above, notice that I use <<- to assign a value to a variable defined in a outer scope. Do this to re-define min.y in the global environment.)
How do you update the range of your slider?
1) Declare your limits as reactive variables. This allows Shiny to recognise when to update something.
server <- function(input, output) {
min.y <- reactiveVal()
max.y <- reactiveVal()
}
2) min.y and max.y are only updated when the uploaded dataset is updated:
observeEvent(mydata, {
min.y(min(mydata()$mpg))
max.y(max(mydata()$mpg))
})
In fact, we can reduce 1) and 2) to be directly reactives on the uploaded data set:
mydata <- reactiveVal(data.frame())
observeEvent(input$go, {
if (is.null(input$file1))
return(NULL)
df <- read.csv(input$file1$datapath, header=input$header)
# do some checking?
mydata(df)
})
min.y <- reactive(min(mydata()$mpg))
max.y <- reactive(max(mydata()$mpg))
I've updated the routine, so the mydata is a reactive that only gets updated when everything is checked and OK. In your code, if input$file1 is NULL, the reactive mydata will be updated to NULL causing issues downstream when you expect it to be a data frame.
So, how to update the UI? See https://shiny.rstudio.com/reference/shiny/1.2.0/updateSliderInput.html
Which leads us to the following. First update your server-function to accept the session argument:
server <- function(input, output, session) {
and then react to an updated min and max:
observe({
updateSliderInput(session, "y.value", min=min.y(), max=max.y())
})
Of cause, if you only use min.y and max.y for updating the slider, you can do away with min.y and max.y reactives as:
observe({
df <- mydata()
if (is.null(df) || nrow(df) == 0)
return()
updateSliderInput(session, "y.value", min=min(df$mpg), max=max(df$mpg))
})
but that is a matter of taste and modularisation of your code.
I have a shiny app where I want to allow the user to select a dataset based on a set of uploaded files and then specify the columns to display from the selected dataset. If I leave some columns selected and then switch datasets, an error flashes and is output to the console stating that the selected columns are unknown before the app switches datasets and displays it correctly. In my full app however, the app crashes, though I wasn't able to figure out how to reproduce the crash. I thought it might be related to some preprocessing that is done to add additional columns which are the same across datasets and which remain selected, but the error is the same without that feature.
library(shiny)
library(tidyverse)
library(DT)
ui <- fluidPage(
checkboxGroupInput("select_var", label = "Select Variables"),
selectInput("dataset", label = NULL, choices = c("mtcars", "rock")),
DT::dataTableOutput("table")
)
server <- function(session, input, output) {
# define the dataset
data <- reactive({switch(input$dataset,"rock" = rock,"mtcars" = mtcars)})
# add a common column name that is always selected
dataprocessed <- reactive({data <- data()
data$num <- seq(1:nrow(data))
return(data)})
# dynamically generate the variable names
observe({
vchoices <- names(dataprocessed())
updateCheckboxGroupInput(session, "select_var", choices = vchoices, selected = c("num"))
})
# select the variables based on checkbox
data_sel <- reactive({
req(input$select_var)
df_sel <- dataprocessed() %>% select(input$select_var)
})
output$table <- DT::renderDataTable(data_sel())
}
# Run the application
shinyApp(ui = ui, server = server)
We can add a conditional requirement using req() to test for column existence before rendering:
library(shiny)
library(tidyverse)
library(DT)
ui <- fluidPage(
checkboxGroupInput("select_var", label = "Select Variables"),
selectInput("dataset", label = NULL, choices = c("mtcars", "rock")),
DT::dataTableOutput("table")
)
server <- function(session, input, output) {
# define the dataset
data <- reactive({
switch(input$dataset,"rock" = rock,"mtcars" = mtcars)
})
# add a common column name that is always selected
dataprocessed <- reactive({
data <- data()
data$num <- seq(1:nrow(data))
return(data)
})
# dynamically generate the variable names
observe({
vchoices <- names(dataprocessed())
updateCheckboxGroupInput(session, "select_var", choices = vchoices, selected = c("num"))
})
# select the variables based on checkbox
data_sel <- reactive({
req(input$select_var)
req(names(dataprocessed()) %in% input$select_var)
a <- names(dataprocessed())[names(dataprocessed()) %in% input$select_var]
df_sel <- dataprocessed() %>% select(a)
})
output$table <- DT::renderDataTable(data_sel())
}
# Run the application
shinyApp(ui = ui, server = server)
I am trying to print dataset values in shiny web app. But I am only able to print data set name using below code. How can I print dataset values?
library(MASS)
library(shinythemes)
library(shiny)
library(ggplot2)
mass.tmp <- data(package = "MASS")[3]
mass.datasets <- as.vector(mass.tmp$results[,3])
ui <- fluidPage(
theme = shinytheme("superhero"),
titlePanel("Linear Regression Modelling"),
sidebarLayout(
sidebarPanel(
selectInput("dsname", "Dataset:",choices = c(mass.datasets))
,
uiOutput("x_axis")
# ,
# textOutput("txt"),
# tableOutput("tab")
),
mainPanel(
tags$br(),
tags$br()
)
)
)
server <- function(input, output) {
num_ds <- function(ds)
{
nums <- sapply(ds,is.numeric)
num_ds <- ds[,nums]
return(num_ds)
}
ds_ext <- reactive({ num_ds(input$dsname) })
output$x_axis <- renderUI({
col_opts <- get(ds_ext())
selectInput("x_axis2", "Independent Variable:", choices = names(col_opts))
})
}
shinyApp(ui = ui, server = server)
Actually I am trying to solve error in above code "Incorrect number of dimensions". I have written function which would return data frame with only numeric variables so that I can analyze. But getting error in line I guess where I am creating object x_axis. pls help.