ggplot not working properly inside eventReactive() in shiny - r

I wasted hours to find out why my plot is automatically updating itself when I change inputs while it was supposed to wait for the Run button but it simply ignored that step and I ended up finally finding ggplot as the trouble maker!!! This is my minimal code:
library(ggplot2)
library(tidyverse)
varnames <- names(cars)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(
column(
width = 12,
# Variables Inputs:
varSelectInput("variables", "Select Input Variables", cars, multiple = TRUE),
selectizeInput("outvar", "Select Output Variable", choices = varnames, "speed", multiple = F),
# Run Button
actionButton(inputId = "run", label = "Run")
)
)
),
# Main panel for displaying outputs ----
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
df <- reactive({
cars %>% dplyr::select(!!!input$variables, input$outvar)
})
plt <- eventReactive(input$run, {
#Just creating lm formula
current_formula <- paste0(input$outvar, " ~ ", paste0(input$variables, collapse = " + "))
current_formula <- as.formula(current_formula)
#Fitting lm
fit <- lm(current_formula, data = df())
pred <- predict(fit, newdata = df())
#Plotting
ggplot(df(), aes(df()[, input$outvar], pred)) +
labs(x = "Observed", y = "Predicted") +
geom_point() +
theme_bw()
#plot(df()[, input$outvar], pred) #This one works fine!!!!
})
output$plot <- renderPlot({
plt()
})
}
# Run the application
shinyApp(ui = ui, server = server)
If you run this, you'll notice that ggplot doesn't care anymore about the Run button after the 1st run and it keeps updating as you change the inputs!! However, if you use the simple base plot function (which I put in a comment in the code) there wouldn't be any problems and that works just fine! Sadly I need ggplot in my app because base plot is ugly. I am seeing suggestion for using isolate() to solve this issue but I have no clue where isolate() should be put to fix my problem also it doesn't make sense to use isolate() when base plot function works fine without it and it's the ggplot that makes the problem. Any explanation would be appreciated.

The issue is that ggplot aesthetics are lazy evaluated. You really want to put symbols into the aes() rather that reactive data values. Change your plotting code to
ggplot(df(), aes(.data[[input$outvar]], pred)) +
labs(x = "Observed", y = "Predicted") +
geom_point() +
theme_bw()
With ggplot you use the .data pronoun to access the current data source rather than trigger the reactive df() object again.

Related

ggplot not working in eventReactive() correctly in shiny

I wasted hours to find out why my plot is automatically updating itself when I change inputs while it was supposed to wait for the Run button but it simply ignored that step and I ended up finally finding ggplot as the trouble maker!!! This is my minimal code:
library(ggplot2)
library(tidyverse)
varnames <- names(cars)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(
column(
width = 12,
# Variables Inputs:
varSelectInput("variables", "Select Input Variables", cars, multiple = TRUE),
selectizeInput("outvar", "Select Output Variable", choices = varnames, "speed", multiple = F),
# Run Button
actionButton(inputId = "run", label = "Run")
)
)
),
# Main panel for displaying outputs ----
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
df <- reactive({
cars %>% dplyr::select(!!!input$variables, input$outvar)
})
plt <- eventReactive(input$run, {
#Just creating lm formula
current_formula <- paste0(input$outvar, " ~ ", paste0(input$variables, collapse = " + "))
current_formula <- as.formula(current_formula)
#Fitting lm
fit <- lm(current_formula, data = df())
pred <- predict(fit, newdata = df())
#Plotting
ggplot(df(), aes(df()[, input$outvar], pred)) +
labs(x = "Observed", y = "Predicted") +
geom_point() +
theme_bw()
#plot(df()[, input$outvar], pred) #This one works fine!!!!
})
output$plot <- renderPlot({
plt()
})
}
# Run the application
shinyApp(ui = ui, server = server)
If you run this, you'll notice that ggplot doesn't care anymore about the Run button after the 1st run and it keeps updating as you change the inputs!! However, if you use the simple base plot function (which I put in a comment in the code) there wouldn't be any problems and that works just fine! Sadly I need ggplot in my app because base plot is ugly. I am seeing suggestions for using isolate() to solve this issue but I have no clue where isolate() should be put to fix my problem also it doesn't make sense to use isolate() when base plot function works fine without it and it's the ggplot that makes the problem. Any explanation would be appreciated.
It seems to work fine if you follow the ggplot's preferred method of passing column names i.e using .data.
library(ggplot2)
library(shiny)
server <- function(input, output, session) {
df <- reactive({
cars %>% dplyr::select(!!!input$variables, input$outvar)
})
plt <- eventReactive(input$run, {
#Just creating lm formula
current_formula <- paste0(input$outvar, " ~ ", paste0(input$variables, collapse = " + "))
current_formula <- as.formula(current_formula)
#Fitting lm
fit <- lm(current_formula, data = df())
pred <- predict(fit, newdata = df())
#Plotting
ggplot(df(), aes(.data[[input$outvar]], pred)) +
labs(x = "Observed", y = "Predicted") +
geom_point() +
theme_bw()
})
output$plot <- renderPlot({
plt()
})
}
# Run the application
shinyApp(ui = ui, server = server)
I think the issue is that ggplot is processing things lazily. If you make one change to your code to pre-pull df() and input$outvar, the over-reactivity is fixed:
plt <- eventReactive(input$run, {
#Just creating lm formula
current_formula <- paste0(input$outvar, " ~ ", paste0(input$variables, collapse = " + "))
current_formula <- as.formula(current_formula)
#Fitting lm
fit <- lm(current_formula, data = df())
pred <- predict(fit, newdata = df())
#Plotting
dat <- df()
outv <- input$outvar
ggplot(dat, aes(dat[, outv], pred)) +
labs(x = "Observed", y = "Predicted") +
geom_point() +
theme_bw()
#plot(df()[, input$outvar], pred) #This one works fine!!!!
})
The issue is that ggplot is somehow internally preserving some of the reactivity.
(I placed the assignment to dat and outv immediately before ggplot, just for demonstration. It might be more sensible to assign them first-thing in the eventReactive block and use dat for everything, just for consistency in code (none of the other code is operating on lazy principles).

Interactive Bar Chart Using Shiny - Graph changes based on selected columns

I am teaching myself r and shiny and trying to make an interactive bar chart where the user can change the chart based on columns. I keep getting errors with this code. Any help would be appreciated! My data has four columns: v, one, two, three. The first column is characters and the last three are numbers. I want to change the bar chart based on the y axis (columns: one, two and three). Right now, the error I am getting is: missing value where TRUE/FALSE needed.
library(shiny)
library(readr)
library(ggplot2)
data <- read.csv('scoring.csv')
data$v <- as.character(data$v)
ui <- fluidPage(
titlePanel("Scoring"),
sidebarPanel(
selectInput(inputId = "scoring", label = "Select a score:", c("Scoring Method 1", "Scoring Method 2", "Scoring Method 3"))),
mainPanel(
plotOutput(outputId = "bar")
)
)
#browser()
server <- function(input, output) {
new_data <- reactive({
selected_score = as.numeric(input$"scoring")
if (selected_score == "Scoring Method 1"){(data[data$one])}
if (selected_score == "Scoring Method 2"){(data[data$two])}
if (selected_score == "Scoring Method 3"){(data[data$three])}
})
#browser()
output$bar <- renderPlot({
newdata <- new_data()
p <- ggplot(newdata, aes(x=reorder(v, -selected_score), selected_score, y = selected_score, fill=v)) +
geom_bar(stat = 'identity', fill="darkblue") +
theme_minimal() +
ggtitle("Sports")
barplot(p, height = 400, width = 200)
})
}
Run the application
shinyApp(ui = ui, server = server)
You have a few errors in your code. In the server part, please use input$scoring, instead of input$"scoring".
First, in ui selectInput could be defined as
selectInput(inputId = "scoring", label = "Select a score:", c("Scoring Method 1"="one",
"Scoring Method 2"="two",
"Scoring Method 3"="three")))
Second, your reactive dataframe new_data() could be defined as shown below:
new_data <- reactive({
d <- data %>% mutate(selected_score = input$scoring)
d
})
Third, ggplot could be defined as
output$bar <- renderPlot({
newdata <- new_data()
p <- ggplot(newdata, aes(x=v, y = newdata[[as.name(selected_score)]], fill=v)) +
geom_bar(stat = 'identity', position = "dodge", fill="blue") +
theme_bw() +
#scale_fill_manual(values=c("blue", "green", "red")) +
scale_y_continuous(limits=c(0,10)) +
ggtitle("Sports")
p
})
Please note that you had an extra selected_score variable within aes. My suggestion would be to play with it to reorder x, and review some online or youtube videos on R Shiny.

How to display ggplot2 on Rshiny main panel

The ggplot2 object doesnt display properly in RShiny mainpanel. For recreation, the below code uses iris dataset. Need help
I checked the link - RShiny ggplot2 not showing , but this didnt help. I also ran through https://shiny.rstudio.com/ website, but nothing had explanation with example on how to display the ggplot2 object. I used renderPlot and renderImage functions, but none gave required results.
'''
library(shiny)
library(shinydashboard)
ui <- fluidPage(titlePanel("Sample Shiny"),
navbarPage(
br(),
tabPanel(h4("Iris Data"),
sidebarPanel(
radioButtons("var1",
label = "Choose a FILL field",
choices = c("Species"),
selected = "Species"),
mainPanel(plotOutput("plot",click = "plot_click")))
)
))
server <- function(input, output) {
output$plot <- renderPlot(
{
#browser()
sw <- input$var1
### "a" below is iris dataset which I pass on as input**
ggplot(data = a) +
aes(x = Sepal.Length, fill = sw) +
geom_bar() +
theme_minimal() +
coord_flip()
},width = "auto",height = "auto",res = 72)
}
# Run app ----
shinyApp(ui, server)
'''
I was hoping to see the graph in the middle of mainpanel, but all I see is a small graph with no proper margins.
Expected: (Something like this on RShiny)
Here is what I see now:
input$var1 is a string => use aes_string:
ggplot(data = a) +
aes_string(x = "Sepal.Length", fill = sw) + ......

Unable to plot stacked barplot using R Shiny

I am new to R Shiny. Actually i have drawn Stacked Barplot using ggplot in my
R code. I want to draw the same using shiny. Below is my R code:
ggplot(data = df, aes(x = OutPut, y = Group, fill = Group)) +
geom_bar(stat = "identity") +
facet_grid(~ Environment)
In my R code it is giving correct results.But i am trying to draw in shiny. Below is my shiny R code.
ui <- fluidPage(theme = shinytheme("lumen"),
titlePanel("Data Analysis"),
selectInput("variable", "Variable:", c("OutPut", "Member", "Levels")),
mainPanel(plotOutput("plot")))
# Define server function
server <- function(input, output){
x = ggplot(data = df, aes(x = variable.names(), y = Group, fill = Group)) +
geom_bar(stat = "identity") +
facet_grid(~ Environment)
plot(x)
}
# Create Shiny object
shinyApp(ui = ui, server = server)
It is throwing an error,here i have created a dropdown box where all the variables have been stored. So when i select one variable, it should plot the Stacked barplot. Could anyone please help me.
Like it was mentioned in the comments, you need to use the rendering functions and actually assign them to the output to get the outputs you need.
I believe an example of using plots in rshiny would help, since it wouldn't make sense to have it in the comments, here it is:
library(shiny)
library(ggplot2)
ui <- fluidPage(titlePanel("Fast Example with mtcars"),
# inputs
selectInput("x", "Choose x:", choices = names(mtcars), selected = 'mpg'),
selectInput("y", "Choose y:", choices = names(mtcars), selected = 'hp'),
selectInput("fill", "Choose fill:", choices = names(mtcars), selected = 'carb'),
mainPanel(
#outputs
h2("Chosen variables are:"),
h4(textOutput("vars")),
plotOutput("plot")))
server <- function(input, output) {
df <- mtcars
# here's how you would use the rendering functions
# notice that I used aes_string
output$plot <- renderPlot({
ggplot(data=df,
aes_string(x= input$x, y= input$y, fill=input$fill)) +
geom_point()
})
output$vars <- renderText(paste0('x: ', input$x, " , ",
'y: ', input$y, " , ",
'fill: ', input$fill))
}
shinyApp(ui = ui, server = server)
The Rshiny tutorial is pretty helpful, you can take a look at it here https://shiny.rstudio.com/tutorial/

R: Referring to a variable name with special characters

For a Shiny program I'm writing, I have input variables that contain a dash, commas and brackets. Spaces I can substitute out but the rest are needed since they are refering to chemical compounds and don't make sense without them. As expected, these characters make the Shiny app unable to find the desired variable; whilst variables with none of these characters work fine.
EDITED: The code below is a test Shiny app. With Chemical-X(a,b) the app returns "could not find function X". With Chemical.B the app returns "object Chemical.B not found" which is the desired result since the app sees the chemical as an object and not some function that doesn't exist.
library (shiny)
library (ggplot2)
dat <- as.data.frame(c("Chemical-X(a,b)", "Chemical.B"))
dat[,2] <- (c(6,3))
colnames(dat) <- c("Chemical", "Count")
ui <- fluidPage(
titlePanel("SE Test"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "varX",
label = "Chemical",
choices = dat[,1],
width = "200px"),
selectInput(inputId = "varY1",
label = "Count",
choices = dat[,2],
width = "200px")
),
mainPanel(
plotOutput("chemPlot")
)
)
)
server <- function(input, output){
output$chemPlot <- renderPlot({
plot.data <- ggplot(data = dat)
point <- plot.data + geom_point(
aes_string(x = input$varX, y = input$varY1))
plot(point)
})
}
shinyApp(ui = ui, server = server)
Is there a known way of doing this or will I need to come up with some viable work around? I have tried using backticks as suggested here but this hasn't worked.
Thanks, Matt
I have found that backticks and aes_string usually works for me.
library("ggplot2")
my_dodgy_var <- "name with~special character"
mtcars[[my_dodgy_var]] <- mtcars$cyl
ggplot(mtcars, aes_string(x=paste0("`", my_dodgy_var, "`"), y="mpg")) +
geom_point()
I often use a helper function paste_aes to do this, eg:
paste_aes <- function(x) paste0("`", x, "`")
I've fixed it now by calling as.name the Shiny input$ variable. For the example above it would look like this.
server <- function(input, output){
output$chemPlot <- renderPlot({
plot.data <- ggplot(data = dat)
point <- plot.data + geom_point(
aes_string(x = as.name(input$varX), y = as.name(input$varY1)))
plot(point)
This appears to work now as intended. Thank you aocall for your efforts.

Resources