Search code examples
rggplot2shinyaes

Does aes_string() change any default settings in R? A problem with R Shiny and ggplot input$ interaction


I'm creating a Shiny app in Rstudio and I had trouble inserting an input$char into a ggplot boxplot, where input$char was the variable for y-axis, and the x-axis was not an input variable, but instead from the data frame. The issue was that the data wouldn't appear in the graph, but the input function of the dropdown menu still changed the y-axis in the graph when the app was loaded. Example code:

g = reactive({ggplot(data=iris, aes(x=Species, y=input$ybox))+ theme(legend.position = "top")})
output$Boxplot = renderPlot(g() + geom_boxplot(aes(fill=Species)) + scale_fill_brewer(palette 
  = "Set2"))

I found through another post on here that aes_string() could fix the problem, and it did. Where aes is in ggplot, I fixed the error with aes_string() and let x="Species" in the function....

except now all of my other variables are not recognized in the rest of my shiny app! Where ever there is Species or another input$char variable, they are not recognized and I receive an error. Either object 'Species' not found, or unused arguments 'input$char;,...,'Species'.

Example code that gives error:

  selection = reactive({iris[, aes(c(input$xcol, input$ycol, 'Species'))]}) 
  fit = reactive({knn3(Species ~ ., data=selection(), k = input$K)}) 
  output$KNNplot = renderPlot({decisionplot(fit(), selection(), class=Species,main=paste('Decision Boundary for KNN (',input$K,')', sep=' '))})
  

Is there any way I can have both a solution to the ggplot error and for the rest of my app to still work?

Edit (Reproducible code):

library(shiny)
library(shinyWidgets)
library(lattice)
library(ggplot2)
library(RColorBrewer)
library(nnet)
library(naivebayes)
library(e1071)
library(randomForest)
library(MASS)

# function for decisionplot.R
decisionplot <- function(model, data, class = NULL, predict_type = "class",
                         resolution = 100, showgrid = TRUE, ...) {
  
  if(!is.null(class)) cl <- data[,class] else cl <- 1
  data <- data[,1:2]
  k <- length(unique(cl))
  
  plot(data, col = as.integer(cl)+1L, pch = as.integer(cl)+1L, ...)
  
  # make grid
  r <- sapply(data, range, na.rm = TRUE)
  xs <- seq(r[1,1], r[2,1], length.out = resolution)
  ys <- seq(r[1,2], r[2,2], length.out = resolution)
  g <- cbind(rep(xs, each=resolution), rep(ys, time = resolution))
  colnames(g) <- colnames(r)
  g <- as.data.frame(g)
  
  ### guess how to get class labels from predict
  ### (unfortunately not very consistent between models)
  p <- predict(model, g, type = predict_type)
  if(is.list(p)) p <- p$class
  p <- as.factor(p)
  
  if(showgrid) points(g, col = as.integer(p)+1L, pch = ".")
  
  z <- matrix(as.integer(p), nrow = resolution, byrow = TRUE)
  contour(xs, ys, z, add = TRUE, drawlabels = FALSE,
          lwd = 2, levels = (1:(k-1))+.5)
  
  invisible(z)
}

ui = fluidPage(
  navlistPanel(
    tabPanel("Descriptive Statistics",
             fluidRow(
               column(6,
                      wellPanel(selectInput('ybox', 'Y Variable', names(iris)[1:4], selected = names(iris)[[2]])),
                      plotOutput('Boxplot'))),
              fluidRow(
               column(12, 
                      wellPanel(selectInput('xdens', 'X Variable', names(iris)[1:4], selected= names(iris)[2])),
                      plotOutput("Densplot")))),
    tabPanel("Naive Bayes Classifier",
           wellPanel(selectInput('xcol5', 'X Variable', names(iris)[1:4], selected = names(iris)[[1]]),
                     selectInput('ycol5', 'Y Variable', names(iris)[1:4], selected = names(iris)[[2]])),
           plotOutput('NBplot'))
  
    ))

server = function(input, output) {
   
  # Boxplot
  g = reactive({ggplot(data=iris, aes_(x='Species', y=iris[[as.name(input$ybox)]]))+ theme(legend.position = "top")})
  output$Boxplot = renderPlot(g() + geom_boxplot(aes(fill=Species)) + scale_fill_brewer(palette = "Set2") + ylab(input$ybox))
  
  
  # Densityplot
  c = reactive({ggplot(iris, aes_(iris[[as.name(input$xdens)]]))})
  mu = reactive({ddply(iris, "Species", summarise, grp.mean=mean(c()))})
  dc = reactive({c() + geom_density(kernel="gaussian", aes(color=Species, fill=Species), alpha=0.4) +
      geom_vline(data=mu(), aes(xintercept=mu()$grp.mean, color=Species),linetype="dashed") +
      theme(legend.position = "top") + xlab(input$xdens)})
  output$Densplot = renderPlot({dc() + scale_color_brewer(palette="Dark2") + scale_fill_brewer(palette = "Dark2")})
  
  
  #example model
  selection5 = reactive({iris[, c(input$xcol5, input$ycol5, 'Species')]}) 
  fit5 = reactive({naive_bayes(Species ~ ., data=selection5(), usekernel = T)})
  output$NBplot = renderPlot({decisionplot(fit5(), selection5(), class = "Species",
                                           main = "Decision Boundary for Naive Bayes Classifier")})
  
  
}

shinyApp(ui = ui, server = server)

Solution

  • Your selection5 was an issue. The following code gives a reactive data frame.

      #example model
      selection5 <- reactive({
        # df <- iris[, c(input$xcol5, input$ycol5, 'Species')] ## this call does not work
        df <- data.frame(x=iris[[input$xcol5]], y=iris[[input$ycol5]], Species=iris[, "Species"])
      })