I want to use numeric input from a slider to change available size of options in my app. I have all my code running, except the selection for the top_n subgroups is not working. I am getting the Error:
Error in : n must be a scalar integer
I tried to convert input$selected_precision to various datatype but to no success. I also tried dplyr::slice, but that also didn't work because it returned unexpected results.
So my questions is: How can turn my input$selected_precision into a scalar integer?
Here is my code (the code which raises the error is in line 58):
library(shiny)
library(plotly)
library(dplyr)
library(shinyWidgets)
groupA <- sort(rep(c('AA'),16))
groupB <- sort(rep(c('BB'),32))
group <- c(groupA, groupB)
subgroupA <- rep(c('AAA','BBB'),8)
subgroupB <- rep(c('EEE','FFF','GGG','HHH'),8)
subgroup <- c(subgroupA, subgroupB)
one <- rep(1990,4)
two <- rep(1991,4)
three <- rep(1992,4)
four <- rep(1993,4)
year <- as.character(rep(c(one,two,three,four),3))
relValue <- rnorm(48, 30, 10)
df <- data.frame(group, subgroup, year, relValue, stringsAsFactors = FALSE)
ui <- fluidPage(
sidebarPanel(
selectInput(inputId = 'selected_group', label = 'group', choices = ''),
uiOutput("selected_precision"),
pickerInput(inputId = 'selected_subgroup', label = 'subgroup', choices = '', multiple = TRUE),
verbatimTextOutput('text', placeholder = TRUE))
)
server <- function(input, output, session){
observe({
updateSelectInput(session,
'selected_group',
choices = unique(df$group))})
maxNum <- reactive({
df %>%
filter(group == input$selected_group) %>%
distinct(subgroup) %>%
summarise(n = n()) %>%
.$n})
output$selected_precision <- renderUI({
sliderInput('selected_precision', label = 'precision', min = 1, max = maxNum(),
value = maxNum(), round = TRUE, step = 1)})
filteredChoices <- eventReactive({
maxNum()
input$selected_group}, {
df %>%
filter(group == input$selected_group) %>%
group_by(subgroup) %>%
summarise(avg = mean(relValue)) %>%
arrange(desc(avg)) %>%
top_n(input$selected_precision, avg) %>% ### If you change the input$ to any integer the code will run
.[[1]]})
observeEvent({
filteredChoices
input$selected_group}, {
updatePickerInput(
session,
'selected_subgroup',
choices = filteredChoices(),
selected = filteredChoices()
)})
output$text <- renderText({input$selected_precision})
}
shinyApp(ui,server)
The first time the filderedChoices block runs, input$selected_precision will be NULL because the input hasn't yet updated after the corresponding UI component was rendered.
You need to handle the NULL, e.g.:
n <- input$selected_precision
if (is.null(n)) {
n <- maxNum()
}
And then use top_n(n, avg) later.
Related
I keep getting errors like Error in : object of type 'closure' is not subsettable or
'..1'. x Input '..1' must be of size 28 or 1, not size 0. I am trying to change the bar graph based on what options are selected or not in the checkbox.
I changed the column names for ease of use from where I got the data.
library(shiny)
library(dplyr)
library(plotly)
#dataset link: https://www.kaggle.com/mahirahmzh/starbucks-customer-retention-malaysia-survey?select=Starbucks+satisfactory+survey.csv
#c("Timestamp","Gender","age","currently","income","visit_freq","Enjoy","Time","Nearby","membership","freq_purchase","avg_spend","Ratevsother","rateprice","salesandpromotion","ambiance","wifi","service","meetup","heardaboutpromotions","continuepatronage")
data <-read.csv("Starbucks satisfactory survey.csv", header=TRUE)
Categorical.Variables <- c("visit_freq", "age", "income")
ui <- fluidPage(
sidebarPanel(
selectInput('category', choices = Categorical.Variables, label = 'Select filter options:'),
conditionalPanel(condition = "input.category != '-'",
uiOutput("select_category"))
)
)
server <- function(input, output) {
output$select_category <- renderUI({
choices <- as.list(unique(data[[input$category]]))
checkboxGroupInput('categorycheck', label = 'Select filter:',
choices = choices,selected = choices)
data2 <- reactive({
data %>%
group_by(gender,data[[input$category]], currently,membership) %>%
summarize(n = n(), .groups="drop") %>%
filter(data[[input$category]] %in% input$categorycheck) %>%
filter(membership == "Yes")})
renderPlotly({
data2 <- data2()
colnames(data2) <- c("gender","filtercategory","currently","membership","n")
plot_ly(data2, x = ~currently, y = ~n, type = "bar", color=~gender, colors="Dark2") %>%
layout(barmode = 'group')
})
})
}
shinyApp(ui, server)
You have several issues. You should close your renderUI prior to using input$categorycheck in the reactive object data2. In addition, columns names in the csv file are long. Once you define the column names of data the way you are analyzing, it will work. Try this
mydata <-read.csv("Starbucks satisfactory survey.csv", header=TRUE)
names(mydata)[1:10] <- c("Timestamp", "gender", "Age", "currently", "Income", "visit_freq","drink_freq","time_spent", "nearby","membership")
Categorical.Variables <- c("Age", "Income", "visit_freq")
ui <- fluidPage(
sidebarPanel(
selectInput('category', choices = Categorical.Variables, label = 'Select filter options:'),
#conditionalPanel(condition = "input.category != '-'",
uiOutput("select_category")
# )
),
mainPanel(plotlyOutput("myplot"),
DTOutput("t1")
)
)
server <- function(input, output) {
output$select_category <- renderUI({
req(input$category)
choices <- as.list(unique(mydata[[input$category]]))
checkboxGroupInput('categorycheck', label = 'Select filter:',
choices = choices,selected = choices)
})
data2 <- reactive({
req(input$category,input$categorycheck)
mydata %>%
group_by(gender,.data[[input$category]], currently,membership) %>%
dplyr::summarize(n = n(), .groups="drop") %>%
filter(.data[[input$category]] %in% input$categorycheck) %>%
filter(membership == "Yes")})
output$t1 <- renderDT(data2())
output$myplot <- renderPlotly({
req(data2())
data2 <- data2()
colnames(data2) <- c("gender","filtercategory","currently","membership","n")
plot_ly(data2, x = ~currently, y = ~n, type = "bar", color=~gender, colors="Dark2") %>%
layout(barmode = 'group')
})
}
shinyApp(ui, server)
I would like to get a reactive plot with multiple filters and an initial minimal plot with the number of ALL diagnostics by month.
The aim requires 4 filters AND a scale of period (display data by week,month ..).
I tried but not it's not working on the step to sum data (ALL).
If you have a suggestion I am open .
Thank you :)
library(shiny)
library(ggplot2)
library(dplyr)
#data
data.hosp <- data.frame(stringsAsFactors=FALSE,
id= c(1,2,3,4,5),
Annee = c(2018, 2018, 2018, 2018, 2018),
Mois = c("2018-01","2018-01","2018-02","2018-03","2018-03"),
Semaine = c("2018-001","2018-003","2018-008","2018-011","2018-013"),
uf= c("A3352","Z6687", "A3352", "A3352", "Z6687"),
um= c(3350,6687, 3352, 3350, 6687),
ghm= c("AAAAA","DFFDF","DDFDA","AZEEA","DDFDA"),
diag= c("A","A","C","Z","R"),
nb=c(1,3,1,1,10))
ui <- fluidPage(
titlePanel("Plot with filters"),
fluidRow(
column(2,
selectInput(inputId = "sel_uf",
label = "UF",
choices = as.character(unique(data.hosp$uf)),
multiple=TRUE,
width = validateCssUnit(200))),
column(2,
selectInput(inputId = "sel_um",
label = "UM",
choices = as.character(unique(data.hosp$um)),
multiple=TRUE,
width = validateCssUnit(200))),
column(3,
selectInput(inputId = "sel_ghm",
label = "GHM",
choices =as.character(unique(data.hosp$ghm)),
multiple=TRUE,
width = validateCssUnit(200))),
column(3,
selectInput(inputId = "sel_diag",
label = "Diagnostic",
choices =c('Tous', as.character(unique(data.hosp$diag))),
multiple=TRUE,
width = validateCssUnit(250))),
column(4, selectInput("periodec",
label="Affichage par :",
choices = c("Semaine"= "Semaine",
"Mois" = "Mois",
"Année" = "Annee"),
selected = "Mois" ) ),
plotOutput("graph1", height=300 )
))
server <- function(input, output) {
#Period filter (display by :)
periode <- reactive({
ifelse(input$periodec=="Semaine", "Semaine",
ifelse(input$periodec=="Mois", "Mois",
ifelse(input$periodec=="Année", "Annee"))) })
# Data filters
df_dat <- reactive({
df_dat <- data.hosp
#code to get "ALL" in selectsizeInput
if ('Tous' %in% input$sel_diag) { sel_diag <- unique(data.hosp$diag)
} else {
data.hosp <- data.hosp %>% filter(nb == input$sel_diag)
nb <- unique(data.hosp$nb) }
if (!is.null(input$sel_um)) {
df_dat <- df_dat %>% filter(um == input$sel_um) %>% group_by_(periode()) %>% summarise(sum_active = sum(nb))}
if (!is.null(input$sel_uf)) {
df_dat <- df_dat %>% filter(uf == input$sel_uf) %>% group_by_(periode()) %>% summarise(sum_active = sum(nb))}
if (!is.null(input$sel_ghm)) {
df_dat <- df_dat %>% filter(ghm == input$sel_ghm) %>% group_by_(periode()) %>% summarise(sum_active = sum(nb)) }
if (!is.null(input$sel_diag)) {
df_dat <- df_dat %>% filter(diag == input$sel_diag) %>% group_by_(periode()) %>% summarise(sum_active = sum(nb)) }
return(df_dat)
})
# Ensures that our filter works properly
observe(print(str(df_dat())))
# graph
output$graph1 <- renderPlot({
req(df_dat())
ggplot(df_dat(), aes_string(x =periode(), y = "sum_active", group = factor(periode() ))) +
geom_bar(aes_string(periode(), "sum_active"), stat = "identity", fill="steelblue")
})
}
shinyApp(ui,server)
Maybe this is what you are looking for. In my opinion the main issue is that your approach was overly complicated. (;
My approach sets all filters first. If NULL (and/or 'Tous') the "selection" is set to all categories of a variable, otherwise only the chosen ones are included.
Doing so you only need one pipe to filter and summarise the data
There is no need for the observe statement which I dropped
I fixed the req in the renderPlot to check wether the filtered data contains any rows. Otherwise ggplot will raise an error.
server <- function(input, output) {
#Period filter (display by :)
# While a nested ifelse works in the present case a better choice is using `if
periode <- reactive({
if (input$periodec == "Semaine") {
"Semaine"
} else if (input$periodec == "Mois") {
"Mois"
} else {
"Annee"
}
})
df_dat <- reactive({
# First: Setup the filters. If NULL: all categories else: chosen ctaegories
# In case of diag additionally check for "Tous"
sel_diag <- if (is.null(input$sel_diag) | 'Tous' %in% input$sel_diag) unique(data.hosp$diag) else input$sel_diag
sel_um <- if (is.null(input$sel_um)) unique(data.hosp$um) else input$sel_um
sel_uf <- if (is.null(input$sel_uf)) unique(data.hosp$uf) else input$sel_uf
sel_ghm <- if (is.null(input$sel_ghm)) unique(data.hosp$ghm) else input$sel_ghm
# Filter the data and summarise
data.hosp %>%
filter(diag %in% sel_diag, um %in% sel_um, uf %in% sel_uf, ghm %in% sel_ghm) %>%
group_by_(periode()) %>%
summarise(sum_active = sum(nb), .groups = "drop")
})
# graph
output$graph1 <- renderPlot({
# Plot only if any data
req(nrow(df_dat()) > 0)
ggplot(df_dat(), aes_string(x = periode(), y = "sum_active", group = factor(periode() ))) +
geom_bar(aes_string(periode(), "sum_active"), stat = "identity", fill="steelblue")
})
}
I try to create a simple shiny app. What we have here is app with two tabPanel modules, each refers to different dataset. Actually both datasets have the same structure (i.e. name of column, name of factors within columns), only difference is column value and number of instances in those columns. I would like to create the same layout of each tabPanel. I try to depend widget in Module 1 on widget in Module 2. For example, if I choose product P2 in Module 1 and then change tabPanel into Module 2, widget automatically change value into P2. The main goal is to create mechanism which allow me to change the value of both widgets in both ways. For example, after I go to the Module 2 with value P2 and then I change it into P3 and come back to Module 1 I want to see P3 as well.
ui.R
library(ggvis)
library(shiny)
shinyUI(
navbarPage(title = '',
tabPanel("Module 1",
fluidRow(
selectInput('prod1','', prod),
ggvisOutput('ggvis_plot1')
)
),
tabPanel("Module 2",
fluidRow(
uiOutput('in_prod2'),
ggvisOutput('ggvis_plot2')
))
)
)
server.R
library(shiny)
library(ggvis)
library(dplyr)
shinyServer(function(input, output) {
# renderUI part
output$in_prod2 <- renderUI({
selectInput('prod2','',
choices = prod, selected = input$prod1)
})
# Code for data module1
data_mod1_0 <- reactive({
df <- module1_df
df <- df %>%
filter(prod == input$prod1)
})
ggvis_plot1 <- reactive({
plot <- data_mod1_0() %>%
ggvis(~id, ~value) %>%
layer_points(fill = ~part)
})
ggvis_plot1 %>% bind_shiny('ggvis_plot1')
# Code for data module2
data_mod2_0 <- reactive({
if (is.null(input$prod2))
df <- module2_df
else {
df <- module2_df
df <- df %>%
filter(prod == input$prod2)
}
})
ggvis_plot2 <- reactive({
plot1 <- data_mod2_0() %>%
ggvis(~id, ~value) %>%
layer_points(fill = ~part)
})
ggvis_plot2 %>% bind_shiny('ggvis_plot2')
})
global.R
library(dplyr)
prod <- c('P1','P2','P3')
level <- c('L1','L2','L3')
part <- c('p1','p2','p3','p4','p5')
axis_x <- list(L1 = list('Ordering' = 'id'),
L2 = list('Ordering' = 'id', 'Part name' = 'part'),
L3 = list('Ordering' = 'id', 'Part name' = 'part'))
# Data for module 1
set.seed(123)
module1_df <- data.frame(prod = sample(prod,300, replace = T),
level = sample(level, 300, replace = T),
part = sample(part, 300, replace = T),
value = rnorm(300))
module1_df <- module1_df %>%
group_by(prod) %>%
mutate(id = 1:n()) %>%
arrange(prod, id)
# Data for module 2
set.seed(321)
module2_df <- data.frame(prod = sample(prod,300, replace = T),
level = sample(level, 300, replace = T),
part = sample(part, 300, replace = T),
value = rnorm(300))
module2_df <- module2_df %>%
group_by(prod) %>%
mutate(id = 1:n()) %>%
arrange(prod, id)
Here is a very simple example of this. Basically you use observeEvent to determine when a selectInput has changed, and then use updateSelectnput to update the other select.
library(shiny)
ui <-navbarPage(title = '',
tabPanel("Module 1",
fluidRow(
selectInput('sel1','Select 1', choices=c("A","B","C")),
textOutput('select1')
)
),
tabPanel("Module 2",
fluidRow(
selectInput('sel2','Select 2', choices=c("A","B","C")),
textOutput('select2')
))
)
server <- function(input, output, session) {
output$select1<-renderText(input$sel1)
output$select2<-renderText(input$sel2)
observeEvent(input$sel1, updateSelectInput(session,input='sel2',selected=input$sel1))
observeEvent(input$sel2, updateSelectInput(session,input='sel1',selected=input$sel2))
}
shinyApp(ui = ui, server = server)
I try to implement one feature in my simple shiny app. My target is to create reactive widget which will be dependent on different widget. In my example I have two important widgets: radioButtons and selectInput. Depending on what I choose in radioButtons, the output of widget selectInput will be change.
Unfortunately I get an error: Error in as.name(input$xvar) :invalid type/length (symbol/0). Thanks for any help.
ui.R
library(shiny)
shinyUI(
fluidPage(
fluidRow(
column(3,
selectInput('prod','', prod),
radioButtons('level','',level, level[1]),
uiOutput('in_xvar')
),
column(9,
ggvisOutput('ggvis_plot')
)
)
))
server.R
library(shiny)
library(ggvis)
library(dplyr)
shinyServer(function(input, output) {
data0 <- reactive({
df <- test_data
df <- df %>%
filter(prod == input$prod)
})
data <- reactive({
df <- data0()
df <- df %>%
filter(level == input$level)
})
output$in_xvar <- renderUI({
choosen_list <- axis_x[input$level]
selectInput('xvar','', choosen_list)
})
ggvis_plot <- reactive({
x <- prop('x', as.name(input$xvar))
plot <- data() %>%
ggvis(x, ~value) %>%
layer_points(fill = ~part)
})
ggvis_plot %>% bind_shiny('ggvis_plot')
})
global.R
prod <- c('P1','P2','P3')
level <- c('L1','L2','L3')
part <- c('p1','p2','p3','p4','p5')
axis_x <- list(L1 = list('Ordering' = 'id'),
L2 = list('Ordering' = 'id', 'Part name' = 'part'),
L3 = list('Ordering' = 'id', 'Part name' = 'part'))
set.seed(123)
test_data <- data.frame(prod = sample(prod,300, replace = T),
level = sample(level, 300, replace = T),
part = sample(part, 300, replace = T),
value = rnorm(300))
test_data <- test_data %>%
group_by(prod) %>%
mutate(id = 1:n()) %>%
arrange(prod, id)
You get the error because input$xvar is initially NULL, try adding an if/else in your ggvis_plot:
ggvis_plot <- reactive({
if(is.null(input$xvar))
x <- prop('x', as.name("id"))
else
x <- prop('x', as.name(input$xvar))
plot <- data() %>%
ggvis(x, ~value) %>%
layer_points(fill = ~part)
})
I have one problem with my Shiny app. Firstly, I have two dataframes in which there are two numeric columns ( number and number2). I also have dynamic ui sliderInput. Shiny app works fine till... when I choose Item dataframe, choose number in Y-axis variable and set range in sliderInput between e.g. 15 and 18, and after that I want to change Y-axis variable to number2 I get an error: Error in eval(substitute(expr), envir, enclos) : wrong result size (2), expected 0 or 1
I know that the problem is caused because number in number2 column is between 1 and 10 and previous settings does not included that numbers. Could anyone tell me how to improve it?
ui.R
library(ggvis)
shinyUI(fluidPage(
titlePanel(""),
sidebarLayout(
sidebarPanel(
radioButtons("dataset", label = h4("Choose dataframe"),
choices = list("Item" = "df1", "Task" = "df2")),
selectInput("yvar", "Y-axis variable", axis_vars_y, selected = "number"),
uiOutput("slider")
),
mainPanel(
ggvisOutput("plot")
)
)
))
server.R
library(shiny)
library(dplyr)
library(magrittr)
library(lazyeval)
df1_number <-sample(seq(1,20,0.01),20,replace = T)
df2_number <-sample(seq(1,20,0.01),20,replace = T)
df1_number2 <-sample(seq(1,10,0.01),20,replace = T)
df2_number2 <-sample(seq(1,10,0.01),20,replace = T)
df1 <- data.frame(name = rep(letters[1:4],each = 5), number = df1_number, number2 = df1_number2)
df2 <- data.frame(name = rep(letters[1:4],each = 5), number = df2_number, number2 = df2_number2)
axis_vars_y <- c("number" = "number", "number2" = "number2")
shinyServer(function(input, output) {
datasetInput <- reactive({
switch(input$dataset,
df1 = df1,
df2 = df2)
})
axis_vara_y <- reactive({
switch(input$yvar,
number = 2,
number2 = 3)
})
output$slider <- renderUI({
sliderInput("inslider","Slider", min = min(datasetInput()[,axis_vara_y()]),
max = max(datasetInput()[,axis_vara_y()]),
value = c(min(datasetInput()[,axis_vara_y()]),
max(datasetInput()[,axis_vara_y()])),
step = 0.5)
})
data <- reactive({
filteredData <- datasetInput()
if(!is.null(input$inslider)){
filteredData <- filteredData %>%
filter(filteredData[,axis_vara_y()] >= input$inslider[1],
filteredData[,axis_vara_y()] <= input$inslider[2])
}
filteredData
})
data_two <- reactive({
data() %>%
mutate(id = 1:n())
})
vis <- reactive({
yvar_name <- names(axis_vars_y)[axis_vars_y == input$yvar]
yvar <- prop("y", as.symbol(input$yvar))
data_two %>%
ggvis(x = ~name, y = yvar) %>%
layer_points(size := 120,
fill = ~name,
fillOpacity := 0.6,
key := ~id)
})
vis %>% bind_shiny("plot")
})
Update---------------------------------------------------------------------------------------------------
The same error occurs when I set a range which does not included any value from number column (e.g. between 13 and 14).
As you realize, you are getting the error because the filtered dataset has no rows. A simple workaround would be to return the full dataset. This will reset the inputSlider for you to continue working without error. You only need to change your reactive data function.
data <- reactive({
filteredData <- datasetInput()
axisData <- axis_vara_y()
if(!is.null(input$inslider)){
filteredData <- filteredData %>%
filter(filteredData[,axisData] >= input$inslider[1],
filteredData[,axisData] <= input$inslider[2])
}
# the new part to reset the slider
if(nrow(filteredData) == 0){
return(datasetInput())
}else{
return(filteredData)
}
})
I have taken the liberty of simplifying the code a little bit as you can assign your reactive data statements and not have the shiny app call them several times.