Pete900
Pete900

Reputation: 2166

Check for NULL ggplot object before adding layers in dplyr pipe

I have template plot functions that check whether a data frame has > 0 rows before running:

library(tidyverse)

plot_fun <- function(df) {
  if (nrow(df) > 0) {
    df %>%
      ggplot(., aes(Sepal.Length, Sepal.Width)) +
      geom_point()
  } 
}

Then I use this within pipes and add bespoke layers:

iris %>% plot_fun() + ggtitle("Plot me")

enter image description here

However if the data framed piped in has 0 rows I get an error because I am trying to add layers to nothing:

iris %>% 
  filter(Sepal.Length > 1000) %>% 
  plot_fun() +
  ggtitle("Plot me")

Error in iris %>% filter(Sepal.Length > 1000) %>% plot_fun() + ggtitle("Plot me") : non-numeric argument to binary operator

I can build a check mid pipe to avoid the error:

iris %>%
  filter(Sepal.Length > 1000) %>%
  plot_fun() %>%
  {
    if (!is.null(.)) {
      . +
        ggtitle("Plot me")
    }
  }

That works but that seems clunky. Is it possible to make a function check_df_pipe() or something to stop the pipe if preceding filters remove all data? Or maybe to check if the pipe . is NULL at a point?

check_df_pipe <- function(x) {
  if(nrow(x) > 0) {
    x 
  } else{
    stop("Dont return an error just to exit pipe")
  }
}

iris %>%
  filter(Sepal.Length > 1000) %>%
  check_df_pipe() %>% 
  plot_fun() +
  ggtitle("Plot me")

Error in check_df_pipe(.) : Dont return an error just to exit pipe

Or any other ideas on how to deal with this? I don't want to add ggtitle("Plot me") into plot_fun because I wan't plot_fun to stay generic.

Upvotes: 2

Views: 2341

Answers (2)

A. S. K.
A. S. K.

Reputation: 2816

Option 1: Blank plot

This doesn't directly answer your question, but how about a different approach? When the dataframe has zero rows, use geom_blank() to create an empty plot. Adding a title later doesn't return an error.

plot_fun <- function(df) {
  if (nrow(df) > 0) {
    df %>%
      ggplot(., aes(Sepal.Length, Sepal.Width)) +
      geom_point()
  } else {
    ggplot(data = data.frame()) +
      geom_blank()
  }
}

iris %>% 
  filter(Sepal.Length > 1000) %>% 
  plot_fun() +
  ggtitle("Plot me")

Option 2: Pass layers into a function

You can pass layers as function arguments. Here's a function that checks whether the plot exists, and then adds the layers only if it does.

append_layers_maybe <- function(p, l) {
  if(!is.null(p)) {
    p + l
  }
}

iris %>%
  plot_fun() %>%
  append_layers_maybe(facet_wrap(~ Species)) %>%
  append_layers_maybe(ggtitle("Foo"))

iris %>%
  filter(Sepal.Length > 1000) %>%
  plot_fun() %>%
  append_layers_maybe(facet_wrap(~ Species)) %>%
  append_layers_maybe(ggtitle("Plot me"))

Upvotes: 2

Florian
Florian

Reputation: 1258

I would not recommend such piping. It can be confusing to decode such a code. But here is a solution:

cond_pipe <- function(x) {
  if (nrow(x) > 0) {
    x %>%
      plot_fun() +
      ggtitle("Plot me")
  } else {
    warning("Dont return an error just to exit pipe")
  }
}

iris %>%
  filter(Sepal.Length > 1000) %>%
  cond_pipe()

Upvotes: 1

Related Questions