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)
})
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 am trying to get the suffix of an output$suffix name in R Shiny and incorporate it into the input$suffix_rows_selected function. The drilldown table is coming empty. Would someone have any idea of what am I doing wrong?
Function that I am trying to build:
f.drilldata <- function(base.summary, base.drilldown, suffix.output, group_var){
group = enquo(group_var)
base.summary = base.summary %>% mutate(var = !!group)
base.drilldown = base.drilldown %>% mutate(var = !!group)
#input = expr(!!glue("input${suffix.output}_rows_selected"))
input = paste0(suffix.output,'_rows_selected')
validate(need(length(input[[input]]) > 0, ''))
selected_rows <- base.summary[as.integer(input[[input]]), ]$var
base.drilldown[base.drilldown$var %in% selected_rows, ]
}
Error Example:
library("dplyr")
library("shiny")
library("DT")
tbl.summary <- group_by(iris, Species) %>% summarise(Count = n())
tbl.drilldown <- iris
ui <- fluidPage(
DTOutput("output.summary.name")
, DTOutput("output.drilldown.name"))
server <- function(input, output){
# display the data that is available to be drilled down
output$output.summary.name <- renderDT(tbl.summary)
# subset the records to the row that was clicked through f.drilldata function
drilldata <- reactive({ f.drilldata(tbl.summary, tbl.drilldown, 'output.summary.name', Species) })
# display the subsetted data
output$output.drilldown.name <- renderDT(drilldata())}
shinyApp(ui, server)
Example that works but out of the f.drilldata function
library("dplyr")
library("shiny")
library("DT")
tbl.summary <- group_by(iris, Species) %>% summarise(Count = n())
tbl.drilldown <- iris
ui <- fluidPage(
DTOutput("output.summary.name")
, DTOutput("output.drilldown.name"))
server <- function(input, output){
output$output.summary.name <- renderDT(tbl.summary)
drilldata <- reactive({ validate( need(length(input$output.summary.name_rows_selected) > 0, "Select rows to drill down!"))
selected_species <-
tbl.summary[as.integer(input$output.summary.name_rows_selected), ]$Species
tbl.drilldown[tbl.drilldown$Species %in% selected_species, ] })
output$output.drilldown.name <- renderDT(drilldata())}
shinyApp(ui, server)
I have found a simple solution by just adding the entire input (input$output.summary.name_rows_selected) as an argument of the function as below.
library("dplyr")
library("shiny")
library("DT")
f.drilldata <- function(base.summary, base.drilldown, input, group_var){
group = enquo(group_var)
base.summary = base.summary %>% mutate(var = !!group)
base.drilldown = base.drilldown %>% mutate(var = !!group)
validate(need(length(input) > 0, ''))
selected_rows <- base.summary[as.integer(input), ]$var
base.drilldown[base.drilldown$var %in% selected_rows, ]
}
tbl.summary <- group_by(iris, Species) %>% summarise(Count = n())
tbl.drilldown <- iris
ui <- fluidPage(
DTOutput("output.summary.name")
, DTOutput("output.drilldown.name"))
server <- function(input, output){
output$output.summary.name <- renderDT(tbl.summary)
drilldata <- reactive({ f.drilldata(tbl.summary, tbl.drilldown,
input$output.summary.name_rows_selected, Species) })
output$output.drilldown.name <- renderDT(drilldata())}
shinyApp(ui, server)
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.
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 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.