reactive change min/max range + 2 dataframes Shiny - r

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.

Related

Trying to get a checkboxGroupInput derived from column values to filter a bar graph, but keep getting various errors?

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)

How to use inputSlider with top_n in Shiny

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.

navbarPage shiny with two datasets and identical set of widgets - both ways dependence

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)

reactive updates in shiny app selectInput and radioButtons - ggvis

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)
})

ggvis plot disappears at random Shiny

I have a strange problem in Shiny. My shiny app has one ggvis plot with layer_points() and several options to manipulate the plot . When I run my app sometimes everything works good even if I change all options, but sometimes ( I suppose there is no specific rule) plot disappers. Plot comes back when I change one of options but it is not cool.
I study this issue but I do not really know whether it is a solution for my problem.
When the plot disappears my Shiny app looks like:
This my code:
ui.R
library(ggvis)
library(markdown)
library(shiny)
library(dplyr)
library(magrittr)
shinyUI(
fluidPage(
h3("Title"),
fluidRow(
column(3,
wellPanel(
radioButtons("radio",h5("Select"),choices=list("All values","Selected values"),
selected="All values"),
conditionalPanel(
condition = "input.radio != 'All values'",
checkboxGroupInput("checkGroup",label = "",
choices,
selected = c("AT1","AT2"))
),
hr(),
radioButtons("dataset", label = h5("Drilldown"),
choices = list("2 Level" = "df1", "3 Level" = "df2")
),
hr(),
h5("Choice"),
selectInput("xvar", h6(""),
axis_vars_x,
selected = "value"),
selectInput("yvar", h6(""),
axis_vars_y,
selected = "number2"),
hr(),
uiOutput("slider")
)
),
column(9,
ggvisOutput("plot")
)
)
)
)
server.R
library(shiny)
shinyServer(function(input, output,session) {
datasetInput <- reactive({
switch(input$dataset,
df2 = df2,
df1 = df1)
})
axis_vara_y <- reactive({
switch(input$yvar,
number = 2,
number2 = 3)
})
output$slider <- renderUI({
sliderInput("inslider",h5(""), min = round(min(datasetInput()[,axis_vara_y()]),0)-1,
max = round(max(datasetInput()[,axis_vara_y()]),0)+1,
value = c(round(min(datasetInput()[,axis_vara_y()]),0)-1,
round(max(datasetInput()[,axis_vara_y()]),0)+1),
step = 0.5)
})
data <- reactive({
filteredData <- datasetInput()
axisData <- axis_vara_y()
if(!is.null(input$inslider)){
if(input$radio == "All values"){
filteredData <- filteredData %>%
filter(filteredData[,axisData] >= input$inslider[1],
filteredData[,axisData] <= input$inslider[2])
}
else {
filteredData <- filteredData %>%
filter(value %in% input$checkGroup,
filteredData[,axisData] >= input$inslider[1],
filteredData[,axisData] <= input$inslider[2])
}
}
return(filteredData)
})
data_point <- reactive({
data() %>%
mutate(id = row_number())
})
xvar <- reactive(as.symbol(input$xvar))
yvar <- reactive(as.symbol(input$yvar))
dotpoint_vis <- reactive({
xvar_name <- names(axis_vars_x)[axis_vars_x == input$xvar]
yvar_name <- names(axis_vars_y)[axis_vars_y == input$yvar]
data_point_detail <- data_point()
plot <- data_point_detail %>%
ggvis(x = xvar(),y = yvar()) %>%
layer_points(size := 120,fill = ~value) %>%
add_axis("x", title = xvar_name) %>%
add_axis("y", title = yvar_name) %>%
set_options(width = 750, height = 500, renderer = "canvas")
})
dotpoint_vis %>% bind_shiny("plot")
})
global.R
choices <- list("Value1" = "AT1", "Value2" = "AT2",
"Value3" = "AT3", "Value4" = "AT4",
"Value5" = "AT5", "Value6" = "RT1",
"Value7" = "AT6", "Value8" = "AT7",
"Value9" = "AT8", "Value10" = "AT9",
"Value11" = "AT10", "Value12" = "RT2")
levele <- c("AT1","AT2","AT3","AT4","AT5","RT1","AT6","AT7","AT8","AT9","AT10","RT2")
df1 <- data.frame(value = levele,number = seq(2,46,4), number2 = seq(2,24,2),order = 1:12)
df2 <- data.frame(value = levele,number = rep(4:15), number2 = rep(4:9,each = 2),order = 1:12)
df1$value <- factor(df1$value, levels = levele)
df2$value <- factor(df2$value, levels = levele)
axis_vars_y <- c("number","number2")
axis_vars_x <- c("value", "order","number","number2")
update
I also do not know what happened with animation in ggvis.
The problem was difficult to reproduce at first, but I found I can reproduce it by clicking back and forth between All Values and Selected Values. The graph disappears or reappears after some number of switches between the two radio buttons, but it varies seemingly randomly -- sometimes it takes 4 clicks to make the graph disappear or reappear and other times it takes 2 clicks or some other number of clicks.
There must be a bug in bind_shiny() or ggvisOutput(), because the following changes do create a graphic that does not seem to disappear:
In ui.R, make this change:
# ggvisOutput("plot")
plotOutput('plot')
In server.R, make this change:
plot(data_point_detail[ , c(input$xvar, input$yvar)], xlab=xvar_name, ylab=yvar_name)
# plot <- data_point_detail %>%
# ggvis(x = xvar(),y = yvar()) %>%
# layer_points(size := 120,fill = ~value) %>%
# add_axis("x", title = xvar_name) %>%
# add_axis("y", title = yvar_name) %>%
# set_options(width = 750, height = 500, renderer = "canvas")
# plot
and
output$plot <- renderPlot(dotpoint_vis())
# dotpoint_vis %>% bind_shiny("plot")

Resources