For two categorical variables inside a dataframe I want to compute the fisher test based on the user selection for the variables, specific factors of these variables (and also filtering by another column).
For this, I need to obtain the contingency table, and then apply the fisher.test function.
Just to visualize it, here is how it can be done in R base:
library(vcd)
library(dplyr)
a <- Arthritis %>%
dplyr::filter(Treatment == "Treated") %>%
dplyr::filter(Improved == "Some") %>%
count() %>%
as.numeric()
b <- Arthritis %>%
dplyr::filter(Treatment == "Treated") %>%
dplyr::filter(Improved != "Some") %>%
count() %>%
as.numeric()
c <- Arthritis %>%
dplyr::filter(Treatment == "Placebo") %>%
dplyr::filter(Improved == "Some") %>%
count() %>%
as.numeric()
d <- Arthritis %>%
dplyr::filter(Treatment == "Placebo") %>%
dplyr::filter(Improved != "Some") %>%
count() %>%
as.numeric()
data <- as.data.frame(matrix(c(a,b,c,d), nrow= 2, ncol = 2, byrow = TRUE))
fisher.test(data)
For the RepEx below I just want to obtain the contingency table.
You can see it clearly, but just to explain a little bit:
First we create the UI, where we allow the user to select several variables (var1, var2, biomarker) and then the factors for the statistics.
Then we update this variables based on user input
We create the contingency table (dataframe) based on the users selection
# Shiny
library(shiny)
library(shinyWidgets)
library(shinyjqui)
# Data
library(vcd)
library(readxl)
library(dplyr)
# Plots
library(ggplot2)
# Stats cohen.d wilcox.test
library(effsize)
not_sel <- "Not selected"
## UI
ui <- navbarPage(
tabPanel(
title = "Plotter",
titlePanel("Plotter"),
sidebarLayout(
sidebarPanel(
title = "Inputs",
fileInput("xlsx_input", "Select XLSX file to import", accept = c(".xlsx")),
selectInput("num_var_1", "Variable X axis", choices = c(not_sel)), # X variable num_var_1
selectInput("num_var_2", "Variable Y axis", choices = c(not_sel)),
selectInput("biomarker", "Select biomarker", choices = c(not_sel)), uiOutput("factor"),
uiOutput("Xgroup1"),uiOutput("Xgroup2"), uiOutput("Ygroup1"), uiOutput("Ygroup2"),
br(),
actionButton("run_button", "Run Analysis", icon = icon("play"))
),
mainPanel(
tabsetPanel(
tabPanel(
title = "Statistics",
verbatimTextOutput("test")
)
)
)
)
)
)
## Server
server <- function(input, output){
# Dynamic selection of the data. We allow the user to input the data that they want
data_input <- reactive({
#req(input$xlsx_input)
#inFile <- input$xlsx_input
#read_excel(inFile$datapath, 1)
Arthritis
})
# We update the choices available for each of the variables
observeEvent(data_input(),{
choices <- c(not_sel, names(data_input()))
updateSelectInput(inputId = "num_var_1", choices = choices)
updateSelectInput(inputId = "num_var_2", choices = choices)
updateSelectInput(inputId = "biomarker", choices = choices)
})
num_var_1 <- eventReactive(input$run_button, input$num_var_1)
num_var_2 <- eventReactive(input$run_button, input$num_var_2)
biomarker <- eventReactive(input$run_button, input$biomarker)
## Update variables
# Factor for the biomarker
output$factor <- renderUI({
req(input$biomarker, data_input())
if (input$biomarker != not_sel) {
b <- unique(data_input()[[input$biomarker]])
pickerInput(inputId = 'selected_factors',
label = 'Select factors',
choices = c(b[1:length(b)]), selected=b[1], multiple = TRUE,
# choices = c("NONE",b[1:length(b)]), selected="NONE", If we want "NONE" to appear as the first option
# multiple = TRUE, ## if you wish to select multiple factor values; then deselect NONE
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
}
})
output$Xgroup1 <- renderUI({
req(input$num_var_1, data_input())
c <- unique(data_input()[[input$num_var_1]])
pickerInput(inputId = 'selected_Xgroup1',
label = 'Select group for statistics',
choices = c(c[1:length(c)]), selected=c[1], multiple = TRUE,
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
})
output$Xgroup2 <- renderUI({
req(input$num_var_1, data_input())
d <- unique(data_input()[[input$num_var_1]])
pickerInput(inputId = 'selected_Xgroup2',
label = 'Select group for statistics',
choices = c(d[1:length(d)]), selected=d[1], multiple = TRUE,
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
})
output$Ygroup1 <- renderUI({
req(input$num_var_2, data_input())
c <- unique(data_input()[[input$num_var_2]])
pickerInput(inputId = 'selected_Ygroup1',
label = 'Select group for statistics',
choices = c(c[1:length(c)]), selected=c[1], multiple = TRUE,
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
})
output$Ygroup2 <- renderUI({
req(input$num_var_2, data_input())
c <- unique(data_input()[[input$num_var_2]])
pickerInput(inputId = 'selected_Ygroup1',
label = 'Select group for statistics',
choices = c(c[1:length(c)]), selected=c[1], multiple = TRUE,
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
})
##############################################################################
data_stats <- reactive({
req(data_input(), input$num_var_1, input$num_var_2, input$biomarker, input$selected_factors)
# We filter by biomarker in case user selected, otherwise data_input() remains the same
if (input$biomarker != "Not Selected") df <- data_input()[data_input()[[input$biomarker]] %in% input$selected_factors,]
else df <- data_input()
a <- df %>%
dplyr:filter(.data[[input$num_var_1]] %in% input$Xgroup1) %>%
dplyr:filter(.data[[input$num_var_2]] %in% input$Ygroup1) %>%
count()
b <- df %>%
dplyr:filter(.data[[input$num_var_1]] %in% input$Xgroup2) %>%
dplyr:filter(.data[[input$num_var_2]] %in% input$Ygroup1) %>%
count()
c <- df %>%
dplyr:filter(.data[[input$num_var_1]] %in% input$Xgroup1) %>%
dplyr:filter(.data[[input$num_var_2]] %in% input$Ygroup2) %>%
count()
d <- df %>%
dplyr:filter(.data[[input$num_var_1]] %in% input$Xgroup2) %>%
dplyr:filter(.data[[input$num_var_2]] %in% input$Ygroup2) %>%
count()
data <- as.data.frame(matrix(c(a,b,c,d), nrow= 2, ncol = 2, byrow = TRUE))
})
output$test <- renderPrint(data_stats())
}
shinyApp(ui = ui, server = server)
However, this app is not generating any results.
You have a few syntax errors. First, the inputID for Ygroup2 was still selected_Ygroup1. Second, dplyr:filter() will not reference the dplyr package as it should be dplyr::filter() - that is double colon. Lastly, your variables should not be input$Xgroup1 but actually be input$selected_Xgroup1, and so on. Also, it is better to have eventReactive instead of reactive. Try this
# Shiny
library(shiny)
library(shinyWidgets)
library(shinyjqui)
# Data
library(vcd)
library(readxl)
library(dplyr)
# Plots
library(ggplot2)
# Stats cohen.d wilcox.test
library(effsize)
not_sel <- "Not selected"
## UI
ui <- navbarPage(
tabPanel(
title = "Plotter",
titlePanel("Plotter"),
sidebarLayout(
sidebarPanel(
title = "Inputs",
fileInput("xlsx_input", "Select XLSX file to import", accept = c(".xlsx")),
selectInput("num_var_1", "Variable X axis", choices = c(not_sel)), # X variable num_var_1
selectInput("num_var_2", "Variable Y axis", choices = c(not_sel)),
selectInput("biomarker", "Select biomarker", choices = c(not_sel)), uiOutput("factor"),
uiOutput("Xgroup1"),uiOutput("Xgroup2"), uiOutput("Ygroup1"), uiOutput("Ygroup2"),
br(),
actionButton("run_button", "Run Analysis", icon = icon("play"))
),
mainPanel(
tabsetPanel(
tabPanel(
title = "Statistics",
verbatimTextOutput("test")
)
)
)
)
)
)
## Server
server <- function(input, output){
# Dynamic selection of the data. We allow the user to input the data that they want
data_input <- reactive({
#req(input$xlsx_input)
#inFile <- input$xlsx_input
#read_excel(inFile$datapath, 1)
Arthritis
})
# We update the choices available for each of the variables
observeEvent(data_input(),{
choices <- c(not_sel, names(data_input()))
updateSelectInput(inputId = "num_var_1", choices = choices)
updateSelectInput(inputId = "num_var_2", choices = choices)
updateSelectInput(inputId = "biomarker", choices = choices)
})
num_var_1 <- eventReactive(input$run_button, input$num_var_1)
num_var_2 <- eventReactive(input$run_button, input$num_var_2)
biomarker <- eventReactive(input$run_button, input$biomarker)
## Update variables
# Factor for the biomarker
output$factor <- renderUI({
req(input$biomarker, data_input())
if (input$biomarker != not_sel) {
b <- unique(data_input()[[input$biomarker]])
pickerInput(inputId = 'selected_factors',
label = 'Select factors',
choices = c(b[1:length(b)]), selected=b[1], multiple = TRUE,
# choices = c("NONE",b[1:length(b)]), selected="NONE", If we want "NONE" to appear as the first option
# multiple = TRUE, ## if you wish to select multiple factor values; then deselect NONE
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
}
})
output$Xgroup1 <- renderUI({
req(input$num_var_1, data_input())
c <- unique(data_input()[[input$num_var_1]])
pickerInput(inputId = 'selected_Xgroup1',
label = 'Select group for statistics',
choices = c(c[1:length(c)]), selected=c[1], multiple = TRUE,
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
})
output$Xgroup2 <- renderUI({
req(input$num_var_1, data_input())
d <- unique(data_input()[[input$num_var_1]])
pickerInput(inputId = 'selected_Xgroup2',
label = 'Select group for statistics',
choices = c(d[1:length(d)]), selected=d[1], multiple = TRUE,
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
})
output$Ygroup1 <- renderUI({
req(input$num_var_2, data_input())
c <- unique(data_input()[[input$num_var_2]])
pickerInput(inputId = 'selected_Ygroup1',
label = 'Select group for statistics',
choices = c(c[1:length(c)]), selected=c[1], multiple = TRUE,
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
})
output$Ygroup2 <- renderUI({
req(input$num_var_2, data_input())
c <- unique(data_input()[[input$num_var_2]])
pickerInput(inputId = 'selected_Ygroup2',
label = 'Select group for statistics',
choices = c(c[1:length(c)]), selected=c[1], multiple = TRUE,
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
})
##############################################################################
data_stats <- eventReactive(input$run_button, {
req(data_input(), input$num_var_1, input$num_var_2, input$biomarker, input$selected_factors)
req(input$selected_Xgroup1,input$selected_Xgroup2,input$selected_Ygroup1,input$selected_Ygroup2)
# We filter by biomarker in case user selected, otherwise data_input() remains the same
if (input$biomarker != "Not Selected") df <- data_input()[data_input()[[input$biomarker]] %in% input$selected_factors,]
else df <- data_input()
a <- df %>%
dplyr::filter(.data[[input$num_var_1]] %in% input$selected_Xgroup1) %>%
dplyr::filter(.data[[input$num_var_2]] %in% input$selected_Ygroup1) %>%
count()
b <- df %>%
dplyr::filter(.data[[input$num_var_1]] %in% input$selected_Xgroup2) %>%
dplyr::filter(.data[[input$num_var_2]] %in% input$selected_Ygroup1) %>%
count()
c <- df %>%
dplyr::filter(.data[[input$num_var_1]] %in% input$selected_Xgroup1) %>%
dplyr::filter(.data[[input$num_var_2]] %in% input$selected_Ygroup2) %>%
count()
d <- df %>%
dplyr::filter(.data[[input$num_var_1]] %in% input$selected_Xgroup2) %>%
dplyr::filter(.data[[input$num_var_2]] %in% input$selected_Ygroup2) %>%
count()
data <- as.data.frame(matrix(c(a,b,c,d), nrow= 2, ncol = 2, byrow = TRUE))
m <- matrix(unlist(data), 2)
fisher.test(m)
})
output$test <- renderPrint(data_stats())
}
shinyApp(ui = ui, server = server)
Related
I have a fully functioning shiny app for performing regression analysis, with summary(), tidy(), and augment().
However, I would like to add a filter selection in the shiny for the uploaded data.
My dataset is quite big and within the dataset, it is divided into 5 types, (so, type_1, type_2, type_3, etc). Right now I have to divide my dataset manually outside the shiny app to 5 different datasets so I can only run the regression for one specific type at a time.
It would be great to be able to choose and select the type within the shiny, without going through all this hassle.
Grateful for all your help.
library(shiny)
library(shinyWidgets)
library(DT)
library(dplyr)
library(nlme)
library(broom)
ui <- navbarPage("dd",
tabPanel("Reg",
sidebarPanel(
fileInput(
inputId = "filedata",
label = "Upload data. csv",
multiple = FALSE,
accept = c(".csv"),
buttonLabel = "Choosing ...",
placeholder = "No files selected yet"
),
uiOutput("xvariable"),
uiOutput("yvariable")
),
mainPanel(
DTOutput("tb1"),
fluidRow(
column(6, verbatimTextOutput('lmSummary')),
column(6,verbatimTextOutput("tid")),
column(6,verbatimTextOutput("aug"))
)
)
)
)
server <- function(input, output, session) {
data_1 <- reactive({
req(input$filedata)
inData <- input$filedata
if (is.null(inData)){ return(NULL) }
mydata <- read.csv(inData$datapath, header = TRUE, sep=",")
})
output$tb1 <- renderDT(head(data_1()))
output$xvariable <- renderUI({
req(data_1())
xa<-colnames(data_1())
pickerInput(inputId = 'xvar',
label = 'Select x-axis variable',
choices = c(xa[1:length(xa)]), selected=xa[2],
options = list(`style` = "btn-info"),
multiple = TRUE)
})
output$yvariable <- renderUI({
req(data_1())
ya<-colnames(data_1())
pickerInput(inputId = 'yvar',
label = 'Select y-axis variable',
choices = c(ya[1:length(ya)]), selected=ya[1],
options = list(`style` = "btn-info"),
multiple = FALSE)
})
lmModel <- reactive({
req(data_1(),input$xvar,input$yvar)
x <- as.numeric(data_1()[[as.name(input$xvar)]])
y <- as.numeric(data_1()[[as.name(input$yvar)]])
current_formula <- paste0(input$yvar, " ~ ", paste0(input$xvar, collapse = " + "))
current_formula <- as.formula(current_formula)
model <- lm(current_formula, data = data_1(), na.action=na.exclude)
return(model)
})
output$lmSummary <- renderPrint({
req(lmModel())
summary(lmModel())
})
output$tid <- renderPrint({
req(lmModel())
tidy(lmModel())
})
output$aug <- renderPrint({
req(lmModel())
augment(lmModel())
})
}
shinyApp(ui, server)
How the uploaded dataset could look like, for better explanation
data_set <- data.frame (Simulation_1 = c(1,2,3,4,5,6,7,8,9,10),
Simulation_2 = c(1,2,3,4,5,6,7,8,9,10),
Simulation_3 = c(1,2,3,4,5,6,7,8,9,10),
type = c("type_1", "type_2", "Type_5",
"type_1", "type_2", "Type_3",
"type_1", "type_2", "Type_1","Type_4")
)
Perhaps you are looking for this
library(shiny)
library(shinyWidgets)
library(DT)
library(dplyr)
library(nlme)
library(broom)
data_set <- data.frame (Simulation_1 = c(1,2,3,4,5,6,7,8,9,10),
Simulation_2 = c(1,2,3,4,5,6,7,8,9,10),
Simulation_3 = c(1,2,3,4,5,6,7,8,9,10),
type = c("type_1", "type_2", "Type_5",
"type_1", "type_2", "Type_3",
"type_1", "type_2", "Type_1","Type_4")
)
ui <- navbarPage("dd",
tabPanel("Reg",
sidebarPanel(
fileInput(
inputId = "filedata",
label = "Upload data. csv",
multiple = FALSE,
accept = c(".csv"),
buttonLabel = "Choosing ...",
placeholder = "No files selected yet"
),
uiOutput("col"),
uiOutput("type"),
uiOutput("xvariable"),
uiOutput("yvariable")
),
mainPanel(
DTOutput("tb1"),
fluidRow(
column(6, verbatimTextOutput('lmSummary')),
column(6,verbatimTextOutput("tid")),
column(6,verbatimTextOutput("aug"))
)
)
)
)
server <- function(input, output, session) {
data_0 <- reactive({
# req(input$filedata)
# inData <- input$filedata
# if (is.null(inData)){ return(NULL) }
# mydata <- read.csv(inData$datapath, header = TRUE, sep=",")
data_set
})
output$tb1 <- renderDT(head(data_1()))
output$col <- renderUI({
req(data_0())
selected = colnames(data_0())[length(colnames(data_0()))]
selectInput("mycol", "Choose column", choices = colnames(data_0()), selected = selected)
})
output$type <- renderUI({
req(data_0(),input$mycol)
selectInput("mytype", "Choose Type", choices = unique(data_0()[[input$mycol]]))
})
data_1 <- eventReactive(input$mytype, {
req(data_0(),input$mycol,input$mytype)
df <- data_0()
df$newvar <- df[[input$mycol]]
df %>% dplyr::filter(newvar %in% input$mytype) %>% dplyr::select(- c(newvar))
})
output$xvariable <- renderUI({
req(data_1())
xa<-colnames(data_1())
pickerInput(inputId = 'xvar',
label = 'Select x-axis variable',
choices = c(xa[1:length(xa)]), selected=xa[2],
options = list(`style` = "btn-info"),
multiple = TRUE)
})
output$yvariable <- renderUI({
req(data_1())
ya<-colnames(data_1())
pickerInput(inputId = 'yvar',
label = 'Select y-axis variable',
choices = c(ya[1:length(ya)]), selected=ya[1],
options = list(`style` = "btn-info"),
multiple = FALSE)
})
lmModel <- reactive({
req(data_1(),input$xvar,input$yvar)
x <- as.numeric(data_1()[[as.name(input$xvar)]])
y <- as.numeric(data_1()[[as.name(input$yvar)]])
current_formula <- paste0(input$yvar, " ~ ", paste0(input$xvar, collapse = " + "))
current_formula <- as.formula(current_formula)
model <- lm(current_formula, data = data_1(), na.action=na.exclude)
return(model)
})
output$lmSummary <- renderPrint({
req(lmModel())
summary(lmModel())
})
output$tid <- renderPrint({
req(lmModel())
tidy(lmModel())
})
output$aug <- renderPrint({
req(lmModel())
augment(lmModel())
})
}
shinyApp(ui, server)
If I want to obtain the fisher test first I need a contigency table. I can do that for the Arthritis package by simply:
library(vcd)
data(Arthritis)
freq <- as.data.frame.matrix(table(Arthritis$Treatment, Arthritis$Improved))
> freq
None Some Marked
Placebo 29 7 7
Treated 13 7 21
So I could do for example, a fisher test for:
Not marked Marked
Placebo 36 7
Treated 20 21
For now, what I want to do in shiny is allow the user to select two categorical variables (Treatment and Improved), and then filter by another one (Gender) and obtain the contingency table.
I could use later this one to obtain the 2x2 frequency. But for now this is what I have:
# Shiny
library(shiny)
library(shinyWidgets)
library(shinyjqui)
# Data
library(vcd)
library(readxl)
library(dplyr)
library(arules) # Discretization
# Plots
library(ggplot2)
not_sel <- "Not Selected"
ui <- fluidPage(
titlePanel("Plotter"),
sidebarPanel(
fileInput("xlsx_input", "Select XLSX file to import", accept = c(".xlsx")),
selectInput("num_var_1", "Variable X axis", choices = c(not_sel)),
selectInput("num_var_2", "Variable Y axis", choices = c(not_sel)),
selectInput("biomarker", "Select Biomarker", choices = c(not_sel)), uiOutput("factor")
),
mainPanel(
tabsetPanel(
tabPanel(
verbatimTextOutput("test")
)
)
)
)
## Server ##
server <- function(input, output){
# Dynamic selection of the data. We allow the user to input the data that they want
data_input <- reactive({
#req(input$xlsx_input)
#inFile <- input$xlsx_input
#read_excel(inFile$datapath, 1)
Arthritis
})
# We update the choices available for each of the variables
observeEvent(data_input(),{
choices <- c(not_sel, names(data_input()))
updateSelectInput(inputId = "num_var_1", choices = choices)
updateSelectInput(inputId = "num_var_2", choices = choices)
updateSelectInput(inputId = "biomarker", choices = choices)
})
num_var_1 <- eventReactive(input$run_button, input$num_var_1)
num_var_2 <- eventReactive(input$run_button, input$num_var_2)
biomarker <- eventReactive(input$run_button, input$biomarker)
output$factor <- renderUI({
req(input$biomarker, data_input())
if (input$biomarker != not_sel) {
b <- unique(data_input()[[input$biomarker]])
pickerInput(inputId = 'selected_factors',
label = 'Select factors',
choices = c(b[1:length(b)]), selected=b[1], multiple = TRUE,
# choices = c("NONE",b[1:length(b)]), selected="NONE", If we want "NONE" to appear as the first option
# multiple = TRUE, ## if you wish to select multiple factor values; then deselect NONE
options = list(`actions-box` = TRUE)) #options = list(`style` = "btn-warning"))
}
})
data_stats_discrete <- reactive({
req(data_input(), input$num_var_1, input$num_var_2, input$biomarker)
# We filter by biomarker in case user selected, otherwise data_input() remains the same
if (input$biomarker != "Not Selected") df <- data_input()[data_input()[[input$biomarker]] %in% input$selected_factors,]
else df <- data_input()
df <- as.data.frame.matrix(table(.data[[input$num_var_1]], .data[[input$num_var_2]]))
df
})
output$test <- renderPrint(data_stats_discrete())
}
shinyApp(ui = ui, server = server)
As you can see in this RepEx, no dataframe is being selected in the data_stats_discrete.
Change
df <- as.data.frame.matrix(table(.data[[input$num_var_1]], .data[[input$num_var_2]]))
to
df <- as.data.frame.matrix(table(df[[input$num_var_1]], df[[input$num_var_2]]))
I have struggled with the following issue and have found no suitable solution on SO.
Here is what I require from my DataTable
I would like to edit my DataTable (achieved)
Filter the data in DataTable with my edits intact. Currently, my edits disappear after I change the filters
Save whole DataTable as RDS rather than just the current displayed data based on filters. Currently, I just save the current displayed DataTable based on filters
Thank you for your help in advance!
df <- iris
species <- unique(as.character(df$Species))
width <- unique(df$Petal.Width)
#==========================================UI=======================================================#
ui = navbarPage("CSAT & SA", theme = shinytheme("flatly"),
tabPanel("Sentiment Analysis",
sidebarLayout(
sidebarPanel(
pickerInput(inputId = "species",
label = "Species", selected = species,
choices = species, multiple = T,
options = list(`actions-box` = TRUE, `deselect-all-text` = "None...",
`select-all-text` = "Select All", `none-selected-text` = "None Selected")),
pickerInput(inputId = "width",
label = "Petal Width", selected = width,
choices = width, multiple = T,
options = list(`actions-box` = TRUE, `deselect-all-text` = "None...",
`select-all-text` = "Select All", `none-selected-text` = "None Selected")),
width = 2,
actionButton(inputId = "save", label = "Save"),
actionButton(inputId = "update", label = "Update")
),
mainPanel(
h2("Iris"), fluidRow(
tabPanel("Iris", DT::dataTableOutput("x1"),
width = 12)
)))))
#==========================================SERVER=======================================================#
server <- function(input, output, session) {
SA <- reactive({
df<-df %>%
filter(Species %in% input$species) %>%
filter(Petal.Width %in% input$width)
})
rec_val = reactiveValues(df = NULL)
observe({
rec_val$SA <- SA()
})
output$x1 = renderDT(SA(), selection = 'none', editable = list(target = 'cell', disable = list(columns=c(0,1,2))))
proxy = dataTableProxy('x1')
observeEvent(input$x1_cell_edit, {
info = input$x1_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
rec_val$SA[i, j] <<- DT::coerceValue(v, rec_val$SA[i, j])
replaceData(proxy, rec_val$SA, resetPaging = FALSE)
})
observeEvent(input$save, {
saveRDS(rec_val$SA, "somewhere.rds") # write new data out
})
}
shinyApp(ui = ui, server = server)
Edit:
see here
You need to use updatePickerInput() to update the choices available based on the edits. Also, define row id to keep the modified data. Using reset you can return to the original datatable. Try this
library(shinythemes)
dat <- iris
species <- unique(as.character(dat$Species))
width <- unique(dat$Petal.Width)
#==========================================UI=======================================================#
ui = navbarPage("CSAT & SA", theme = shinytheme("flatly"),
tabPanel("Sentiment Analysis",
sidebarLayout(
sidebarPanel(
pickerInput(inputId = "species",
label = "Species", selected = species,
choices = as.list(species), multiple = T,
options = list(`actions-box` = TRUE, `deselect-all-text` = "None...",
`select-all-text` = "Select All", `none-selected-text` = "None Selected")),
pickerInput(inputId = "width",
label = "Petal Width", selected = width,
choices = as.list(width), multiple = T,
options = list(`actions-box` = TRUE, `deselect-all-text` = "None...",
`select-all-text` = "Select All", `none-selected-text` = "None Selected")),
width = 2,
actionButton(inputId = "save", label = "Save"),
actionButton(inputId = "reset", label = "Reset")
),
mainPanel(
h2("Iris"), fluidRow(
tabPanel("Iris", DT::dataTableOutput("x1"), DTOutput("x2"),
width = 12)
)))))
#==========================================SERVER=======================================================#
server <- function(input, output, session) {
SA <- reactive({
row_id <- c(1:nrow(dat))
data <- data.frame(dat,row_id)
data
})
rv = reactiveValues(df = NULL)
observe({
rv$df <- SA() %>%
filter(Species %in% isolate(input$species)) %>%
filter(Petal.Width %in% isolate(input$width))
})
observeEvent(input$species, {
df1 <- SA() ### orig data
df2 <- rv$df ### modified data
if (is.null(df2)){
rvdf <- SA()
}else{
vn <- colnames(df1)
vnx <- paste0(vn,".x")
vny <- paste0(vn,".y")
rvdf <- left_join(df1, df2, by="row_id") %>% transmute(var1 = get(!!vnx[1]), var2 = get(!!vnx[2]), var3 = get(!!vnx[3]),
var4 = ifelse(is.na(get(!!vny[4])), get(!!vnx[4]), get(!!vny[4])),
var5 = get(!!vnx[5]), # ifelse(is.na(get(!!vny[5])), get(!!vnx[5]), get(!!vny[5])),
row_id)
colnames(rvdf) <- vn
}
rv$df <- rvdf %>%
filter(Species %in% input$species) %>%
filter(Petal.Width %in% input$width)
})
observeEvent(input$width, {
df1 <- SA() ### orig data
df2 <- rv$df ### modified data
if (is.null(df2)){
rvdf <- SA()
}else{
vn <- colnames(df1)
vnx <- paste0(vn,".x")
vny <- paste0(vn,".y")
### keep modified data, if present; if not, keep original data
rvdf <- left_join(df1, df2, by="row_id") %>% transmute(var1 = get(!!vnx[1]), var2 = get(!!vnx[2]), var3 = get(!!vnx[3]),
var4 = ifelse(is.na(get(!!vny[4])), get(!!vnx[4]), get(!!vny[4])), ## keep modified data
var5 = get(!!vnx[5]), # ifelse(is.na(get(!!vny[5])), get(!!vnx[5]), get(!!vny[5])),
row_id)
colnames(rvdf) <- vn
}
rv$df <- rvdf %>%
filter(Species %in% input$species) %>%
filter(Petal.Width %in% input$width)
})
output$x1 <- renderDT(rv$df, selection = 'none',
editable = list(target = 'cell', disable = list(columns=c(0,1,2))),
options = list(
columnDefs = list(
list(
visible = FALSE,
targets = 6
)
)
)
)
proxy <- dataTableProxy('x1')
observeEvent(input$x1_cell_edit, {
info = input$x1_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
rv$df[i, j] <<- DT::coerceValue(v, rv$df[i, j])
#replaceData(proxy, rv$df, resetPaging = FALSE)
})
observeEvent(input$save, {
#choicess <- as.list(unique(c(as.character(rv$df[,5]), as.character(SA()[,5]))))
choicesp <- as.list(unique(c(rv$df[,4], SA()[,4])))
# updatePickerInput(session, inputId = "species", choices = choicess, selected=choicess)
updatePickerInput(session, inputId = "width", choices = choicesp, selected=choicesp)
saveRDS(rv$df, "somewhere.rds") # write new data out
df3 <- readRDS("C:/My Disk Space/_My Work/RStuff/GWS/somewhere.rds")
output$x2 <- renderDT({
df3
})
})
observeEvent(input$reset, {
rv$df <- SA()
# choicess <- unique(as.character(rv$df[,5]))
choicesp <- unique(SA()[,4])
# updatePickerInput(session, inputId = "species", choices = choicess, selected=choicess)
updatePickerInput(session, inputId = "width", choices = choicesp, selected=choicesp)
})
}
shinyApp(ui = ui, server = server)
I need to create an application where I filter multiple fields from a data frame. When the first field is filtered (using Date Range), the user then has to filter several pickerInputs before the data is displayed in a table. I'm not sure if this is the best way to create dependent filters. I cannot seem to find enough resources. I have tried the following. However, I'm not sure why I keep getting this warning::
Warning:Error in: Problem with filter() input '..1'
X Input '..1' must be of size 100 or 1, not size 0
get_data <- function(size){
longs <- seq(from=40, to =90, by = 0.01)
lats <- seq(from = 5, to= 50, by = 0.01)
LONGITUDE <- sample(longs, size, rep = TRUE)
LATITUDE <- sample(lats, size, rep = TRUE)
df <- data.frame(cbind(LONGITUDE, LATITUDE))
df$LOCATION <- sample(c("Location_A", "Location_B", "Location_C"), size, replace = T, prob = c(0.4, 0.4, 0.2))
df$EQUIPMENT <- sample(c("Equipment_A", "Equipment_B", "Equipment_C", "Equipment_D"), size, replace = TRUE)
startTime <- as.POSIXct("2016-01-01")
endTime <- as.POSIXct("2019-01-31")
df$DATE <- as.Date(sample(seq(startTime, endTime, 1), size))
df$WEEKDAY <- weekdays(as.Date(df$DATE))
return(df)
}
df <-get_data(100)
ui <- navbarPage(
id = "navBar",
title = "Data Exploration",
theme = shinytheme("cerulean"),
shinyjs::useShinyjs(),
selected = "Data",
tabPanel("Data",
fluidPage(
sidebarPanel(
div(id = "form",
uiOutput('timestamp'),
uiOutput('location'),
uiOutput('days_of_week'),
uiOutput('equipment_type'),
hr(),
HTML("<h3>Reset your filter settings here:</h3>"),
actionButton("resetAll", "Reset Entries"),
hr()),
mainPanel(
DT::DTOutput("datatable"))))
)
)#end the ui
server <- function(session, input, output){
filter_data <- reactive({
df %>%
filter(DATE >= input$timestamp[1] & DATE <= input$timestamp[2]) %>%
filter(LOCATION %in% input$location) %>%
filter(WEEKDAY %in% input$days_of_week) %>%
filter(EQUIPMENT %in% input$equipment_type)
})
output$timestamp <- renderUI({
dateRangeInput('timestamp',label = 'Date range input:',start = min(df$DATE), end = max(df$DATE))
})
output$location <- renderUI({
location <- reactive({
df %>%
filter(DATE >= input$timestamp[1] & DATE <= input$timestamp[2]) %>%
pull(LOCATION) %>%
as.character() %>% unique()
})
pickerInput('location', "Select Location:", choices = location(),selected = NULL, options = list(`actions-box` = TRUE),multiple = T)
})
output$days_of_week <- renderUI({
days_of_week <- reactive({
df %>%
filter(DATE >= input$timestamp[1] & DATE <= input$timestamp[2]) %>%
filter(LOCATION %in% input$location) %>%
pull(WEEKDAY) %>%
as.character() %>% unique()
})
pickerInput('days_of_week', 'Choose Weekdays:', choices=days_of_week(), selected = NULL, options = list(`actions-box` = TRUE),multiple = T)
})
output$equipment_type <- renderUI({
equipment <- reactive({
df %>%
filter(DATE >= input$timestamp[1] & DATE <= input$timestamp[2]) %>%
filter(LOCATION%in% input$location) %>%
filter(WEEKDAY %in% input$days_of_week) %>%
pull(EQUIPMENT) %>%
as.character() %>% unique()
})
pickerInput('equipment_type', "Choose Equipment:", choices = equipment(),selected = NULL, options = list(`actions-box` = TRUE),multiple = T)
})
output$datatable <- DT::renderDT({
filter_data()
})
#Allow the user to reset all their inputs
observeEvent(input$resetAll, {
reset("form")
})
}
shinyApp(ui, server)
I think your warnings are due to input$timestamp being NULL the first time in your reactive expressions, before you create the dateRangeInput.
You could move your input to ui, and then use updatePickerInput when the dates change to alter your other inputs accordingly.
You might want to include two separate reaction expressions. One for filtering the data based on the date range, which will be used to update the other pickers. The second will include the other filters for location, equipment, and weekday, based on the picker selections.
See if this provides something closer to what you are looking for. I included what seemed to be the relevant packages at the top. I also adjusted your parentheses in the ui a bit.
library(shinythemes)
library(shinyWidgets)
library(shinyjs)
library(shiny)
library(dplyr)
get_data <- function(size){
longs <- seq(from=40, to =90, by = 0.01)
lats <- seq(from = 5, to= 50, by = 0.01)
LONGITUDE <- sample(longs, size, rep = TRUE)
LATITUDE <- sample(lats, size, rep = TRUE)
df <- data.frame(cbind(LONGITUDE, LATITUDE))
df$LOCATION <- sample(c("Location_A", "Location_B", "Location_C"), size, replace = T, prob = c(0.4, 0.4, 0.2))
df$EQUIPMENT <- sample(c("Equipment_A", "Equipment_B", "Equipment_C", "Equipment_D"), size, replace = TRUE)
startTime <- as.POSIXct("2016-01-01")
endTime <- as.POSIXct("2019-01-31")
df$DATE <- as.Date(sample(seq(startTime, endTime, 1), size))
df$WEEKDAY <- weekdays(as.Date(df$DATE))
return(df)
}
df <-get_data(100)
ui <- navbarPage(
id = "navBar",
title = "Data Exploration",
theme = shinytheme("cerulean"),
shinyjs::useShinyjs(),
selected = "Data",
tabPanel("Data",
fluidPage(
sidebarPanel(
div(id = "form",
dateRangeInput('timestamp', label = 'Date range input:', start = min(df$DATE), end = max(df$DATE)),
pickerInput('location', "Select Location:", choices = unique(df$LOCATION), options = list(`actions-box` = TRUE), multiple = T),
pickerInput('days_of_week', 'Choose Weekdays:', choices = unique(df$WEEKDAY), options = list(`actions-box` = TRUE), multiple = T),
pickerInput('equipment_type', "Choose Equipment:", choices = unique(df$EQUIPMENT), options = list(`actions-box` = TRUE), multiple = T),
hr(),
HTML("<h3>Reset your filter settings here:</h3>"),
actionButton("resetAll", "Reset Entries"),
hr())
),
mainPanel(
DT::DTOutput("datatable")))
)
)#end the ui
server <- function(session, input, output){
filter_by_dates <- reactive({
filter(df, DATE >= input$timestamp[1] & DATE <= input$timestamp[2])
})
filter_by_all <- reactive({
fd <- filter_by_dates()
if (!is.null(input$location)) {
fd <- filter(fd, LOCATION %in% input$location)
}
if (!is.null(input$days_of_week)) {
fd <- filter(fd, WEEKDAY %in% input$days_of_week)
}
if (!is.null(input$equipment_type)) {
fd <- filter(fd, EQUIPMENT %in% input$equipment_type)
}
return(fd)
})
observeEvent(input$timestamp, {
updatePickerInput(session, 'location', "Select Location:", choices = unique(filter_by_dates()$LOCATION), selected = input$location)
updatePickerInput(session, 'days_of_week', 'Choose Weekdays:', choices = unique(filter_by_dates()$WEEKDAY), selected = input$days_of_week)
updatePickerInput(session, 'equipment_type', "Choose Equipment:", choices = unique(filter_by_dates()$EQUIPMENT), selected = input$equipment_type)
})
output$datatable <- DT::renderDT({
filter_by_all()
})
#Allow the user to reset all their inputs
observeEvent(input$resetAll, {
reset("form")
})
}
shinyApp(ui, server)
Edit (1/28/21): Based on the comment, it sounds like there is interest in updating all the input choices based on selections made.
If you substitute observeEvent with an observe, and use filter_by_all() instead of filter_by_date() in the three updatePickerInput, then all the non-date input choices will update whenever any changes are made to any input:
observe({
input$timestamp
updatePickerInput(session, 'location', "Select Location:", choices = unique(filter_by_all()$LOCATION), selected = input$location)
updatePickerInput(session, 'days_of_week', 'Choose Weekdays:', choices = unique(filter_by_all()$WEEKDAY), selected = input$days_of_week)
updatePickerInput(session, 'equipment_type', "Choose Equipment:", choices = unique(filter_by_all()$EQUIPMENT), selected = input$equipment_type)
})
I am trying to create a reactive function with the isolate command from the following code in the ui.R file into the server.R file where the data table only populates after the user has inputed their selections and filters only.
Right now the data table just populates on its own after running the filters and selections without having to click on the Run Query button.
Any help would be appreciated!
actionButton("runit", "RUN QUERY")
Thank you so much!
Code below:
ui.R
library(DT)
library(shiny)
library(shinydashboard)
ui <- function(request) {
dashboardPage(
dashboardHeader(title = "CL Pivot"),
dashboardSidebar(
actionButton("runit", "RUN QUERY"),
h4(HTML(" "), "Select Table Rows"),
uiOutput("rowSelect"),
hr(),
h4(HTML(" "), "Select Table Columns"),
uiOutput("colSelect"),
hr(),
h4(HTML(" "), "Select Table Cell Fill"),
selectizeInput(
inputId = "funChoices",
label = NULL,
multiple = FALSE,
choices = c("Count", "Average", "Median", "Sum", "Maximum", "Minimum"),
selected = c()
),
hr(),
h4(HTML(" "), "Filter Data Set"),
uiOutput("hairColorFilter"),
uiOutput("skinColorFilter")
),
dashboardBody(dataTableOutput("data"))
)
}
server.R
library(DT)
library(shiny)
library(shinydashboard)
library(dplyr)
library(dbplyr)
library(tidyverse)
library(DBI)
data <- starwars
# Convenience Function to Make Upcoming Chain Less Messy
fun_across <- function(cols, fun, fun_name) {
fun_list <- list(fun)
names(fun_list) <- fun_name
across(all_of(cols), fun_list, .names = "{fn}_{col}")
}
shinyServer(function(input, output, session) {
# Identify Measures and Dimensions -------------
dimensions <- colnames(data)[!sapply(data, is.numeric)]
measures <- colnames(data)[sapply(data, is.numeric)]
# Identify Filter Choices -----------------------------------------------
hairColorChoices <- sort(unique(data$hair_color))
skinColorChoices <- sort(unique(data$skin_color))
# Define User Inputs ----------------------------------------------------
output$rowSelect <- renderUI({
selectizeInput(
inputId = "rowChoices",
label = NULL,
multiple = TRUE,
choices = dimensions,
selected = c()
)
})
output$colSelect <- renderUI({
selectizeInput(
inputId = "colChoices",
label = NULL,
multiple = TRUE,
choices = measures,
selected = c()
)
})
output$hairColorFilter <- renderUI({
sidebarMenu(
menuItem(
text = "Hair Color",
icon = icon("briefcase"),
checkboxGroupInput(
inputId = "hairColorChoices",
label = NULL,
choices = hairColorChoices,
selected = hairColorChoices
)
)
)
})
output$skinColorFilter <- renderUI({
sidebarMenu(
menuItem(
text = "Skin Color",
icon = icon("thermometer-half"),
checkboxGroupInput(
inputId = "skinColorChoices",
label = NULL,
choices = skinColorChoices,
selected = skinColorChoices
)
)
)
})
# Define Reactive Functions ---------------------------------------------
pairColFuns <- reactive({
colChoices <- input$colChoices
names(colChoices) <- input$funChoices
return(colChoices)
})
# Construct DataFrame Based on User Inputs
output$data <- renderDataTable({
colChoices <- pairColFuns()
rowChoices <- input$rowChoices
countCols <- unname(colChoices[names(colChoices) == "Count"])
averageCols <- unname(colChoices[names(colChoices) == "Average"])
medianCols <- unname(colChoices[names(colChoices) == "Median"])
sumCols <- unname(colChoices[names(colChoices) == "Sum"])
maxCols <- unname(colChoices[names(colChoices) == "Maximum"])
minCols <- unname(colChoices[names(colChoices) == "Minimum"])
displayTable <- as_tibble(data) %>%
filter(
hair_color %in% input$hairColorChoices,
skin_color %in% input$skinColorChoices
) %>%
group_by(across(all_of(rowChoices))) %>%
summarize(
# Once again we've sacrificed a bit of elegance for clarity. This chunk will
# apply the specified function to whichever columns are included in the
# specified variable. If the variable is empty, no operation is performed.
fun_across({{countCols}}, length, "count"),
fun_across({{averageCols}}, ~mean(.x, na.rm = TRUE), "average"),
fun_across({{medianCols}}, ~median(.x, na.rm = TRUE), "median"),
fun_across({{sumCols}}, ~sum(.x, na.rm = TRUE), "total"),
fun_across({{maxCols}}, ~max(.x, na.rm = TRUE), "max"),
fun_across({{minCols}}, ~min(.x, na.rm = TRUE), "min"),
.groups = "drop"
)
return(displayTable)
})
})
I would split the table rendering and data processing, then you can use the eventReactive approach. This saves you to wrap every input into isolate.
First make an eventReactive that calculates your data. It only updates if the first reactive/input changes. Then you can use this to render your table:
table_data <- eventReactive(input$runit, {
colChoices <- pairColFuns()
rowChoices <- input$rowChoices
countCols <- unname(colChoices[names(colChoices) == "Count"])
averageCols <- unname(colChoices[names(colChoices) == "Average"])
medianCols <- unname(colChoices[names(colChoices) == "Median"])
sumCols <- unname(colChoices[names(colChoices) == "Sum"])
maxCols <- unname(colChoices[names(colChoices) == "Maximum"])
minCols <- unname(colChoices[names(colChoices) == "Minimum"])
displayTable <- as_tibble(data) %>%
filter(
hair_color %in% input$hairColorChoices,
skin_color %in% input$skinColorChoices
) %>%
group_by(across(all_of(rowChoices))) %>%
summarize(
# Once again we've sacrificed a bit of elegance for clarity. This chunk will
# apply the specified function to whichever columns are included in the
# specified variable. If the variable is empty, no operation is performed.
fun_across({{countCols}}, length, "count"),
fun_across({{averageCols}}, ~mean(.x, na.rm = TRUE), "average"),
fun_across({{medianCols}}, ~median(.x, na.rm = TRUE), "median"),
fun_across({{sumCols}}, ~sum(.x, na.rm = TRUE), "total"),
fun_across({{maxCols}}, ~max(.x, na.rm = TRUE), "max"),
fun_across({{minCols}}, ~min(.x, na.rm = TRUE), "min"),
.groups = "drop"
)
displayTable
})
output$data <- renderDataTable({
table_data()
})
You need to isolate() all inputs that shouldn't trigger the event, and you could use req() to enable the submit button:
pairColFuns <- reactive({
colChoices <- isolate(input$colChoices) #isolated
names(colChoices) <- isolate(input$funChoices) #isolated
return(colChoices)
})
# Construct DataFrame Based on User Inputs
output$data <- renderDataTable({
req(input$runit) # submit button should trigger
colChoices <- pairColFuns()
rowChoices <- isolate(input$rowChoices) #isolated
countCols <- unname(colChoices[names(colChoices) == "Count"])
averageCols <- unname(colChoices[names(colChoices) == "Average"])
medianCols <- unname(colChoices[names(colChoices) == "Median"])
sumCols <- unname(colChoices[names(colChoices) == "Sum"])
maxCols <- unname(colChoices[names(colChoices) == "Maximum"])
minCols <- unname(colChoices[names(colChoices) == "Minimum"])
displayTable <- as_tibble(data) %>%
filter(
hair_color %in% isolate(input$hairColorChoices), #isolated
skin_color %in% isolate(input$skinColorChoices) #isolated
...