Search code examples
rggplot2shinyflexdashboard

facet_grid in Shiny flexdashboard giving error "Faceting variables must have at least one value"


I am having some trouble getting a ggplot2 facet_grid plot working for an evaluation system. The plot renders well but I get the following error in the browser and console:

Error in : Faceting variables must have at least one value

This occurs every time I switch the brand entry based on the input input$brand. The application doesn't crash but the error message is annoying.

I have prepared this reproducible example:

---
title: "Power ranking for mtcars"
runtime: shiny
output:
  flexdashboard::flex_dashboard:
    orientation: rows
    source_code: embed
---



```{r rows.print = 25}
library(dplyr)
library(ggplot2)

mtcars_tidy <- mtcars %>% 
    tibble::rownames_to_column() %>% 
    rename(model = rowname) %>% 
    mutate(brand = gsub( " .*$", "", model )) %>% 
    mutate(model = model) %>% 
    select(brand, model,  everything())  %>% 
    tidyr::gather(key = 'measure', value = "value", mpg:carb) %>%
    mutate(ranking = as.factor(sample(x = c(1, 2, 3), size = n(), replace = TRUE))) %>%

    mutate(power = case_when(
        .$measure == "hp" & value > 200 | (.$measure == "cyl" & value == 8) ~ "high",
        .$measure == "hp" & value < 200 | (.$measure == "cyl" & value == 8) ~ "medium",
        .$measure == "hp" & value > 100 | (.$measure == "cyl" & value == 6) ~ "high",
        .$measure == "hp" & value < 100 | (.$measure == "cyl" & value == 6) ~ "medium",
        .$measure == "hp" & value > 50  | (.$measure == "cyl" & value == 6) ~ "high",
        .$measure == "hp" & value < 50  | (.$measure == "cyl" & value == 6) ~ "medium",

        .$measure == "hp" & value > 200 | (.$measure == "carb" & value >  4) ~ "high",
        .$measure == "hp" & value < 200 | (.$measure == "carb" & value <= 4) ~ "medium",
        .$measure == "hp" & value > 100 | (.$measure == "carb" & value >  2.8) ~ "high",
        .$measure == "hp" & value < 100 | (.$measure == "carb" & value <= 2.8) ~ "medium",
        .$measure == "hp" & value > 50  | (.$measure == "carb" & value > 2) ~ "high",
        .$measure == "hp" & value < 50  | (.$measure == "carb" & value <= 2) ~ "medium",
        TRUE ~ "low"
    )) 
```

# Sidebar {.sidebar data-width="350"}

```{r}
selectInput("brand", "Brand of the car", 
            choices = unique(mtcars_tidy$brand))

renderUI({
    selectInput("model", "Car model",
                choices = mtcars_tidy$model[mtcars_tidy$brand == levels(mtcars_tidy$brand)[1]])
})

br()

observe({
    brand <- input$brand
    updateSelectInput(session, "model", 
                      choices = mtcars_tidy$model[mtcars_tidy$brand == brand])
})    


# when switching the brand of the car, input$brand this error pops up:
# Error in : Faceting variables must have at least one value
```


# Main

##

### Plot power ranking for each measure

```{r}
nameorder <- make.unique(mtcars_tidy$measure[order(mtcars_tidy$power, mtcars_tidy$ranking)])
mtcars_tidy$measure <- factor(mtcars_tidy$measure, levels=nameorder, 
                                   ordered = TRUE)

dataset <- reactive({
    subset(mtcars_tidy, brand == input$brand & model == input$model) 
})


renderPlot({
    ggplot(dataset(), aes(x = ranking, y = measure)) +
        geom_segment(aes(yend = measure), xend=0, color = "grey50") +
        geom_point(size = 3, aes(colour = power)) +
        scale_colour_brewer(palette="Set1", limits = c("high","medium", "low")) +
        theme_bw() +
        theme(panel.grid.major.y = element_blank()) +   # No horizontal grid lines
        facet_grid(power ~ ., scales="free_y", space="free_y") +
        ggtitle(paste0("Brand: ", input$brand, ", Model: " , input$model))
})    
```

EDIT 1: I changed facet_grid to facet_wrap but the error still there.

EDIT 2: As per suggestion, I switched to facet_wrap with this formula: p <- p + facet_wrap(power ~ .). Still same error. I also tried this other formula p <- p + facet_wrap(power ~ ranking). Error still there.

EDIT 3: On the facet_wrap function I also tried with these formulas as well:

  • facet_wrap(~power )
  • facet_wrap(vars(power ))
  • facet_wrap(vars(power , ranking)).

The error is still the same (identical). No change (Error in : Faceting variables must have at least one value).

EDIT 4: If I try with facet_wrap(power), the error is even worse because crashes Shiny with this mouthful:

Error: Column `function (lambda = 1) \n{\n    if (!is.numeric(lambda) || is.na(lambda)) \n        stop("invalid argument 'lambda'")\n    if (lambda <= 0) \n        return(make.link("log"))\n    if (lambda == 1) \n        return(make.link("identity"))\n    linkfun <- function(mu) mu^lambda\n    linkinv <- function(eta) pmax(eta^(1/lambda), .Machine$double.eps)\n    mu.eta <- function(eta) pmax((1/lambda) * eta^(1/lambda - \n        1), .Machine$double.eps)\n    valideta <- function(eta) all(is.finite(eta)) && all(eta > \n        0)\n    link <- paste0("mu^", round(lambda, 3))\n    structure(list(linkfun = linkfun, linkinv = linkinv, mu.eta = mu.eta, \n        valideta = valideta, name = link), class = "link-glm")\n}` must be a 1d atomic vector or a list

Solution

  • This error is occuring when dataset() has no rows in it. When I run your code (the current version with the facet_grid(power ~ .,), it actually works fine. When I choose a new brand, there is a short gap where it displays this error while the input$model list updates. Once it does, and the combination of brand and model return rows, the plot displays nicely.

    You can prevent this gap by using req to hold off rendering the plot until certain requirements are met. Just insert the following code at the top of your renderPlot

    req(nrow(dataset()) > 0)
    

    This will prevent the renderPlot from running if dataset() doesn't contain at least one row. In that case, the plot will just be blank (removing the scary error message) until the data is ready to use. With that line added, your app seems to be running fine (and looks quite nice, by the way).


    You can see the source of that error message by testing your code outside the shiny context. Here's a minimal example of your plot:

    ggplot(dataset, aes(x = ranking, y = measure)) +
            geom_segment(aes(yend = measure), xend=0, color = "grey50") +
            geom_point(size = 3, aes(colour = power)) +
            facet_grid(power ~ ., scales="free_y", space="free_y")
    

    when I make dataset using this call:

    dataset <- subset(mtcars_tidy, brand == 'Honda' & model == 'Honda Civic')
    

    The plot renders correctly. When I use a subset that doesn't return any rows:

    dataset <- subset(mtcars_tidy, brand == 'Honda' & model == 'Civic')
    

    I get your same error:

    Error: Faceting variables must have at least one value