I'm trying to add a dynamic ggvis plot to a Shiny app. First, user picks a dimension, and then adds items from that dimension.
For global.R and sample data, see https://gist.github.com/tts/a41c8581b9d77f131b31
server.R:
shinyServer(function(input, output, session) {
# Render a selectize drop-down selection box
output$items <- renderUI({
selectizeInput(
inputId = 'items',
label = 'Select max 4. Click to delete',
multiple = TRUE,
choices = aalto_all[ ,names(aalto_all) %in% input$dim],
options = list(maxItems = 4, placeholder = 'Start typing')
)
})
selected <- reactive({
if (is.null(input$items)) {
return(aalto_all)
}
df <- aalto_all[aalto_all[[input$dim]] %in% input$items, ]
df$keys <-seq(1, nrow(df))
df
})
selected %>%
ggvis(~WoS, ~NrOfAuthors, fill = ~School, key := ~keys) %>%
layer_points() %>%
add_tooltip(show_title) %>%
bind_shiny("gv")
show_title <- function(x=NULL) {
if(is.null(x)) return(NULL)
key <- x["keys"][[1]]
selected()$Title20[key]
}
})
ui.R:
shinyUI(fluidPage(
titlePanel('Some (alt)metric data for articles published since 2010'),
sidebarLayout(
sidebarPanel(
selectInput(
inputId = "dim",
label = "Dimension",
choices = dimensions,
selected = c("Title")),
uiOutput("items")
),
mainPanel(
tabsetPanel(
# I'll add more tabs
tabPanel("Plot with ggvis", ggvisOutput("gv"))
)
)
)
))
This is OK
in the beginning, when there are no items selected, and all data is plotted. This is a hack because the ggvis object throws an error if there is no data served.
when all selected items are deleted (which is the same as 1.) and another dimension is chosen
But when I try to switch to another dimension without deleting the items first, I get this:
Error in `$<-.data.frame`(`*tmp*`, "keys", value = c(1L, 0L)) :
replacement has 2 rows, data has 0
I understand that ggvis is very new and constantly developing, but I suspect that there is merely something in Shiny reactive values that is out of sync. If anyone could point out what I'm doing wrong, thanks a lot!
The error is caused because you have a data.frame with zero rows and have a resulting 1:0.
You can change your selected function to:
selected <- reactive({
if (is.null(input$items)) {
return(aalto_all)
}
df <- aalto_all[aalto_all[[input$dim]] %in% input$items, ]
df$keys <-seq_along(df[,1])
if(nrow(df) == 0){
return(aalto_all)
}
df
})
Related
I am trying to build an interactive data table that changes the displayed columns based on filters chosen by the user. The aim is to have a user select the columns they want to see via a dropdown, which will then cause the datatable to display those columns only.
library(shinyWidgets)
library(DT)
ui <-
fluidPage(
fluidRow(
box(width = 4,
pickerInput(inputId = "index_picker",
label = "Select index/indices",
choices = c("RPI", "RPIX", "CPI", "GDP Deflator"),
selected = "RPI",
multiple = T
)
)
)
fluidRow(
box(DT::dataTableOutput("index_table"), title = "Historic Inflation Indices", width = 12,
solidHeader = T, status = "primary")
)
)
server <- function(input, output, session) {
df_filt <- reactive({
if({
input$index_picker == "RPI" &
!is.null()
})
df_index %>%
select(Period, RPI.YOY, RPI.INDEX)
else if({
input$index_picker == "RPIX"
})
df_index %>%
select(Period, RPIX.YOY, RPIX.INDEX)
})
output$index_table <- renderDataTable({
DT::datatable(df_filt(),
options =
list(dom = "itB",
fixedHeader = T
),
rownames = F
)
})
}
I have similar code to the above that filters based on the row instead, and this works just fine, however, for this column filtering I am getting this error:
Warning in if ({ : the condition has length > 1 and only the first element will be used
I understand that I'm passing a vector to the if statement, but not sure how to recode - would anyone be able to help?
I am trying to generate a shiny app that will first allow the user to (using the notion of dplyr verbs) select the variables they are interested in and then filter those variables based on subsequent selections. I am trying to do this using conditionalPanel() but I am getting stuck finding a way to access the input$ from each conditional panel.
Here is an example:
library('shiny')
library('tidyverse')
library('shinyWidgets')
#Create the data
data <- select(mtcars, c(gear, carb))
#Create page with sidebarlayout
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
#Create picker input where relevant variables are selected
pickerInput(
inputId = 'vars',
label = 'Variables',
choices = colnames(data),
selected = colnames(data),
multiple = T,
pickerOptions(actionsBox = TRUE)
),
#Create conditional panels which show when the variable above is selected
#These panels will be used to filter the data that is selected based on the above variables
conditionalPanel(condition = "input.vars.includes('gear')",
pickerInput(inputId = 'gear',
label = 'Gear',
choices = unique(data$gear),
selected = unique(data$gear),
multiple = T,
pickerOptions(actionsBox = TRUE)
)
),
conditionalPanel(condition = "input.vars.includes('carb')",
pickerInput(inputId = 'carb',
label = 'Carb',
choices = unique(data$carb),
selected = unique(data$carb),
multiple = T,
pickerOptions(actionsBox = TRUE)
)
)
),
mainPanel(
#Show the selected data
verbatimTextOutput('term_selected'),
#Show the selected and filtered data - this won't show
verbatimTextOutput('term_selected_filtered'),
#Try debug with just getting the
verbatimTextOutput('debug_print')
)
)
)
server <- function(input, output) {
#Create the reactive selected data
selected_data <- reactive ({
data %>%
select(input$vars)
})
#Render the selected data
output$term_selected <- renderPrint(selected_data())
#This is where i am stuck
#I need to find a way to access the inputs related to the conditional functions
# selected_filtered_data <- reactive ({
# for (i in length(input$vars)) {
# selected_data() %>%
# filter(input$[first condiitonal panel select] %in% as.symbol(input$vars[i])
# }
# })
#
output$term_selected_filtered <- renderPrint(selected_filtered_data())
#Try to render input input$[first item of input.vars]
output$debug_print <- renderPrint(input$as.symbol(input$vars[1]))
}
shinyApp(ui = ui, server = server)
The problem lies in the server. I have tried input$as.symbol(input$vars[1]) to access the input$gear (assuming that was selected), but it just throws the error: attempt to apply non-function. I tried adding !! as syntactic sugar in front of as.symbol(), but that makes no difference.
I also tried this, in the hope that i could conditionally filter, and had no luck.
selected_filtered_data <- reactive({
selected_data() %>%
if('gear' %in% input$vars) {
filter(gear %in% input$gear) %>%
}
if('carb' %in% input$vars) {
filter(carb %in% input$carb)
}
})
How should I go about doing this?
We may use across (if we want to filter the rows when both column conditions are TRUE) or replace across with if_any (if either one of them is TRUE when they are both selected)
selected_data() %>%
filter(across(all_of(intersect(input$vars,
c('gear', "carb"))), ~ .x %in% input[[cur_column()]]))
-full code
library('shiny')
library('dplyr')
library(tidyr)
library('shinyWidgets')
#Create the data
data <- select(mtcars, c(gear, carb))
#Create page with sidebarlayout
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
#Create picker input where relevant variables are selected
pickerInput(
inputId = 'vars',
label = 'Variables',
choices = colnames(data),
selected = colnames(data),
multiple = TRUE,
pickerOptions(actionsBox = TRUE)
),
#Create conditional panels which show when the variable above is selected
#These panels will be used to filter the data that is selected based on the above variables
conditionalPanel(condition = "input.vars.includes('gear')",
pickerInput(inputId = 'gear',
label = 'Gear',
choices = unique(data$gear),
selected = unique(data$gear),
multiple = T,
pickerOptions(actionsBox = TRUE)
)
),
conditionalPanel(condition = "input.vars.includes('carb')",
pickerInput(inputId = 'carb',
label = 'Carb',
choices = unique(data$carb),
selected = unique(data$carb),
multiple = TRUE,
pickerOptions(actionsBox = TRUE)
)
)
),
mainPanel(
#Show the selected data
verbatimTextOutput('term_selected'),
#Show the selected and filtered data - this won't show
verbatimTextOutput('term_selected_filtered'),
#Try debug with just getting the
verbatimTextOutput('debug_print')
)
)
)
server <- function(input, output) {
#Create the reactive selected data
selected_data <- reactive ({
req(input$vars)
data %>%
select(input$vars)
})
#Render the selected data
output$term_selected <- renderPrint(selected_data())
#This is where i am stuck
#I need to find a way to access the inputs related to the conditional functions
selected_filtered_data <- reactive ({
selected_data() %>%
filter(across(all_of(intersect(input$vars, c('gear', "carb"))), ~ .x %in% input[[cur_column()]]))
})
#
output$term_selected_filtered <- renderPrint(
selected_filtered_data()
)
output$debug_print <- renderPrint(input[[input$vars[1]]])
}
shinyApp(ui = ui, server = server)
-output
I have two datasets, one with a list of two hundred cities and their corresponding state and another much larger dataset that I'd like to make an app to sort through. I need help making two drop down boxes in my shiny app where the first is the state variable and the second is the list of cities within that chosen state. I then want those selections to filter the much larger, second dataset in the output. I've tried solutions from several similar but slightly different examples online, but I'm having trouble translating it to what I'm doing.
So far I have this:
ui <- fluidPage(
headerPanel(''),
sidebarPanel(
#add selectinput boxs
htmlOutput("state_selector"),
htmlOutput("city_selector"),
),
mainPanel(
fluidRow(
# Create a new row for the table.
DT::dataTableOutput("table")
)
server <- function(session, input, output) {
output$state_selector = renderUI({
selectInput("state", label = h4("State"),
choices = as.character(unique(citystatedata$state)), selected = NULL)
})
output$city_selector = renderUI({
data_available = citystatedata[citystatedata$State == input$state, "state"]
selectInput(inputId = "city", #name of input
label = "City", #label displayed in ui
choices = unique(data_available), #calls list of available cities
selected = unique(data_available)[1])
})
shinyApp(ui = ui, server = server)
I tried to take out the portions of the code that weren't specifically related to the drop down boxes, since that's what I was more specifically asking about. So I'm sorry if I've left anything out! Let me know if I need to include anything else
Using available gapminder data, you can try this.
df <- gapminder
df$state <- gapminder$continent
df$city <- gapminder$country
citystatedata <- df
ui <- fluidPage(
headerPanel('Test'),
sidebarPanel(
#add selectinput boxs
uiOutput("state_selector"),
uiOutput("city_selector"),
),
mainPanel(
fluidRow(
# Create a new row for the table.
DTOutput("table")
)
)
)
server <- function(session, input, output) {
output$state_selector = renderUI({
selectInput("state", label = h4("State"),
choices = as.character(unique(citystatedata$state)), selected = NULL)
})
output$city_selector = renderUI({
data_available = citystatedata[citystatedata$state == req(input$state),]
selectInput(inputId = "city", #name of input
label = "City", #label displayed in ui
choices = unique(data_available$city), #calls list of available cities
selected = 1)
})
mydt <- reactive({
citystatedata %>% filter(citystatedata$state == req(input$state) & citystatedata$city %in% req(input$city))
})
output$table <- renderDT(mydt())
}
shinyApp(ui = ui, server = server)
I have a huge shiny app and met with the below issue. I tried to provide pseudo code for the problem since it is nearly impossible for my expertize to creating working app to demonstrate the problem. I hope i have conveyed with the pseudo code. Kindly help me.
Here is the pseudo code in ui.R file which has an actionButton and a radioButton with underlying selectizeInput and checkboxGroupInput input options and plotOutput to render a plot.
###ui.R#####
tabPanel("Plots",
fluidRow(column(4,wellPanel(
actionButton("action_plot","Generate Plots"),
h6(textOutput("numheat")),
radioButtons("plot_subset",label="Chose by sample or group?",
choices=c("Sample","Group"),selected="Sample"),
conditionalPanel("input.plot_subset=='Sample'",
selectizeInput("view_sample_plot",
label = h5("Select Samples"),
choices = NULL,
multiple = TRUE,
options = list(placeholder = 'select samples to plot')
)
),
conditionalPanel("input.plot_subset=='Group'",
checkboxGroupInput("view_group_plot",
label=h5("Select Groups to View"),
choices="",
selected="")
)
)
),
column(8,
tabsetPanel(
tabPanel(title="Plot",
#textOutput("which_genes"),
h4(textOutput("plot_title")),
plotOutput("plot_rna",height="800px")
)
)
)
)
)
Below is the pseudo server.R code that observes the user input values and updates updateSelectizeInput and updateCheckboxGroupInput with choice from the default loaded R dataset. The user selected choices are used in the subsequent function to generate plot.
###server.R#####
## observed the user input and updated the selectize input and checkBoxGroup input values#####
observe({
print("server-plot-update")
# browser()
data_analyzed = inputData()
tmpgroups = data_analyzed$group_names
tmpdatlong = data_analyzed$data_long
tmpsamples = unique(tmpdatlong$sampleid)
tmpynames = tmpdatlong$
updateSelectizeInput(session,'view_sample_plot',
choices=tmpsamples, selected=NULL)
updateCheckboxGroupInput(session,'view_group_plot',
choices=tmpgroups, selected=NULL)
})
#####code to render plot based on user selection value i.e. by group or samples######
##plot_render utilizes the R functions in Plot.R file to subset the data by user input and generate plot###
plotdatReactive <- reactive({
data_analyzed = inputData
tmp <- plot_data(data_analyzed = data_analyzed,
yname="log2",
orderby="significance",
view_group=input$view_group_plot,
view_sample=input$view_sample_plot)
tmp
})
output$plot_rna <- renderPlot({
if(input$action_plot==0) return()
isolate({
tmp = plotdatReactive()
plot_render( data_analyzed=tmp,
yname = input$heatmapvaluename,
view_group=input$view_group_plot,
view_sample=input$view_sample_plot
)
})
})
Pseudo Code for R functions in plot.R file
####plot.R#####
###function to subset data based on user input samples or groups###
plot_subdat <- function(data_analyzed,
yname="log2",
orderby="significance",
view_sample=NULL,
view_group=NULL) {
if(is.null(view_sample)) view_sample=unique(data_analyzed$sampleid) ## sample names in the dataset
if(is.null(view_group)) view_group=data_analyzed$group_names ## group names in the dataset
tmpdat = data_analyzed$data_long
##subset dataset by **sampleid** if the user selected **samples** in SelectizeInput
tmpdat = tmpdat%>%filter(sampleid%in%view_sample)
subdat = filter(data_analyzed$data_long,unique_id%in%thesegenes,sampleid%in%view_sample)
#subset dataset by **group** if the user selected **group** in checkBoxGroup input
tmpdat = tmpdat%>%filter(group%in%view_group)
subdat = filter(data_analyzed$data_long,unique_id%in%thesegenes,group%in%view_group)
}
###this function generates the plot on the subset of data from the above function#####
plot_data <- function(...) {
tmpdat = plot_subdat(...)
plotdat = tmpdat$data
plotdat
}
The tmpdat and subdat are the inputs to generate the plot in plot_render function. If the user selects and inputs values through selectizeInput then the subsetting of data should be done by samples. If the user selects and input through checkBoxGroupInput then the subsetting should be done by group as commented in the code. I am not unable to subset the data based on user selection i.e. sample/group reactively in plot_subdat function. How can i do this reactively so that the output plot is generated as per the user selection.
I think you might want a reactive expression to subset your data.
Here is a basic working example that includes your inputs, and will plot subsetted data based on input selections reactively.
Edit:
The filtering of data is now in an external .R file, with input variables to filter on passed through.
library(shiny)
source("plot.R", local = TRUE)
ui <- fluidPage(
mainPanel(
tabsetPanel(
tabPanel("Plots",
fluidRow(column(4,wellPanel(
#actionButton("action_plot","Generate Plots"),
h6(textOutput("numheat")),
radioButtons("plot_subset",label="Chose by sample or group?",
choices=c("Sample","Group"),selected="Sample"),
conditionalPanel("input.plot_subset=='Sample'",
selectizeInput("view_sample_plot",
label = h5("Select Samples"),
choices = NULL,
multiple = TRUE,
options = list(placeholder = 'select samples to plot')
)
),
conditionalPanel("input.plot_subset=='Group'",
checkboxGroupInput("view_group_plot",
label=h5("Select Groups to View"),
choices="",
selected="")
)
)),
column(8,
tabsetPanel(
tabPanel(title="Plot",
#textOutput("which_genes"),
h4(textOutput("plot_title")),
plotOutput("plot_rna",height="800px")
)
)
)
)
)
)
)
)
server <- function(input, output, session) {
observe({
updateSelectizeInput(session,'view_sample_plot',
choices=unique(mtcars$gear), selected=NULL)
updateCheckboxGroupInput(session,'view_group_plot',
choices=unique(mtcars$cyl), selected=NULL)
})
plot_prepare <- reactive({
if (input$plot_subset == "Sample") {
plot_subdat(mtcars, "gear", input$view_sample_plot)
} else {
plot_subdat(mtcars, "cyl", input$view_group_plot)
}
})
output$plot_rna <- renderPlot({
plot(plot_prepare())
})
}
shinyApp(ui, server)
plot.R
# plot.R file
library(tidyverse)
plot_subdat <- function(data, variable, choices) {
data %>%
filter((!!sym(variable)) %in% choices) %>%
select(c(!!sym(variable), mpg))
}
This one has me really going around in circles.
I am working on an R script that loads a dataframe and uses fields from the dataframe to populate a hierarchical set of selectizeInput. E.g. each of the inputs represent a subset of what is in the previous. Each SubRegion contains multiple LCC’s, Each LCC contains multiple ENB’s, and so on.
When the user select a value in any of the inputs, that value will used to filter the dataframe and all of the other selectizeInputs need to be updated from the filtered data.
It seems to work fine for the first input (SubRegionInput) but every time I try to get it to respond to and/or filter by any of the others (e.g. add input$LCCInput to the observe block) they get populated for a few seconds and then go blank.
I suspect the answer is quite simple and/or I am doing something really dumb, but I am a total hack with no formal R training so am probably missing something quite basic (if so sorry).
Below is a partial chunk of code (sorry I can’t include it all but this is for work and I can’t share the details of what I am doing).
NOTES
The current outputs are just so I can see what is going on while I develop this portion of the code.
I know right now it is only set up to filter on the one value…everything I have tried to do it on more has failed so I included the most functional code I have so far.
ui <- fluidPage(
# Application title
titlePanel("KPI DrillDown"),
# Sidebar with a slider input for number of bins
fluidRow(
selectizeInput("SubRegionInput", "SubRegion", SubRegionList ,selected = NULL, multiple = TRUE),
selectizeInput("LCCInput", "LCC", LCCList,selected = NULL, multiple = TRUE),
selectizeInput("ENBIDInput", "ENBID", ENBIDList,selected = NULL, multiple = TRUE),
selectizeInput("SiteNumInput", "SiteNumber", SiteNumberList,selected = NULL, multiple = TRUE),
selectizeInput("SiteNameInput", "SiteName", SiteNameList,selected = NULL, multiple = TRUE),
selectizeInput("LNCELInput", "LNCell", LNCellList,selected = NULL, multiple = TRUE),
selectizeInput("SectorInput", "Sector", SectorList,selected = NULL, multiple = TRUE),
mainPanel(
#plotOutput("distPlot")
verbatimTextOutput("SubRegionText"),
verbatimTextOutput("LCCText"),
verbatimTextOutput("view")
)
)
)
server <- function(input, output) {
observe({
input$SubRegionInput
temp <- SiteInfo[SiteInfo$SITE_SUB_REGION %in% input$SubRegionInput, ]
thisLCCList = sort(temp$BACKHAUL_LCC[!is.na(temp$BACKHAUL_LCC)])
updateSelectizeInput(session = getDefaultReactiveDomain()
, inputId = "LCCInput"
, choices = thisLCCList
, selected= NULL)
thisENBIDList = sort(temp$ENODEB_ID[!is.na(temp$ENODEB_ID)])
updateSelectizeInput(session = getDefaultReactiveDomain()
, inputId = "ENBIDInput"
, choices = thisENBIDList
, selected= NULL)
thisSiteNumberList = sort(temp$SITE_NUMBER[!is.na(temp$SITE_NUMBER)])
updateSelectizeInput(session = getDefaultReactiveDomain()
, inputId = "SiteNumInput"
, choices = thisSiteNumberList
, selected= NULL)
thisSiteNameList = sort(temp$SITE_NAME[!is.na(temp$SITE_NAME)])
updateSelectizeInput(session = getDefaultReactiveDomain()
, inputId = "SiteNameInput"
, choices = thisSiteNameList
, selected= NULL)
thisLNCellList = sort(temp$SECTOR_NUMBER[!is.na(temp$SECTOR_NUMBER)])
updateSelectizeInput(session = getDefaultReactiveDomain()
, inputId = "LNCELInput"
, choices = thisLNCellList
, selected= NULL)
thisSectorList = sort(temp$Sector[!is.na(temp$Sector)])
updateSelectizeInput(session = getDefaultReactiveDomain()
, inputId = "SectorInput"
, choices = thisSectorList
, selected= NULL)
output$view<- renderPrint(temp)
})
Since I do not have access to your data, I used mtcars as an example.
To begin with, since you have so many filtering, I would suggest creating a search or update button, which is what I did in my codes. I only did one filtering using dplyr after extracting all the selectizeInputs. I have to manually change all the empty searching parameter to select all in order to avoid filtering to NA.
Overall, I think the problem with your code was you are observing too many updateSelectizeInputs at once. I did try to recreate using your way, and what I ended with was that I could only update single selectizeInput, and the other selectizeInputs were not selectable.
Hopefully, this method fits your data.
Codes:
library(shiny)
library(dplyr)
library(DT)
data <- mtcars
SubRegionList <- unique(data$cyl)
LCCList <- unique(data$gear)
ENBIDList <- unique(data$am)
SiteNumberList <- unique(data$vs)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("KPI DrillDown"),
# Sidebar with a slider input for number of bins
fluidRow(
selectizeInput("SubRegionInput", "SubRegion/cyl", SubRegionList ,selected = NULL, multiple = TRUE),
uiOutput("LCCInput"),
uiOutput("ENBIDInput"),
uiOutput("SiteNumInput"),
uiOutput("Search"),
mainPanel(
verbatimTextOutput("view")
)
)
)
# Define server logic required
server <- function(input, output, session) {
SiteInfo <- data
# temp <- ""
observe({
if (!is.null(input$SubRegionInput)){
subRegionSelected <- input$SubRegionInput
## Create a temp dataset with the selected sub regions.
temp <- SiteInfo[SiteInfo$cyl %in% subRegionSelected, ]
## Push the newly created selectizeInput to UI
output$LCCInput <- renderUI({
selectizeInput("LCCInput", "LCC/gear", unique(temp$gear), selected = NULL, multiple = TRUE)
})
output$ENBIDInput <- renderUI({
selectizeInput("ENBIDInput", "ENBID/am", unique(temp$am),selected = NULL, multiple = TRUE)
})
output$SiteNumInput <- renderUI({
selectizeInput("SiteNumInput", "SiteNumber/vs", unique(temp$vs), selected = NULL, multiple = TRUE)
})
output$Search <- renderUI({
actionButton("Search", "Search")
})
## Function that linked to the actionButton
display <- eventReactive(input$Search,{
temp <- SiteInfo[SiteInfo$cyl %in% input$SubRegionInput, ]
# ## manually change all the empty searching parameter to select all in order to avoid filtering to NA
LCC <- input$LCCInput
if (is.null(input$LCCInput)){LCC <- unique(temp$gear)}
ENBID <- input$ENBIDInput
if (is.null(input$ENBIDInput)){EBVID <- unique(temp$am)}
SiteNum <- input$SiteNumInput
if (is.null(input$SiteNumInput)){LCC <- unique(temp$vs)}
## Dplyr::filter data
temp <- temp %>%
filter(gear %in% LCC & am %in% ENBID & vs %in% SiteNum)
temp
})
## Run the actionButton
output$view <- renderPrint({
display()
})
} else {
## Display waht the data looks like when no Sub Region is selected
output$view<- renderPrint(data)
}
})
}
# Run the application
shinyApp(ui = ui, server = server)