I am trying to create my first shiny app but I am facing a difficulty: in the reproducible example below I am creating a reactive pickerInput (i.e. only show brands proposing a cylindre equal to the input visitors select).
I then want that based on the combination input_cyl and picker_cny (remember that picker_cny depends on input_cyl) to display a table which shows the relevant data for the observation matching the combination input_cyl and picker_cny.
Thank you for your help!
df <- mtcars
df$brand <- rownames(mtcars)
df$brand <- gsub("([A-Za-z]+).*", "\\1", df$brand)
if (interactive()) {
library(shiny)
library(shinyWidgets)
library(shinythemes)
library(shinycssloaders)
# Define UI -----------------------------------------------
ui <- fluidPage(
# Application title
titlePanel("Reproducible Example"),
# Parameters
sidebarLayout(
sidebarPanel(
selectInput(inputId = "input_cyl", label = "Cyl",
choices = c("6", "4", "8")),
pickerInput(
inputId = "picker_cny",
label = "Select Company",
choices = paste0(unique(df$brand)),
options = list(`actions-box` = TRUE),
multiple = TRUE),
width = 2),
# Show Text
mainPanel(
tableOutput("table"),
width = 10)
))
# Define Server ------------------------------------------
server <- function(input, output, session) {
# Reactive pickerInput ---------------------------------
observeEvent(input$input_cyl, {
df_mod <- df[df$cyl == paste0(input$input_cyl), ]
# Method 1
disabled_choices <- !df$cyl %in% df_mod$cyl
updatePickerInput(session = session,
inputId = "picker_cny",
choices = paste0(unique(df$brand)),
choicesOpt = list(
disabled = disabled_choices,
style = ifelse(disabled_choices,
yes = "color: rgba(119, 119, 119, 0.5);",
no = "")
))
}, ignoreInit = TRUE)
output$table <- renderTable(df)
}
}
# Run the application
shinyApp(ui = ui, server = server)
You need a reactive that will handle the change in the input and subset the dataframe before giving it to the output table. For that, you just need to add this block to your server:
data <- reactive({
if (length(input$picker_cny) > 0)
df[df$brand %in% input$picker_cny,]
else
df
})
and update the output$table like this:
output$table <- renderTable(data())
Note: feel free to remove the if else in the reactive to get that:
data <- reactive({
df[df$brand %in% input$picker_cny,]
})
The only difference in that case is: would you show all or nothing when no input has been entered yet. That's a matter of taste.
Related
I have data table output that I want users to be able to create their own custom table by using checkboxes to select which row/element they want. In the example below is a mtcars output. For example I want users to be able to pick say A Mazda, Fiat, Toyota, and a Dodge model using a check box. As far as trying any code, I haven't found any examples that come close.
library(shiny)
if (interactive()) {
# basic example
shinyApp(
ui = fluidPage(
selectInput("variable", "Variable:",
c("Cylinders" = "cyl",
"Transmission" = "am",
"Gears" = "gear"), multiple = T),
tableOutput("data")
),
server = function(input, output) {
output$data <- renderTable({
mtcars[, c("mpg", input$variable), drop = FALSE]
}, rownames = TRUE)
}
)
}
The general approach below is 1) create a checkbox group input listing the car names (i.e. rownames) as the names, having the corresponding values be the row numbers and 2) using those row numbers to filter your data.frame on the server.
Using the reactive rowsToUse will update every time the selection changes. It also allows the handling of the case when no rows are selecting (default to all rows in the example below).
shinyApp(
ui = fluidPage(
checkboxGroupInput(
inputId = "variable",
label = "Cars:",
choiceNames = rownames(mtcars),
choiceValues = seq(NROW(mtcars))
),
tableOutput("data")
),
server = function(input, output) {
rowsToUse <- reactive(
if(is.null(input$variable)) {
seq(NROW(mtcars))
} else{
as.numeric(input$variable)
}
)
output$data <- renderTable({
mtcars[rowsToUse(), , drop = FALSE]
}, rownames = TRUE)
}
)
I am building a shiny app with a selectize input.
The choices in the input are dependent upon the ids in the underlying data.
In my real app, the data updates with a call to an API.
I would like the selected id choice in the selectize input to hold constant when I hit the "update data" button.
I was able to do this prior to using shiny modules. However, when I tried to transform my code to use a shiny module, it fails to hold the selected id value, and resets the selectize input each time I update the underlying data.
The following example was helpful without the module, but when I use the module it doesn't seem to work...link here
Below is a reprex. Thanks for any help.
library(shiny)
library(tidyverse)
# module UI
mymod_ui <- function(id){
ns <- NS(id)
tagList(
uiOutput(ns("ids_lookup")),
)
}
# module server
mymod_server <- function(input, output, session, data, actionb){
ns <-session$ns
ids <- reactive(
data() %>%
filter(!is.na(first_name) & !is.na(last_name) & !is.na(ages)) %>%
mutate(ids = paste(first_name, last_name, sep = " ")) %>%
select(ids)
)
output$ids_lookup <- renderUI({
selectizeInput(ns("lookup"),
label = "Enter id:",
choices = c("Type here ...", ids()), multiple = FALSE)
})
# here is where I would like to hold on to the selected ids when updating the table
# when I click the "reload_data" button I don't want the name to change
# I pass the button from the main server section into the module
current_id_selection <- reactiveVal("NULL")
observeEvent(actionb(), {
current_id_selection(ns(input$ids_lookup))
updateSelectizeInput(session,
inputId = ns("lookup"),
choices = ids(),
selected = current_id_selection())
})
}
ui <- fluidPage(
titlePanel("Test module app"),
br(),
# this button reloads the data
actionButton(
inputId = "reload_data",
label = "Reload data"
),
br(),
br(),
# have a look at the data
h4("Raw data"),
tableOutput("mytable"),
br(),
# now select a single id for further analysis in a much larger app
mymod_ui("mymod"),
)
server <- function(input, output, session) {
df <- eventReactive(input$reload_data, {
# in reality, df is a dataframe which is updated from an API call everytime you press the action button
df <- tibble(
first_name = c("john", "james", "jenny", "steph"),
last_name = c("x", "y", "z", NA),
ages = runif(4, 30, 60)
)
return(df)
}
)
output$mytable <- renderTable({
df()
})
# make the reload data button a reactive val that can be passed to the module for the selectize Input
mybutton <- reactive(input$reload_data)
callModule(mymod_server, "mymod", data = df, actionb = mybutton)
}
shinyApp(ui, server)
Just using inputId = "lookup" instead of inputId = ns("lookup") in updateSelectizeInput() will do it. Also, you had another typo in there. Try this
library(shiny)
library(tidyverse)
# module UI
mymod_ui <- function(id){
ns <- NS(id)
tagList(
uiOutput(ns("ids_lookup")),
verbatimTextOutput(ns("t1"))
)
}
# module server
mymod_server <- function(input, output, session, data, actionb){
ns <-session$ns
ids <- reactive(
data() %>%
filter(!is.na(first_name) & !is.na(last_name) & !is.na(ages)) %>%
mutate(ids = paste(first_name, last_name, sep = " ")) %>%
select(ids)
)
output$ids_lookup <- renderUI({
selectizeInput(ns("lookup"),
label = "Enter id:",
choices = c("Type here ...", ids()), multiple = FALSE)
})
# here is where I would like to hold on to the selected ids when updating the table
# when I click the "reload_data" button I don't want the name to change
# I pass the button from the main server section into the module
current_id_selection <- reactiveValues(v=NULL)
observeEvent(actionb(), {
req(input$lookup)
current_id_selection$v <- input$lookup
output$t1 <- renderPrint(paste0("Current select is ",current_id_selection$v))
updateSelectizeInput(session,
inputId = "lookup",
choices = ids(),
selected = current_id_selection$v )
})
}
ui <- fluidPage(
titlePanel("Test module app"),
br(),
# this button reloads the data
actionButton(inputId = "reload_data", label = "Reload data"
),
br(),
br(),
# have a look at the data
h4("Raw data"),
tableOutput("mytable"),
br(),
# now select a single id for further analysis in a much larger app
mymod_ui("mymod")
)
server <- function(input, output, session) {
df <- eventReactive(input$reload_data, {
# in reality, df is a dataframe which is updated from an API call everytime you press the action button
df <- tibble(
first_name = c("john", "james", "jenny", "steph"),
last_name = c("x", "y", "z", NA),
ages = runif(4, 30, 60)
)
return(df)
})
output$mytable <- renderTable({
df()
})
# make the reload data button a reactive val that can be passed to the module for the selectize Input
mybutton <- reactive(input$reload_data)
callModule(mymod_server, "mymod", data = df, actionb = mybutton)
}
shinyApp(ui, server)
The user interface of the Shiny app I'm working on is supposed to work in the following manner:
User finds the desired observation(s) after applying a set of filters.
User clicks "Add" action button, so selected observation(s) are added to a running list/vector/etc of observations to be analyzed.
User modifies filters to find other observations which are to be included as well.
Loop back to step 1 as many times as user desires.
I cannot seem to find a way to save this list of observations to be analyzed. In the example I attached, the "observation ID" is the name of the model of the car (mtcars is used). I also did not include any data analysis, since I do not think that's necessary. In essence, the entire dataset (mtcars) should be filtered using dplyr in a reactive environment to only include the running list of selected observations.
Here's the code:
data("mtcars")
mtcars$model <- rownames(mtcars)
ui <- fluidPage(
titlePanel("sample"),
sidebarLayout(
sidebarPanel(
uiOutput("disp"),
uiOutput("qsec"),
uiOutput("model"),
actionButton("add", "Add"),
uiOutput("selectedModel")
),
mainPanel(
plotOutput("data_analysis")
)
)
)
server <- function(input, output) {
output$disp <- renderUI({
selectInput(
"disp_sel",
"Select disp:",
unique(mtcars$disp),
selected = NULL,
multiple = T,
selectize = T
)
})
output$qsec <- renderUI({
temp = mtcars
if (!is.null(input$disp_sel)){temp = temp %>% filter(disp %in% input$disp_sel)}
selectInput(
"qsec_sel",
"Select qsec:",
unique(temp$qsec),
selected = NULL,
multiple = T,
selectize = T
)
})
output$model <- renderUI({
temp = mtcars
if (!is.null(input$disp_sel)){temp = temp %>% filter(disp %in% input$disp_sel)}
if (!is.null(input$qsec_sel)){temp = temp %>% filter(qsec %in% input$qsec_sel)}
selectInput(
"model_sel",
"Select model:",
unique(temp$model),
selected = NULL,
multiple = T,
selectize = T
)
})
output$selectedModel <- renderUI({
req(input$add)
selectInput(
"list_of_selections",
"Selected models:",
unique(mtcars$model),
selected = NULL, # this should change when "Add" is pressed
multiple = T,
selectize = T
)
})
r_data = eventReactive(input$add,{
mtcars %>% filter(model %in% input$list_of_selections)
})
output$data_analysis <- renderPlot({
# do something with r_data (filtered data)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I've looked into modular code, reactive lists, and other stuff I don't even remember... Any help is greatly appreciated.
Try this
data("mtcars")
mtcars$model <- rownames(mtcars)
df1 <- mtcars
ui <- fluidPage(
titlePanel("sample"),
sidebarLayout(
sidebarPanel(
uiOutput("disp"),
uiOutput("qsec"),
uiOutput("model"),
actionButton("add", "Add"),
uiOutput("selectedModel")
),
mainPanel(
DTOutput("selecteddata"),
plotOutput("data_analysis")
)
)
)
server <- function(input, output) {
output$disp <- renderUI({
selectInput(
"disp_sel",
"Select disp:",
unique(mtcars$disp),
selected = NULL,
multiple = T,
selectize = T
)
})
output$qsec <- renderUI({
temp = mtcars
if (!is.null(input$disp_sel)){temp = temp %>% filter(disp %in% input$disp_sel)}
selectInput(
"qsec_sel",
"Select qsec:",
unique(temp$qsec),
selected = NULL,
multiple = T,
selectize = T
)
})
output$model <- renderUI({
temp = mtcars
if (!is.null(input$disp_sel)){temp = temp %>% filter(disp %in% input$disp_sel)}
if (!is.null(input$qsec_sel)){temp = temp %>% filter(qsec %in% input$qsec_sel)}
selectInput(
"model_sel",
"Select model:",
unique(temp$model),
selected = NULL,
multiple = T,
selectize = T
)
})
selected_data <- eventReactive(input$add,{
df1 %>% filter(model %in% input$model_sel)
})
output$selecteddata <- renderDT(
selected_data(), # reactive data
class = "display nowrap compact", # style
filter = "top", # location of column filters
options = list( # options
scrollX = TRUE # allow user to scroll wide tables horizontally
)
)
output$selectedModel <- renderUI({
req(input$add)
selectInput(
"list_of_selections",
"Selected models:",
choices = unique(selected_data()$model),
selected = unique(selected_data()$model), # this should change when "Add" is pressed
multiple = T,
selectize = T
)
})
r_data = eventReactive(input$add,{
mtcars %>% filter(model %in% input$list_of_selections)
})
output$data_analysis <- renderPlot({
ggplot(data=selected_data(), aes(x=disp, y=qsec)) + geom_point()
# do something with r_data (filtered data)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Found the answer. I included
selected <- reactiveValues(s = NULL)
observeEvent(input$add,{selected$s = c(selected$s, input$model})
into the server part. Then the selected models are stored in selected$s.
I have the dataframe below:
DF2 = data.frame(agency_postcode = factor(rep(c(12345,45678,24124,32525,32325),2)),
car_group=factor(rep(c("Microcar","City car","Supermini","Compact","SUV"),2)),
transmission=factor(rep(c("automatic","manual"),5)))
which I use and display as rhandsontable in order to create a second table. First you are supposed to select one or more options from filter by input and then a level from the selected filter(s). Then you press search. What I basically want to do is subset the second table based on the first row of every selected column of the first table. The issue is in line 30 of server.r in which I should give the input$sel
#ui.r
library(shiny)
library(rhandsontable)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(width=2,
selectInput("sel","Filter by:",
choices = c("agency_postcode","date_start","days","car_group","transmission","driver_age"),
multiple=T,selected = "agency_postcode"),
actionButton("sr","Search")
),
mainPanel(
fluidRow(
column(4,offset = 0, style='padding:0px;',rHandsontableOutput("hot")),
column(8,offset = 0, style='padding:0px;',rHandsontableOutput("hot2"))
)
)
)
)
#server.r
#server.r
library(shiny)
library(rhandsontable)
library(jsonlite)
server <- function(input, output) {
#Create rhandsontable as a reactive expression
DFR2<-reactive({
rhandsontable(DF2[1,1:2], rowHeaders = NULL,height = 200)%>%
hot_col(colnames(DF2)[1:2])
})
#Display the rhandsontable
output$hot <- renderRHandsontable({
DFR2()
})
#Convert the rhandsontable to a daraframe
DFR3<-reactive({
req(input$hot)
hot_to_r(input$hot)
})
#Subset the initial dataframe by value of the 1st row-1st column cell of DF3
DFR4 <- reactive({
req(DFR3())
D<-DF2[ which(DF2[,1] %in% DFR3()[1, 1]), ] #input$sel is supposed to be used here instead of 1
for(i in 1:ncol(D)){
D[,i] <- factor(D[,i])
}
D
})
#Display the new rhandsontable
output$hot2 <- renderRHandsontable({
input$sr
isolate(rhandsontable(DFR4()[1,], rowHeaders = NULL,height = 200)%>%
hot_col(colnames(DFR4())) )
})
}
OK. Here is an app that uses a small table to filter a larger one using inner_join. I am not sure this will match the design you had in mind. It is still unclear to me where the filter levels are coming from, or what the hands on tables are for. But you should be able to adapt this approach to your design. Note also that I am not using hands on tables. A direct replacement of the calls to renderTable with renderRHandsontable should work too.
library(shiny)
library(dplyr)
library(purrr)
sub_cars <- mtcars[, c("cyl", "gear", "am")]
ui <- fluidPage(
column(width=3,
selectInput(
inputId = "sel_col",
label = "Select variables",
multiple = TRUE,
choices = c("cyl", "gear", "am"),
selectize = TRUE),
uiOutput("cyl"),
uiOutput("gear"),
uiOutput("am")
),
column(width = 3,
tableOutput("filter_table")),
column(width = 6,
tableOutput("large_table"))
)
server <- function(input, output) {
output$cyl <- renderUI({
if ("cyl" %in% input$sel_col) {
selectInput(
inputId = "sel_cyl",
label = "Select cylinders",
choices = unique(sub_cars$cyl),
multiple = TRUE,
selectize = TRUE
)
}
})
output$gear <- renderUI({
if ("gear" %in% input$sel_col) {
selectInput(
inputId = "sel_gear",
label = "Select gears",
choices = unique(sub_cars$gear),
multiple = TRUE,
selectize = TRUE
)
}
})
output$am <- renderUI({
if ("am" %in% input$sel_col) {
selectInput(
inputId = "sel_am",
label = "Select am",
choices = unique(sub_cars$am),
multiple = TRUE,
selectize = TRUE
)
}
})
# make a small filter table
filter_df <- reactive({
validate(
need(!is_null(input$sel_col),
message = "Please select a column"))
cols <- input$sel_col
cols_vals <- map(cols, function(x) input[[paste0("sel_", x, collapse="")]])
df <- map2_dfr(cols, cols_vals, function(x, y)
filter(sub_cars,!!as.name(x) %in% y)) %>%
select(one_of(cols)) %>%
distinct()
return(df)
})
output$filter_table <- renderTable({
validate(
need(nrow(filter_df()) > 0,
message = "Please select filter values"))
filter_df()
})
# inner join the larger table
large_df <- reactive({
validate(
need(nrow(filter_df()) > 0,
message = "Please select filter values"))
cols <- input$sel_col
inner_join(x=filter_df(), y=mtcars, by = cols)
})
output$large_table <- renderTable({large_df()})
}
shinyApp(ui, server)
Here is a gif of what it does.
I am trying to make a Shiny app that will plot gene of interest for a chosen patient. Each row is the gene name, and each column is a patient ID. For example:
99901 99902 99903 99904
SKI 4.789 5.789 6.324 1.2222
VWA1 6.901 7.002 5.89 4.567
TTLL10 6.783 7.345 8.987 6.345
library(shiny)
library(shinythemes)
library(lattice)
anno <- as.matrix(anno_genExp_gen3[1:3, 1:3])
#Define UI
ui <- fluidPage(
sidebarPanel(
titlePanel(title = "Gen3 Gene Expression Data"),
selectInput(inputId = "patients",
label = strong("Please choose patient/s to examine"),
choices = colnames(anno_genExp_off[,1:25]),
multiple = TRUE),
selectInput(inputId = "geneExp",
label = "Please select gene expressions/s to examine",
choices = rownames(anno_genExp_off[1:25,]),
multiple = TRUE)),
mainPanel(plotOutput("testPlot"))
)
server <- function(input, output) {
pdata <- reactive(input$patients)
gdata <-reactive(input$geneExp)
output$testPlot <- renderPlot ({
levelplot(anno,
col.regions=colorRampPalette(c("red","green","blue")))
})
}
shinyApp(ui = ui, server = server)
The code above just plots a small matrix, but how do I get it to plot user inputs using reactivity?
If the user chooses SKI and TTlLL10 only for patient 99901, how will I go about plotting this?
I've myself created a sample dataframe as you mentioned above. Here's the modified code.
Changes i made:
input$geneExp and input$patients are already reactive so there is no need to use a separate reactive function.
Filtered the dataframe for plotting use the same
Also, made a default selected value in the selectInput to avoid the initial error message when nothing is selected
library(shiny)
library(shinythemes)
library(lattice)
anno_genExp_off <- data.frame(`99901` = c(4.3,6.5,6.6),
`99902` = c(5.3,7.5,8.6),
`99903` = c(6.3,8.5,9.6),
row.names = c("SKI","VWA1","TTLL10"))
anno <- as.matrix(anno_genExp_off)
#Define UI
ui <- fluidPage(
sidebarPanel(
titlePanel(title = "Gen3 Gene Expression Data"),
selectInput(inputId = "patients",
label = strong("Please choose patient/s to examine"),
choices = colnames(anno_genExp_off),
selected = colnames(anno_genExp_off)[1],
multiple = TRUE),
selectInput(inputId = "geneExp",
label = "Please select gene expressions/s to examine",
choices = rownames(anno_genExp_off),
selected = rownames(anno_genExp_off)[1],
multiple = TRUE)),
mainPanel(plotOutput("testPlot"))
)
server <- function(input, output) {
#pdata <- reactive(input$patients)
#gdata <-reactive(input$geneExp)
output$testPlot <- renderPlot ({
levelplot(x = as.matrix(anno_genExp_off[which(rownames(anno_genExp_off) %in% input$geneExp) ,input$patients]),
col.regions=colorRampPalette(c("red","green","blue")))
})
}
shiny::shinyApp(ui,server)