Search code examples
rggplot2r-package

Dispatching code depending on whether ggplot2 is installed


I'm working on an R package that I don't want to have any hard dependencies on other packages. One of the functions I'd like to include is a plotting function and ggplot2 offers a lot of nice functionality, but I don't want to require users to install it if it isn't already installed. My thought was to create something like the below:

package_quickplot <- function(given_data_frame){
  if("ggplot2"%in%.packages()){
    ggplot2::ggplot(given_data_frame) +
      ggplot2::geom_line(ggplot2::aes(x=x_vals, y=y_vals))
  } else {
    with(given_data_frame, plot(x_vals, y_vals, type="l"))
  }
}

where the function checks if ggplot2 is loaded, returning a ggplot if so or a base plot if not. This feels like a bad idea, but I can't articulate exactly why. I'd obviously include ggplot2 in the Suggests header and it currently passes devtools::check() but I'm a little leery because I don't think I've seen this implemented elsewhere. The R Extensions manual describes the Suggests field as "includes packages used only in examples, tests or vignettes" which implies to me that this isn't what's intended. They describe in detail use cases for the if(require("pkgname")) for suggested code but specifically mention that require/library shouldn't be used in package code because it alters the search path.

Is it fine to dispatch code in this way?


Solution

  • It's not standard to dispatch according to whether a package is loaded, which is what your code does. Typically, one would use requireNamespace to ensure that a package is installed. In fact, this is what ggplot2 itself uses for suggested packages such as sf, hexbin ragg, and Hmisc. For example, here. This is quicker than using 'ggplot2' %in% installed.packages()

    So your code might be better written as:

    package_quickplot <- function(given_data_frame) {
    
      if(requireNamespace("ggplot2", quietly = TRUE)) {
        ggplot2::ggplot(given_data_frame) +
          ggplot2::geom_line(ggplot2::aes(x = x_vals, y = y_vals))
      } else {
        with(given_data_frame, plot(x_vals, y_vals, type = "l"))
      }
    }
    

    Though I might separate the logic even more, and have your function dispatch an unexported ggplot-based plotting function or an unexported base R plotting function depending on requireNamespace.