I'm building my first app in Shiny and I've been meaning to get a better idea about reactivity. I have gone through the tutorials on http://shiny.rstudio.com/tutorial/. I'm working on a tennis-related dataset and wanting to create a radarchart using the package 'radarchart'. I'm able to render the radio buttons and select input box successfully using reactive expressions.
However, upon clicking the 'Go!' button, the console shows the following error: "Error in filter_impl: incorrect length (0), expecting: 27". No error shows up in the app itself though, there's just no rendering on clicking the 'Go!' button.
Upon debugging, I see that this error occurs when I'm trying to filter data using the user's selection of input values(lines 60-63 in server.R). My main concern is filtering the data as per the user's selection, I'm unable to do that in any way. I've tried to use eventReactive(), observe() as well as reactiveValues() functions too without any success. I've wrapped the renderChartJSRadar function within the eventReactive function, but I'm not quite sure if that's the right way to go about it.
I'm confused as to how the reactivity in this case should work and what I'm missing to make it work. The code is as shown below. I'd really appreciate any kind of help.
ui.R
library(xlsx)
library(shiny)
library(dplyr)
source("chart.R")
library(radarchart)
shinyUI(fluidPage(
titlePanel("Match Radar Chart"),
sidebarLayout(
sidebarPanel(
selectInput("var",
label = "Choose a tournament",
choices = tour,
selected = "Auckland"),
uiOutput("radioButtons"),
uiOutput("selectControls"),
actionButton("update", "Go!")
),
mainPanel(
chartJSRadarOutput("radarChart", width = "450", height = "300")
)
)
))
server.R
library(xlsx)
library(dplyr)
library(radarchart)
library(data.table)
source("chart.R")
library(shiny)
library(grDevices)
shinyServer(function(input, output, session) {
output$radioButtons <- renderUI({
dataInput <- reactive({input$var})
z <- dataInput()
buttons <- numrounds(z)
radioButtons("button", "Select a round: ", choices = buttons, inline = FALSE)
})
output$selectControls <- renderUI({
dataInput <- reactive({input$var})
z <- dataInput()
dataInput1 <- reactive({input$button})
y <- dataInput1()
winner <- mydata %>%
filter(tourney_name == z) %>%
filter(round == y) %>%
select(winner_name) %>%
sapply(as.character) %>%
as.vector()
loser <- mydata %>%
filter(tourney_name == z) %>%
filter(round == y) %>%
select(loser_name) %>%
sapply(as.character) %>%
as.vector()
players <- c(winner, loser)
selectInput("select", "Select a match: ", choices = players, selected = 1, multiple = FALSE)
})
output$radarChart <- eventReactive(input$update, {
renderChartJSRadar({
dataInput1 <- reactive({input$var})
z <- dataInput1()
dataInput2 <- reactive({input$button})
y <- dataInput2()
dataInput3 <- reactive({input$select})
x <- dataInput3()
match <- mydata %>%
filter(tourney_name == z) %>%
filter(round == y) %>%
filter(winner_name == x)
scoresw <- vector()
scoresl <- vector()
for(j in 25:33) {
scoresw <- c(scoresw, match()[j])
}
for(j in 34:42) {
scoresl <- c(scoresl, match()[j])
}
scores <- list(winner = scoresw, loser = scoresl)
labs <- c("Aces", "Double Faults", "Service points", "1st Service In", "1st Service won", "2nd Service won", "Service games", "Break points saved", "Break points faced")
c <- grDevices::col2rgb(c("green", "red"))
chartJSRadar(scores = scores, labs = labs, labelSize = 15, colMatrix = c)
})
})
})
chart.R
mydata <- read.csv("Match Radar/Data/atp_matches_2014_edited.csv", header = TRUE)
tour <- unique(data$tourney_name)
numrounds <- function(z) {
for(i in 1:64) {
rounds <- mydata %>%
filter(tourney_name == z) %>%
summarise(number = n_distinct(round))
if(rounds == 3){
buttons <- c("RR", "SF", "F")
}
else if(rounds == 5){
buttons <- c("R32", "R16", "QF", "SF", "F")
}
else if(rounds == 6){
buttons <- c("R64", "R32", "R16", "QF", "SF", "F")
}
else {
buttons <- c("R128", "R64", "R32", "R16", "QF", "SF", "F")
}
}
buttons
}
I put your app in a single file for debugging simplicity.
The menus display correctly: the shiny part should work. The basic idea is that the input variables are already reactive, so to build a reactive function out of them is redundant (at least in this case).
In renderChartJSRadar
z,y and x are initialised correctly (once the initial, NULL cases are discarded). Also renderChartJSRadar
is already reactive, but as it is "eagerly reactive" it starts when the other values are not set, hence the filtering for NULL.
In renderChartJSRadar
there is debugging to do in the R logic that calculate the score. Currently there is an error: unfortunately I cannot help as I cannot tell what you want to achieve - and I don't play tennis :)
library(xlsx)
library(dplyr)
library(radarchart)
# library(data.table)
# source("chart.R")
library(shiny)
library(grDevices)
#------------------------------------------------------------------------------
mydata <- read.csv("./data/atp_matches_2014.csv", header = TRUE)
tour <- unique(mydata$tourney_name)
numrounds <- function(z) {
for(i in 1:64) {
rounds <- mydata %>%
filter(tourney_name == z) %>%
summarise(number = n_distinct(round))
if(rounds == 3){
buttons <- c("RR", "SF", "F")
}
else if(rounds == 5){
buttons <- c("R32", "R16", "QF", "SF", "F")
}
else if(rounds == 6){
buttons <- c("R64", "R32", "R16", "QF", "SF", "F")
}
else {
buttons <- c("R128", "R64", "R32", "R16", "QF", "SF", "F")
}
}
return(buttons)
}
#------------------------------------------------------------------------------
ui <- fluidPage(
titlePanel("Match Radar Chart"),
sidebarLayout(
sidebarPanel(
selectInput("var",
label = "Choose a tournament",
choices = tour,
selected = "Auckland"),
uiOutput("radioButtons"),
uiOutput("selectControls"),
actionButton("update", "Go!")
),
mainPanel(
chartJSRadarOutput("radarChart", width = "450", height = "300")
)
)
)
#------------------------------------------------------------------------------
server <- function(input, output, session){
session$onSessionEnded({ stopApp })
output$radioButtons <- renderUI({
# dataInput <- reactive({input$var})
z <- input$var
buttons <- numrounds(z)
radioButtons("button", "Select a round: ", choices = buttons, inline = FALSE)
})
output$selectControls <- renderUI({
# dataInput <- reactive({input$var})
z <- input$var
# dataInput1 <- reactive({input$button})
y <- input$button #dataInput1()
winner <- mydata %>%
filter(tourney_name == z) %>%
filter(round == y) %>%
select(winner_name) %>%
sapply(as.character) %>%
as.vector()
loser <- mydata %>%
filter(tourney_name == z) %>%
filter(round == y) %>%
select(loser_name) %>%
sapply(as.character) %>%
as.vector()
players <- c(winner, loser)
selectInput("select", "Select a match: ", choices = players, selected = 1, multiple = FALSE)
})
output$radarChart <- renderChartJSRadar({
# browser()
if(is.null(input$button )) return()
if(is.null(input$select )) return()
# dataInput1 <- reactive({input$var})
z <- input$var # dataInput1()
# dataInput2 <- reactive({input$button})
y <- input$button # dataInput2()
# dataInput3 <- reactive({input$select})
x <- input$select # dataInput3()
match <- mydata %>%
filter(tourney_name == z) %>%
filter(round == y) %>%
filter(winner_name == x)
scoresw <- vector()
scoresl <- vector()
for(j in 25:33) {
scoresw <- c(scoresw, match()[j])
}
for(j in 34:42) {
scoresl <- c(scoresl, match()[j])
}
scores <- list(winner = scoresw, loser = scoresl)
labs <- c("Aces", "Double Faults", "Service points", "1st Service In", "1st Service won", "2nd Service won", "Service games", "Break points saved", "Break points faced")
c <- grDevices::col2rgb(c("green", "red"))
chartJSRadar(scores = scores, labs = labs, labelSize = 15, colMatrix = c)
})
}
#------------------------------------------------------------------------------
shinyApp(ui, server)
As far as preventing the radar chart to be drawn every time the user changes one of the three inputs, this is possible using isolate
.
For example (code not tested, but it should work :) )
output$radarChart <- renderChartJSRadar({
if(is.null(input$button )) return()
isolate({
if(is.null(input$select )) return()
z <- input$var # dataInput1()
y <- input$button # dataInput2()
x <- input$select # dataInput3()
})
Or something very similar. Take for example input$var
. As it is within isolate
, any change by the user will not trigger the execution of renderChartJSRadar
. In the code above only changes to input$button trigger the execution of renderChartJSRadar
.