Chaeeun Lee
Chaeeun Lee

Reputation: 67

How to remove NA value from the ggplot in shiny app?

library(shiny)
library(palmerpenguins)
library(ggplot2)
library(dplyr)

penguin <- penguins

penguin$year <- as.factor(penguin$year)




ui <- fluidPage(
  titlePanel("Data Visualisation of Penguins Data"),
  sidebarPanel(
    selectInput("yaxis",
                label = "Choose a y-axis variable to display",
                choices = list("bill_length_mm",
                               "bill_depth_mm",
                               "flipper_length_mm",
                               "body_mass_g"),
                selected = "bill_length_mm"),
    selectInput("xaxis",
                label = "Choose a x-axis variable to display",
                choices = c("species",
                            "sex",
                            "year"),
                selected = "sex"),
    checkboxGroupInput("islandlevels",
                       label = "Check to display different island levels",
                       choices = c("island"),
                       selected = NULL),
    br(), br(),
    selectInput("species",
                label = "Choose species to view separate plot",
                choices = list("Adelie",
                               "Chinstrap",
                               "Gentoo"),
                selected = NULL)),
  mainPanel(
    plotOutput("plot1"),
    br(), br(),
    plotOutput("plot2")
  )
)





server <- function(input, output){
  output$plot1 <- renderPlot({
    if(is.null(penguin))
      return(NULL)
  ggplot(penguin, aes(x = penguin[[input$xaxis]], y = penguin[[input$yaxis]])) +
    geom_boxplot()
})
}

shinyApp(ui = ui, server = server)

This is my shiny code, but I'd like to remove NA value when x-axis variable is sex. I can't just remove row with NA values because I have to use variable (that is not missing value but the row has missing value such as row 9 in image 2) when I change x-axis variable or/and y-axis variable. I wanted to find the solution but I wonder what function should I use. Do I have to use if statement, reactive function, or else?

Thank you for help in advance.

sex variable with NA value(want to delete NA on my plot)

NA values in penguin data

Upvotes: 3

Views: 639

Answers (2)

r2evans
r2evans

Reputation: 160437

Conditionally filter your data, perhaps something like this:

  dat <- reactive({
    if (input$xaxis == "sex") penguin[ !is.na(penguin$sex), ] else penguin
  })
  output$plot1 <- renderPlot({
    req(penguin, input$xaxis, input$yaxis)
    ggplot(dat(), aes_string(x = isolate(input$xaxis), y = input$yaxis)) +
      geom_boxplot()
  })

Several critical changes here:

  1. In case you want to do more than a single plot with the filtered data, I make a reactive data component named dat with the filtered data. In this way, if you ever add (say) a table or another plot or something, you don't need to handle selective filtering in each of them, you just need to use dat() everywhere and everything benefits from it.

  2. Reactive can be volatile, and having both the data and the plot reacting to input$xaxis will cause the plot to be rendered twice for each change to xaxis. Because of this, I isolate(input$xaxis) in the plot reactive. When the user changes xaxis, the dat will change which will trigger (once!) the plot to change. (No need to isolate yaxis, as that's correct in this case.)

  3. In general, you should not use ggplot2(x, aes(x$a, x$b)). More specifically, using $ and/or [[ in aesthetic definitions is poor practice, and will fail given certain situations. It is much better to use aes with symbols (e.g., cyl from mtcars) or aes_string with strings ("cyl"). Since you're defining the aesthetics programmatically, it is better to use aes_string.

  4. I changed your if (is.null(penguin)) to shiny's more canonical req, and added checks in the inputs as well. While most simpler shiny apps don't always need this, I've found that more complex apps can cause just enough delay in input instantiation that an output reactive block may trigger before all inputs have been assigned, meaning in this example it might be possible for input$xaxis to be null. While unlikely in simpler shiny apps like this, I still think it's safe.

    There may be reasons to use individual req lines, one for each input. The results in this case will be the same, but there are times when it makes sense to break them out.

    The use of req prohibits the rest of the plot rendering from occurring, but it also does it in a way that shiny components recognize, not causing errors or rendering issues. (I prefer it to manual if (is.null(.)) return(NULL) logic.)


Note: I think @stefan's answer may be the more canonical way in ggplot2 to omit NA values from the axis, so perhaps that is the best way to go for that side of things. However, I still believe that points 3 and 4 are still (also) relevant to your app even with stefan's change.

Upvotes: 1

stefan
stefan

Reputation: 124083

You can prevent the NA values of showing up as categories by making use of scale_x_discrete(na.translate = FALSE):

library(ggplot2)
library(palmerpenguins)
ggplot(penguins, aes(x = sex, y = bill_length_mm)) +
  geom_boxplot() +
  scale_x_discrete(na.translate = FALSE)
#> Warning: Removed 11 rows containing missing values (stat_boxplot).

Upvotes: 2

Related Questions