1#' @export
2#' @examples
3#' ggplot(mpg, aes(displ, hwy)) +
4#'   geom_point(alpha = 0.5, colour = "blue")
5#'
6#' ggplot(mpg, aes(displ, hwy)) +
7#'   geom_point(colour = alpha("blue", 0.5))
8scales::alpha
9
10"%||%" <- function(a, b) {
11  if (!is.null(a)) a else b
12}
13
14"%|W|%" <- function(a, b) {
15  if (!is.waive(a)) a else b
16}
17
18# Check required aesthetics are present
19# This is used by geoms and stats to give a more helpful error message
20# when required aesthetics are missing.
21#
22# @param character vector of required aesthetics
23# @param character vector of present aesthetics
24# @param name of object for error message
25# @keyword internal
26check_required_aesthetics <- function(required, present, name) {
27  if (is.null(required)) return()
28
29  required <- strsplit(required, "|", fixed = TRUE)
30  if (any(vapply(required, length, integer(1)) > 1)) {
31    required <- lapply(required, rep_len, 2)
32    required <- list(
33      vapply(required, `[`, character(1), 1),
34      vapply(required, `[`, character(1), 2)
35    )
36  } else {
37    required <- list(unlist(required))
38  }
39  missing_aes <- lapply(required, setdiff, present)
40  if (any(vapply(missing_aes, length, integer(1)) == 0)) return()
41
42  abort(glue(
43    "{name} requires the following missing aesthetics: ",
44    glue_collapse(lapply(missing_aes, glue_collapse, sep = ", ", last = " and "), sep = " or ")
45  ))
46}
47
48# Concatenate a named list for output
49# Print a `list(a=1, b=2)` as `(a=1, b=2)`
50#
51# @param list to concatenate
52# @keyword internal
53#X clist(list(a=1, b=2))
54#X clist(par()[1:5])
55clist <- function(l) {
56  paste(paste(names(l), l, sep = " = ", collapse = ", "), sep = "")
57}
58
59# Return unique columns
60# This is used for figuring out which columns are constant within a group
61#
62# @keyword internal
63uniquecols <- function(df) {
64  df <- df[1, sapply(df, function(x) length(unique(x)) == 1), drop = FALSE]
65  rownames(df) <- 1:nrow(df)
66  df
67}
68
69#' Convenience function to remove missing values from a data.frame
70#'
71#' Remove all non-complete rows, with a warning if `na.rm = FALSE`.
72#' ggplot is somewhat more accommodating of missing values than R generally.
73#' For those stats which require complete data, missing values will be
74#' automatically removed with a warning. If `na.rm = TRUE` is supplied
75#' to the statistic, the warning will be suppressed.
76#'
77#' @param df data.frame
78#' @param na.rm If true, will suppress warning message.
79#' @param vars Character vector of variables to check for missings in
80#' @param name Optional function name to improve error message.
81#' @param finite If `TRUE`, will also remove non-finite values.
82#' @keywords internal
83#' @export
84remove_missing <- function(df, na.rm = FALSE, vars = names(df), name = "",
85                           finite = FALSE) {
86  if (!is.logical(na.rm)) {
87    abort("`na.rm` must be logical")
88  }
89
90  missing <- detect_missing(df, vars, finite)
91
92  if (any(missing)) {
93    df <- df[!missing, ]
94    if (!na.rm) {
95      if (name != "") name <- paste(" (", name, ")", sep = "")
96      str <- if (finite) "non-finite" else "missing"
97      warning_wrap(
98        "Removed ", sum(missing), " rows containing ", str, " values", name, "."
99      )
100    }
101  }
102
103  df
104}
105detect_missing <- function(df, vars, finite = FALSE) {
106  vars <- intersect(vars, names(df))
107  !cases(df[, vars, drop = FALSE], if (finite) is_finite else is_complete)
108}
109
110# Returns a logical vector of same length as nrow(x). If all data on a row
111# is finite (not NA, NaN, Inf, or -Inf) return TRUE; otherwise FALSE.
112cases <- function(x, fun) {
113  ok <- vapply(x, fun, logical(nrow(x)))
114
115  # Need a special case test when x has exactly one row, because rowSums
116  # doesn't respect dimensions for 1x1 matrices. vapply returns a vector (not
117  # a matrix when the input has one row.
118  if (is.vector(ok)) {
119    all(ok)
120  } else {
121    # Find all the rows where all are TRUE
122    rowSums(as.matrix(ok)) == ncol(x)
123  }
124}
125
126# Wrapper around is.finite to handle list cols
127is_finite <- function(x) {
128  if (typeof(x) == "list") {
129    !vapply(x, is.null, logical(1))
130  } else {
131    is.finite(x)
132  }
133}
134
135is_complete <- function(x) {
136  if (typeof(x) == "list") {
137    !vapply(x, is.null, logical(1))
138  } else {
139    !is.na(x)
140  }
141}
142
143
144#' Used in examples to illustrate when errors should occur.
145#'
146#' @param expr code to evaluate.
147#' @export
148#' @keywords internal
149#' @examples
150#' should_stop(stop("Hi!"))
151#' should_stop(should_stop("Hi!"))
152should_stop <- function(expr) {
153  res <- try(print(force(expr)), TRUE)
154  if (!inherits(res, "try-error")) {
155    abort("No error!")
156  }
157  invisible()
158}
159
160
161#' A waiver object.
162#'
163#' A waiver is a "flag" object, similar to `NULL`, that indicates the
164#' calling function should just use the default value.  It is used in certain
165#' functions to distinguish between displaying nothing (`NULL`) and
166#' displaying a default value calculated elsewhere (`waiver()`)
167#'
168#' @export
169#' @keywords internal
170waiver <- function() structure(list(), class = "waiver")
171
172is.waive <- function(x) inherits(x, "waiver")
173
174
175rescale01 <- function(x) {
176  rng <- range(x, na.rm = TRUE)
177  (x - rng[1]) / (rng[2] - rng[1])
178}
179
180binned_pal <- function(palette) {
181  function(x) {
182    palette(length(x))
183  }
184}
185
186#' Give a deprecation error, warning, or message, depending on version number.
187#'
188#' This function is deprecated.
189#'
190#' @param version The last version of ggplot2 where this function was good
191#'   (in other words, the last version where it was not deprecated).
192#' @param msg The message to print.
193#' @keywords internal
194#' @export
195gg_dep <- function(version, msg) {
196  .Deprecated()
197  v <- as.package_version(version)
198  cv <- utils::packageVersion("ggplot2")
199  text <- "{msg} (Defunct; last used in version {version})"
200
201  # If current major number is greater than last-good major number, or if
202  #  current minor number is more than 1 greater than last-good minor number,
203  #  give error.
204  if (cv[[1,1]] > v[[1,1]]  ||  cv[[1,2]] > v[[1,2]] + 1) {
205    abort(glue(text))
206
207  # If minor number differs by one, give warning
208  } else if (cv[[1,2]] > v[[1,2]]) {
209    warn(glue(text))
210
211  # If only subminor number is greater, give message
212  } else if (cv[[1,3]] > v[[1,3]]) {
213    message(glue(text))
214  }
215
216  invisible()
217}
218
219has_name <- function(x) {
220  nms <- names(x)
221  if (is.null(nms)) {
222    return(rep(FALSE, length(x)))
223  }
224
225  !is.na(nms) & nms != ""
226}
227
228# Use chartr() for safety since toupper() fails to convert i to I in Turkish locale
229lower_ascii <- "abcdefghijklmnopqrstuvwxyz"
230upper_ascii <- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
231to_lower_ascii <- function(x) chartr(upper_ascii, lower_ascii, x)
232to_upper_ascii <- function(x) chartr(lower_ascii, upper_ascii, x)
233
234tolower <- function(x) {
235  abort("Please use `to_lower_ascii()`, which works fine in all locales.")
236}
237
238toupper <- function(x) {
239  abort("Please use `to_upper_ascii()`, which works fine in all locales.")
240}
241
242# Convert a snake_case string to camelCase
243camelize <- function(x, first = FALSE) {
244  x <- gsub("_(.)", "\\U\\1", x, perl = TRUE)
245  if (first) x <- firstUpper(x)
246  x
247}
248
249snakeize <- function(x) {
250  x <- gsub("([A-Za-z])([A-Z])([a-z])", "\\1_\\2\\3", x)
251  x <- gsub(".", "_", x, fixed = TRUE)
252  x <- gsub("([a-z])([A-Z])", "\\1_\\2", x)
253  to_lower_ascii(x)
254}
255
256firstUpper <- function(s) {
257  paste0(to_upper_ascii(substring(s, 1, 1)), substring(s, 2))
258}
259
260snake_class <- function(x) {
261  snakeize(class(x)[1])
262}
263
264empty <- function(df) {
265  is.null(df) || nrow(df) == 0 || ncol(df) == 0 || is.waive(df)
266}
267
268is.discrete <- function(x) {
269  is.factor(x) || is.character(x) || is.logical(x)
270}
271
272# This function checks that all columns of a dataframe `x` are data and returns
273# the names of any columns that are not.
274# We define "data" as atomic types or lists, not functions or otherwise.
275# The `inherits(x, "Vector")` check is for checking S4 classes from Bioconductor
276# and wether they can be expected to follow behavior typical of vectors. See
277# also #3835
278check_nondata_cols <- function(x) {
279  idx <- (vapply(x, function(x) {
280    is.null(x) || rlang::is_vector(x) || inherits(x, "Vector")
281  }, logical(1)))
282  names(x)[which(!idx)]
283}
284
285compact <- function(x) {
286  null <- vapply(x, is.null, logical(1))
287  x[!null]
288}
289
290is.formula <- function(x) inherits(x, "formula")
291
292deparse2 <- function(x) {
293  y <- deparse(x, backtick = TRUE)
294  if (length(y) == 1) {
295    y
296  } else {
297    paste0(y[[1]], "...")
298  }
299}
300
301message_wrap <- function(...) {
302  msg <- paste(..., collapse = "", sep = "")
303  wrapped <- strwrap(msg, width = getOption("width") - 2)
304  message(paste0(wrapped, collapse = "\n"))
305}
306
307warning_wrap <- function(...) {
308  msg <- paste(..., collapse = "", sep = "")
309  wrapped <- strwrap(msg, width = getOption("width") - 2)
310  warn(glue_collapse(wrapped, "\n", last = "\n"))
311}
312
313var_list <- function(x) {
314  x <- encodeString(x, quote = "`")
315  if (length(x) > 5) {
316    x <- c(x[1:5], paste0("and ", length(x) - 5, " more"))
317  }
318
319  paste0(x, collapse = ", ")
320}
321
322dispatch_args <- function(f, ...) {
323  args <- list(...)
324  formals <- formals(f)
325  formals[names(args)] <- args
326  formals(f) <- formals
327  f
328}
329
330is_missing_arg <- function(x) identical(x, quote(expr = ))
331# Get all arguments in a function as a list. Will fail if an ellipsis argument
332# named .ignore
333# @param ... passed on in case enclosing function uses ellipsis in argument list
334find_args <- function(...) {
335  env <- parent.frame()
336  args <- names(formals(sys.function(sys.parent(1))))
337
338  vals <- mget(args, envir = env)
339  vals <- vals[!vapply(vals, is_missing_arg, logical(1))]
340
341  modify_list(vals, list(..., `...` = NULL))
342}
343
344# Used in annotations to ensure printed even when no
345# global data
346dummy_data <- function() new_data_frame(list(x = NA), n = 1)
347
348with_seed_null <- function(seed, code) {
349  if (is.null(seed)) {
350    code
351  } else {
352    withr::with_seed(seed, code)
353  }
354}
355
356seq_asc <- function(to, from) {
357  if (to > from) {
358    integer()
359  } else {
360    to:from
361  }
362}
363
364# Needed to trigger package loading
365#' @importFrom tibble tibble
366NULL
367
368# Check inputs with tibble but allow column vectors (see #2609 and #2374)
369as_gg_data_frame <- function(x) {
370  x <- lapply(x, validate_column_vec)
371  new_data_frame(x)
372}
373validate_column_vec <- function(x) {
374  if (is_column_vec(x)) {
375    dim(x) <- NULL
376  }
377  x
378}
379is_column_vec <- function(x) {
380  dims <- dim(x)
381  length(dims) == 2L && dims[[2]] == 1L
382}
383
384# Parse takes a vector of n lines and returns m expressions.
385# See https://github.com/tidyverse/ggplot2/issues/2864 for discussion.
386#
387# parse(text = c("alpha", "", "gamma"))
388# #> expression(alpha, gamma)
389#
390# parse_safe(text = c("alpha", "", "gamma"))
391# #> expression(alpha, NA, gamma)
392#
393parse_safe <- function(text) {
394  if (!is.character(text)) {
395    abort("`text` must be a character vector")
396  }
397  out <- vector("expression", length(text))
398  for (i in seq_along(text)) {
399    expr <- parse(text = text[[i]])
400    out[[i]] <- if (length(expr) == 0) NA else expr[[1]]
401  }
402  out
403}
404
405switch_orientation <- function(aesthetics) {
406  # We should have these as globals somewhere
407  x <- ggplot_global$x_aes
408  y <- ggplot_global$y_aes
409  x_aes <- match(aesthetics, x)
410  x_aes_pos <- which(!is.na(x_aes))
411  y_aes <- match(aesthetics, y)
412  y_aes_pos <- which(!is.na(y_aes))
413  if (length(x_aes_pos) > 0) {
414    aesthetics[x_aes_pos] <- y[x_aes[x_aes_pos]]
415  }
416  if (length(y_aes_pos) > 0) {
417    aesthetics[y_aes_pos] <- x[y_aes[y_aes_pos]]
418  }
419  aesthetics
420}
421
422#' Utilities for working with bidirectional layers
423#'
424#' These functions are what underpins the ability of certain geoms to work
425#' automatically in both directions. See the *Extending ggplot2* vignette for
426#' how they are used when implementing `Geom`, `Stat`, and `Position` classes.
427#'
428#' `has_flipped_aes()` is used to sniff out the orientation of the layer from
429#' the data. It has a range of arguments that can be used to finetune the
430#' sniffing based on what the data should look like. `flip_data()` will switch
431#' the column names of the data so that it looks like x-oriented data.
432#' `flipped_names()` provides a named list of aesthetic names that corresponds
433#' to the orientation of the layer.
434#'
435#' @section Controlling the sniffing:
436#' How the layer data should be interpreted depends on its specific features.
437#' `has_flipped_aes()` contains a range of flags for defining what certain
438#' features in the data correspond to:
439#'
440#' - `main_is_orthogonal`: This argument controls how the existence of only a `x`
441#'   or `y` aesthetic is understood. If `TRUE` then the exisiting aesthetic
442#'   would be then secondary axis. This behaviour is present in [stat_ydensity()]
443#'   and [stat_boxplot()]. If `FALSE` then the exisiting aesthetic is the main
444#'   axis as seen in e.g. [stat_bin()], [geom_count()], and [stat_density()].
445#' - `range_is_orthogonal`: This argument controls whether the existance of
446#'   range-like aesthetics (e.g. `xmin` and `xmax`) represents the main or
447#'   secondary axis. If `TRUE` then the range is given for the secondary axis as
448#'   seen in e.g. [geom_ribbon()] and [geom_linerange()].
449#' - `group_has_equal`: This argument controls whether to test for equality of
450#'   all `x` and `y` values inside each group and set the main axis to the one
451#'   where all is equal. This test is only performed if `TRUE`, and only after
452#'   less computationally heavy tests has come up empty handed. Examples are
453#'   [stat_boxplot()] and [stat_ydensity]
454#' - `ambiguous`: This argument tells the function that the layer, while
455#'   bidirectional, doesn't treat each axis differently. It will circumvent any
456#'   data based guessing and only take hint from the `orientation` element in
457#'   `params`. If this is not present it will fall back to `FALSE`. Examples are
458#'   [geom_line()] and [geom_area()]
459#' - `main_is_continuous`: This argument controls how the test for discreteness
460#'   in the scales should be interpreted. If `TRUE` then the main axis will be
461#'   the one which is not discrete-like. Conversely, if `FALSE` the main axis
462#'   will be the discrete-like one. Examples of `TRUE` is [stat_density()] and
463#'   [stat_bin()], while examples of `FALSE` is [stat_ydensity()] and
464#'   [stat_boxplot()]
465#' - `main_is_optional`: This argument controls the rare case of layers were the
466#'   main direction is an optional aesthetic. This is only seen in
467#'   [stat_boxplot()] where `x` is set to `0` if not given. If `TRUE` there will
468#'   be a check for whether all `x` or all `y` are equal to `0`
469#'
470#' @param data The layer data
471#' @param params The parameters of the `Stat`/`Geom`. Only the `orientation`
472#'   parameter will be used.
473#' @param main_is_orthogonal If only `x` or `y` are present do they correspond
474#'   to the main orientation or the reverse. E.g. If `TRUE` and `y` is present
475#'   it is not flipped. If `NA` this check will be ignored.
476#' @param range_is_orthogonal If `xmin`/`xmax` or `ymin`/`ymax` is present do
477#'   they correspond to the main orientation or reverse. If `NA` this check will
478#'   be ignored.
479#' @param group_has_equal Is it expected that grouped data has either a single
480#'   `x` or `y` value that will correspond to the orientation.
481#' @param ambiguous Is the layer ambiguous in its mapping by nature. If so, it
482#'   will only be flipped if `params$orientation == "y"`
483#' @param main_is_continuous If there is a discrete and continuous axis, does
484#'   the continuous one correspond to the main orientation?
485#' @param main_is_optional Is the main axis aesthetic optional and, if not
486#'   given, set to `0`
487#' @param flip Logical. Is the layer flipped.
488#'
489#' @return `has_flipped_aes()` returns `TRUE` if it detects a layer in the other
490#' orientation and `FALSE` otherwise. `flip_data()` will return the input
491#' unchanged if `flip = FALSE` and the data with flipped aesthetic names if
492#' `flip = TRUE`. `flipped_names()` returns a named list of strings. If
493#' `flip = FALSE` the name of the element will correspond to the element, e.g.
494#' `flipped_names(FALSE)$x == "x"` and if `flip = TRUE` it will correspond to
495#' the flipped name, e.g. `flipped_names(FALSE)$x == "y"`
496#'
497#' @export
498#' @keywords internal
499#' @name bidirection
500#'
501has_flipped_aes <- function(data, params = list(), main_is_orthogonal = NA,
502                            range_is_orthogonal = NA, group_has_equal = FALSE,
503                            ambiguous = FALSE, main_is_continuous = FALSE,
504                            main_is_optional = FALSE) {
505  # Is orientation already encoded in data?
506  if (!is.null(data$flipped_aes)) {
507    not_na <- which(!is.na(data$flipped_aes))
508    if (length(not_na) != 0) {
509      return(data$flipped_aes[[not_na[1L]]])
510    }
511  }
512
513  # Is orientation requested in the params
514  if (!is.null(params$orientation) && !is.na(params$orientation)) {
515    return(params$orientation == "y")
516  }
517
518  x <- data$x %||% params$x
519  y <- data$y %||% params$y
520  xmin <- data$xmin %||% params$xmin
521  ymin <- data$ymin %||% params$ymin
522  xmax <- data$xmax %||% params$xmax
523  ymax <- data$ymax %||% params$ymax
524
525  # Does a single x or y aesthetic corespond to a specific orientation
526  if (!is.na(main_is_orthogonal) && xor(is.null(x), is.null(y))) {
527    return(is.null(y) == main_is_orthogonal)
528  }
529
530  has_x <- !is.null(x)
531  has_y <- !is.null(y)
532
533  # Does a provided range indicate an orientation
534  if (!is.na(range_is_orthogonal)) {
535    if (!is.null(ymin) || !is.null(ymax)) {
536      return(!range_is_orthogonal)
537    }
538    if (!is.null(xmin) || !is.null(xmax)) {
539      return(range_is_orthogonal)
540    }
541  }
542
543  # If ambiguous orientation = NA will give FALSE
544  if (ambiguous && (is.null(params$orientation) || is.na(params$orientation))) {
545    return(FALSE)
546  }
547
548  # Is there a single actual discrete position
549  y_is_discrete <- is_mapped_discrete(y)
550  x_is_discrete <- is_mapped_discrete(x)
551  if (xor(y_is_discrete, x_is_discrete)) {
552    return(y_is_discrete != main_is_continuous)
553  }
554
555  # Does each group have a single x or y value
556  if (group_has_equal) {
557    if (has_x) {
558      if (length(x) == 1) return(FALSE)
559      x_groups <- vapply(split(data$x, data$group), function(x) length(unique(x)), integer(1))
560      if (all(x_groups == 1)) {
561        return(FALSE)
562      }
563    }
564    if (has_y) {
565      if (length(y) == 1) return(TRUE)
566      y_groups <- vapply(split(data$y, data$group), function(x) length(unique(x)), integer(1))
567      if (all(y_groups == 1)) {
568        return(TRUE)
569      }
570    }
571  }
572
573  # default to no
574  FALSE
575}
576#' @rdname bidirection
577#' @export
578flip_data <- function(data, flip = NULL) {
579  flip <- flip %||% any(data$flipped_aes) %||% FALSE
580  if (isTRUE(flip)) {
581    names(data) <- switch_orientation(names(data))
582  }
583  data
584}
585#' @rdname bidirection
586#' @export
587flipped_names <- function(flip = FALSE) {
588  x_aes <- ggplot_global$x_aes
589  y_aes <- ggplot_global$y_aes
590  if (flip) {
591    ret <- as.list(c(y_aes, x_aes))
592  } else {
593    ret <- as.list(c(x_aes, y_aes))
594  }
595  names(ret) <- c(x_aes, y_aes)
596  ret
597}
598
599split_with_index <- function(x, f, n = max(f)) {
600  if (n == 1) return(list(x))
601  f <- as.integer(f)
602  attributes(f) <- list(levels = as.character(seq_len(n)), class = "factor")
603  unname(split(x, f))
604}
605