From 76eb9d1a16785073132f00c04a3312fa52b8bcfe Mon Sep 17 00:00:00 2001 From: Pedro Aphalo Date: Tue, 26 Dec 2023 01:27:10 +0200 Subject: [PATCH] Update positions to handle x and y vectors of any length Edited position_nudge_center() and position_nudge_to() to allow vectors of any length passed to x and y. I rewrote position_nudge_keep() adding support for groups and controlling keeping or not the original position. --- R/position-nudge-center.R | 150 ++++++++++++++++------- R/position-nudge-line.R | 3 +- R/position-nudge-to.R | 58 +++++++-- _pkgdown.yml | 1 + inst-not/test-with-ggbreak/test-nudgin.R | 109 ++++++++++++++++ man/position_dodgenudge.Rd | 1 + man/position_jitternudge.Rd | 1 + man/position_nudge_center.Rd | 35 +----- man/position_nudge_keep.Rd | 86 +++++++++++++ man/position_nudge_line.Rd | 4 +- man/position_nudge_to.Rd | 41 ++++++- man/position_stacknudge.Rd | 1 + tests/testthat/Rplots.pdf | Bin 3887 -> 3887 bytes 13 files changed, 403 insertions(+), 87 deletions(-) create mode 100644 inst-not/test-with-ggbreak/test-nudgin.R create mode 100644 man/position_nudge_keep.Rd diff --git a/R/position-nudge-center.R b/R/position-nudge-center.R index 726f31e..0152138 100644 --- a/R/position-nudge-center.R +++ b/R/position-nudge-center.R @@ -10,16 +10,10 @@ #' region, either in opposite directions or radially from a virtual \emph{center #' point}. #' -#' The wrapper \code{position_nudge_keep()} with exactly the same signature and -#' behaviour as \code{\link[ggplot2]{position_nudge}} provides an easier to remember name -#' when the desire is only to have access to both the original and nudged -#' coordinates. -#' #' @family position adjustments #' #' @param x,y Amount of vertical and horizontal distance to move. A numeric -#' vector of length 1, or of the same length as rows there are in \code{data}, -#' with nudge values in data rows order. +#' vector, that is recycled if shorter than the number of rows in \code{data}. #' @param center_x,center_y The coordinates of the virtual origin out from which #' nudging radiates or splits in opposite directions. A numeric vector of #' length 1 or of the same length as rows there are in \code{data}, or a @@ -63,7 +57,7 @@ #' which case \code{x} or \code{y} are adjusted to ensure these segments are of the same #' lengths as those at other angles. #' -#' This position is most useful when labeling points forming a cloud or +#' This position is most useful when labelling points forming a cloud or #' grouped along vertical or horizontal lines or "divides". #' #' @seealso [ggplot2::position_nudge()], [ggrepel::position_nudge_repel()]. @@ -171,30 +165,11 @@ #' geom_point() + #' geom_line() + #' geom_text_s(aes(label = y), -#' position = position_nudge_center(x = -0.9, -#' y = -2.7, -#' center_x = mean, -#' center_y = max)) -#' -#' ggplot(df, aes(x, z)) + -#' geom_point() + -#' geom_line() + -#' geom_text_s(aes(label = y), #' position = position_nudge_center(x = 0.9, #' y = 2.7, #' center_x = mean, #' center_y = max)) #' -#' above_max <- function(x) {1.2 * max(x)} -#' ggplot(df, aes(x, z)) + -#' geom_point() + -#' geom_line() + -#' geom_text_s(aes(label = y), -#' position = position_nudge_center(x = -1.2, -#' y = -3, -#' center_x = mean, -#' center_y = above_max)) -#' #' ggplot(df, aes(x, z, color = group)) + #' geom_point() + #' geom_line(color = "black", linetype = "dotted") + @@ -202,7 +177,7 @@ #' position = position_nudge_center(x = -1.2, #' y = -3, #' center_x = 0, -#' center_y = above_max)) +#' center_y = "above_max")) #' #' ggplot(df, aes(x, z, color = group)) + #' geom_point() + @@ -298,6 +273,8 @@ PositionNudgeCenter <- list(x = self$x, y = self$y, + x.reorder = !is.null(self$x) && length(self$x) > 1 && length(self$x) < nrow(data), + y.reorder = !is.null(self$y) && length(self$y) > 1 && length(self$y) < nrow(data), center_x = self$center_x, center_y = self$center_y, kept.origin = self$kept.origin, @@ -344,14 +321,14 @@ PositionNudgeCenter <- # compute focal center by group if (is.function(params$center_x)) { x_ctr <- params$center_x(as.numeric(data[in.grp, "x"])) - } else if(is.numeric(params$center_x)) { + } else if (is.numeric(params$center_x)) { x_ctr <- params$center_x[1] } else { x_ctr <- -Inf # ensure all observations are to the right } if (is.function(params$center_y)) { y_ctr <- params$center_y(as.numeric(data[in.grp, "y"])) - } else if(is.numeric(params$center_y)) { + } else if (is.numeric(params$center_y)) { y_ctr <- params$center_y[1] } else { y_ctr <- -Inf # ensure all observations are above @@ -399,8 +376,18 @@ PositionNudgeCenter <- warning("Ignoring unrecognized direction \"", params$direction, "\".") } - x_nudge[in.grp] <- params$x - y_nudge[in.grp] <- params$y + # behaviour similar to position_nudge() except for handling of vectors of nudges + # and possibly respecting groups for alternating nudges + if (params$x.reorder && length(params$x) > 1 && length(params$x < sum(in.grp))) { + x_nudge[in.grp] <- rep_len(params$x, sum(in.grp))[order(order(data$x[in.grp]))] + } else { + x_nudge[in.grp] <- rep_len(params$x, sum(in.grp)) + } + if (params$y.reorder && length(params$y) > 1 && length(params$y < sum(in.grp))) { + y_nudge[in.grp] <- rep_len(params$y, sum(in.grp))[order(order(data$y[in.grp]))] + } else { + y_nudge[in.grp] <- rep_len(params$y, sum(in.grp)) + } } } # transform only the dimensions for which new coordinates exist @@ -429,16 +416,95 @@ PositionNudgeCenter <- #' position_nudge_centre <- position_nudge_center -#' @rdname position_nudge_center +#' Nudge points a fixed distance +#' +#' The function \code{position_nudge_keep()} has an additional parameters +#' compared to \code{\link[ggplot2]{position_nudge}}, \code{obey_grouping} and +#' by default the same behaviour when the values passed as arguments to \code{x} +#' and \code{y} have length one. +#' +#' @details When \code{x} or \code{y} have length > 1, they are treated +#' specially. If the lengths is the same as there are rows in data, the nudges +#' are applied in the order of the rows in data. When they are shorter, they +#' are recycled and applied to the data values after ordering. This makes it +#' possible to have alternating mudging right and left or up and down. If +#' \code{obey_grouping = TRUE} is passed in the call, the alternation will +#' take place within groups. +#' +#' As other position functions from package 'ggpp', \code{position_nudge_keep()} +#' by default renames and keeps the original positions of the observations in +#' \code{data} making it possible to draw connecting segments or conencting +#' arrows. +#' +#' @family position adjustments +#' +#' @param x,y Amount of vertical and horizontal distance to move. A numeric +#' vector of length 1, or of the same length as rows there are in \code{data}, +#' with nudge values in data rows order. +#' @param obey_grouping A logical flag indicating whether to obey or not +#' groupings of the observations. By default, grouping is obeyed when both of +#' the variables mapped to \emph{x} and \emph{y} are continuous numeric and +#' ignored otherwise. +#' @param kept.origin One of \code{"original"} or \code{"none"}. +#' +#' @note Irrespective of the action, the ordering of rows in \code{data} is +#' preserved. +#' +#' @return A \code{"Position"} object. #' #' @export #' -position_nudge_keep <- function(x = 0, y = 0) { - position_nudge_center(x = x, - y = y, - center_x = NULL, - center_y = NULL, - direction = NULL, - obey_grouping = NULL, - kept.origin = "original") -} +#' @examples +#' df <- data.frame( +#' x = c(1,3,2,5,4,2.5), +#' y = c("abc","cd","d","c","bcd","a") +#' ) +#' +#' # Plain nudging, same as with ggplot2::position_nudge() +#' +#' ggplot(df, aes(x, y, label = y)) + +#' geom_point() + +#' geom_text_s(hjust = "left", vjust = "bottom", +#' position = position_nudge_keep(x = 0.2, y = 0.2)) +#' +#' # alternating nudging +#' ggplot(df, aes(x, y, label = y)) + +#' geom_point() + +#' geom_text_s(position = position_nudge_keep(x = c(0.2, -0.2))) +#' +#' # direct nudging +#' ggplot(df, aes(x, y, label = y)) + +#' geom_point() + +#' geom_text_s(position = position_nudge_keep(x = rep_len(c(0.2, -0.2), 6))) +#' +position_nudge_keep <- + function(x = 0, + y = 0, + obey_grouping = NULL, + kept.origin = c("original", "none")) { + + kept.origin <- rlang::arg_match(kept.origin) + + if (is.null(obey_grouping)) { + # default needs to be set in panel_function when we have access to data + obey_grouping <- NA + } + + if (lubridate::is.duration(x)) { + x <- as.numeric(x) + } + if (lubridate::is.duration(y)) { + y <- as.numeric(y) + } + + # we reuse the code from the position defined above + ggplot2::ggproto(NULL, PositionNudgeCenter, + x = x, + y = y, + center_x = NULL, + center_y = NULL, + kept.origin = kept.origin, + direction = "none", + obey_grouping = obey_grouping + ) + } diff --git a/R/position-nudge-line.R b/R/position-nudge-line.R index 8dab36a..1e90607 100644 --- a/R/position-nudge-line.R +++ b/R/position-nudge-line.R @@ -15,8 +15,7 @@ #' @family position adjustments #' #' @param x,y Amount of vertical and horizontal distance to move. A numeric -#' vector of length 1, or of the same length as rows there are in \code{data}, -#' with nudge values in data rows order. +#' vector of length 1 or longer. #' @param xy_relative Nudge relative to \emph{x} and \emph{y} data expanse, ignored unless #' \code{x} and \code{y} are both \code{NA}s. #' @param abline a vector of length two giving the intercept and slope. diff --git a/R/position-nudge-to.R b/R/position-nudge-to.R index c6d4074..d71b511 100644 --- a/R/position-nudge-to.R +++ b/R/position-nudge-to.R @@ -13,8 +13,7 @@ #' #' @param x,y Coordinates of the destination position. A vector of mode #' \code{numeric}, that is extended if needed, to the same length as rows -#' there are in \code{data}. The values are applied in the order of the -#' observations in data. The default, \code{NULL}, leaves the original +#' there are in \code{data}. The default, \code{NULL}, leaves the original #' coordinates unchanged. #' @param x.action,y.action character string, one of \code{"none"}, or #' \code{"spread"}. With \code{"spread"} evenly distributing the positions @@ -22,11 +21,17 @@ #' range the variable mapped to \emph{x} or \code{y}, otherwise. #' @param kept.origin One of \code{"original"} or \code{"none"}. #' -#' @details The nudged \code{x} or \code{y} replace the original ones in +#' @details The nudged to \code{x} and/or \code{y} values replace the original ones in #' \code{data}, while the original coordinates are returned in \code{x_orig} #' and \code{y_orig}. Values supported are those of \emph{mode} numeric, #' thus including dates and times. #' +#' If the length of \code{x} and/or \code{y} is more than one but less than +#' rows are present in the data, the vector is both recycled and reordered so +#' that the nudges are applied sequentially based on the data values. If their +#' length matches the number of rows in data, they are assumed to be already +#' in data order. +#' #' @note Irrespective of the action, the ordering of rows in \code{data} is #' preserved. #' @@ -44,25 +49,54 @@ #' label = c("abc","cd","d","c","bcd","a") #' ) #' +#' # default does nothing +#' ggplot(df, aes(x, y, label = label)) + +#' geom_point() + +#' geom_text(position = position_nudge_to()) +#' +#' # a single y (or x) value nudges all observations to this data value #' ggplot(df, aes(x, y, label = label)) + #' geom_point() + #' geom_text(position = position_nudge_to(y = 3)) #' +#' # with a suitable geom, segments or arrows can be added #' ggplot(df, aes(x, y, label = label)) + #' geom_point() + #' geom_text_s(position = position_nudge_to(y = 3)) #' +#' # alternating in y value order because y has fewer values than rows in data +#' ggplot(df, aes(x, y, label = label)) + +#' geom_point() + +#' geom_text_s(position = position_nudge_to(y = c(3, 0))) +#' +#' ggplot(df, aes(x, y, label = label)) + +#' geom_point() + +#' geom_text_s(position = position_nudge_to(y = c(0, 3))) +#' +#' # in data row order because y has as many values as rows in data +#' ggplot(df, aes(x, y, label = label)) + +#' geom_point() + +#' geom_text_s(position = position_nudge_to(y = rep_len(c(0, 3), 6))) +#' +#' # spread the values at equal distance within the available space #' ggplot(df, aes(x, y, label = label)) + #' geom_point() + #' geom_text_s(position = #' position_nudge_to(y = 3, x.action = "spread")) #' +#' # spread the values at equal distance within the range given by x #' ggplot(df, aes(x, y, label = label)) + #' geom_point() + #' geom_text_s(position = #' position_nudge_to(y = 3, x = c(2,4), x.action = "spread"), #' hjust = "center") #' +#' ggplot(df, aes(x, y, label = label)) + +#' geom_point() + +#' geom_text_s(position = +#' position_nudge_to(y = 3, x = c(0,6), x.action = "spread"), +#' hjust = "center") +#' position_nudge_to <- function(x = NULL, y = NULL, @@ -110,6 +144,8 @@ PositionNudgeTo <- y = self$y, x.action = self$x.action, y.action = self$y.action, + x.reorder = !is.null(self$x) && length(self$x) > 1 && length(self$x) < nrow(data), + y.reorder = !is.null(self$y) && length(self$y) > 1 && length(self$y) < nrow(data), kept.origin = self$kept.origin ) }, @@ -123,11 +159,15 @@ PositionNudgeTo <- if (params$x.action == "none") { params$x <- rep_len(0, nrow(data)) } else if (params$x.action == "spread") { - params$x <- range(x_orig) + params$x <- range(x_orig) } } else if (is.numeric(params$x)) { if (params$x.action == "none") { - params$x <- rep_len(params$x, nrow(data)) - x_orig + if (params$x.reorder) { + params$x <- rep_len(params$x, nrow(data))[match(1:nrow(data), order(data$x))] - x_orig + } else { + params$x <- rep_len(params$x, nrow(data)) - x_orig + } } else if (params$x.action == "spread") { params$x <- range(params$x) } @@ -148,7 +188,11 @@ PositionNudgeTo <- } } else if (is.numeric(params$y)) { if (params$y.action == "none") { - params$y <- rep_len(params$y, nrow(data)) - y_orig + if (params$y.reorder) { + params$y <- rep_len(params$y, nrow(data))[match(1:nrow(data), order(data$y))] - y_orig + } else { + params$y <- rep_len(params$y, nrow(data)) - y_orig + } } else if (params$y.action == "spread") { params$y <- range(params$y) } @@ -157,7 +201,7 @@ PositionNudgeTo <- # evenly spaced sequence ordered as in data params$y <- seq(from = params$y[1], to = params$y[2], - length.out = nrow(data))[order(order(data$y))] - y_orig + length.out = nrow(data))[match(1:nrow(data), order(data$y))] - y_orig } # As in 'ggplot2' we apply the nudge to xmin, xmax, xend, ymin, ymax, and yend. diff --git a/_pkgdown.yml b/_pkgdown.yml index 00a2c51..d1d4758 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -45,6 +45,7 @@ reference: - title: Positions desc: Advanced nudge functions contents: + - position_nudge_keep - position_nudge_to - position_nudge_center - position_nudge_line diff --git a/inst-not/test-with-ggbreak/test-nudgin.R b/inst-not/test-with-ggbreak/test-nudgin.R new file mode 100644 index 0000000..88f81f3 --- /dev/null +++ b/inst-not/test-with-ggbreak/test-nudgin.R @@ -0,0 +1,109 @@ +library(ggpp) +library(ggbreak) + +set.seed(2019-01-19) +d <- data.frame(x = 1:20, + y = c(rnorm(5) + 4, rnorm(5) + 20, rnorm(5) + 5, rnorm(5) + 22) +) + +p0 <- ggplot(d, aes(y, x)) + +# geom_col(orientation="y") + geom_point(orientation="y") + +p1 <- p0 + + geom_text_s(aes(y, x, label=label), + data = function(z) {cbind(z, label = letters[1:nrow(z)])}, + nudge_x = 0.5, + box.padding = 0.3, + colour = 'firebrick') + +p2 <- p1 + scale_x_break(c(7, 17)) + + geom_text_s(aes(y, x, label=label), + data = function(z) {cbind(z, label = letters[1:nrow(z)])}, + nudge_x = 0.5, + box.padding = 0.5, # increased value + colour = 'firebrick') + + xlab(NULL) + ylab(NULL) + theme_minimal() + +p1 + p2 + +p3 <- p1 + scale_x_break(c(5, 6)) + + geom_text_s(aes(y, x, label=label), + data = function(z) {cbind(z, label = letters[1:nrow(z)])}, + nudge_x = 0.5, + box.padding = 0.3, + colour = 'firebrick') + + xlab(NULL) + ylab(NULL) + theme_minimal() + +p1 + p3 + +# with zoom in/zoom out the displacement by nudge remains fixed in data units +p4 <- p1 + scale_x_break(c(7, 17), scales = 1/3) + + geom_text_s(aes(y, x, label=label), + data = function(z) {cbind(z, label = letters[1:nrow(z)])}, + nudge_x = 0.5, + box.padding = 0.3, + colour = 'firebrick') + + xlab(NULL) + ylab(NULL) + theme_minimal() + +p1 + p4 + +# with zoom in/zoom out the displacement by nudge remains fixed in data units +# a vector of nudges CANNOT be used (yet) +p5 <- p1 + scale_x_break(c(7, 17), scales = 1/3) + + geom_text_s(aes(y, x, label=label), + data = function(z) {cbind(z, label = letters[1:nrow(z)])}, + nudge_x = ifelse(d$y < 7, 0.5/3, 0.5), + box.padding = 0.3, + colour = 'firebrick') + + xlab(NULL) + ylab(NULL) + theme_minimal() + + +p1 + p5 + +# with zoom in/zoom out the displacement by nudge remains fixed in data units +# a vector of nudges CANNOT be used with 'ggplot2'! +p6 <- p1 + + geom_text(aes(y, x, label=label), + data = function(z) {cbind(z, label = letters[1:nrow(z)])}, + nudge_x = ifelse(d$y < 7, 0.5/3, 0.5), + colour = 'firebrick') + + xlab(NULL) + ylab(NULL) + theme_minimal() + +p1 + p6 + +# with zoom in/zoom out the displacement by nudge remains fixed in data units +# a vector of nudges CANNOT be used with position_nudge() from 'ggplot2'! +p7 <- p1 + scale_x_break(c(7, 17), scales = 1/3) + + geom_text_s(aes(y, x, label=label), + data = function(z) {cbind(z, label = letters[1:nrow(z)])}, + position = position_nudge(x = ifelse(d$y < 7, 0.5/3, 0.5)), + box.padding = 0.3, + colour = 'firebrick') + + xlab(NULL) + ylab(NULL) + theme_minimal() + +p1 + p7 + +# with zoom in/zoom out the displacement by nudge remains fixed in data units +# a vector of nudges CANNOT be used with position_nudge_to() (yet)! +p8 <- p1 + scale_x_break(c(7, 17), scales = 1/3) + + geom_text_s(aes(y, x, label=label), + data = function(z) {cbind(z, label = letters[1:nrow(z)])}, + position = position_nudge_to(x = d$y + ifelse(d$y < 7, 0.5/3, 0.5)), + box.padding = 0.3, + colour = 'firebrick') + + xlab(NULL) + ylab(NULL) + theme_minimal() + +p1 + p8 + +# a vector of nudges CANNOT be used with position_nudge_to() (this is a bug in 'ggpp')! +p9 <- p1 + + geom_text_s(aes(y, x, label=label), + data = function(z) {cbind(z, label = letters[1:nrow(z)])}, + position = position_nudge_to(x = d$y + ifelse(d$y < 7, 0.5/3, 0.5)), + box.padding = 0.3, + colour = 'firebrick') + + xlab(NULL) + ylab(NULL) + theme_minimal() + +p1 + p9 + diff --git a/man/position_dodgenudge.Rd b/man/position_dodgenudge.Rd index 8d29b5f..2ae9dc7 100644 --- a/man/position_dodgenudge.Rd +++ b/man/position_dodgenudge.Rd @@ -134,6 +134,7 @@ ggplot(data = df, aes(x2, x1, group = grp)) + Other position adjustments: \code{\link{position_jitternudge}()}, \code{\link{position_nudge_center}()}, +\code{\link{position_nudge_keep}()}, \code{\link{position_nudge_line}()}, \code{\link{position_nudge_to}()}, \code{\link{position_stacknudge}()} diff --git a/man/position_jitternudge.Rd b/man/position_jitternudge.Rd index 5211904..676b53c 100644 --- a/man/position_jitternudge.Rd +++ b/man/position_jitternudge.Rd @@ -164,6 +164,7 @@ ggplot(mpg[1:20, ], Other position adjustments: \code{\link{position_dodgenudge}()}, \code{\link{position_nudge_center}()}, +\code{\link{position_nudge_keep}()}, \code{\link{position_nudge_line}()}, \code{\link{position_nudge_to}()}, \code{\link{position_stacknudge}()} diff --git a/man/position_nudge_center.Rd b/man/position_nudge_center.Rd index 588b403..c3032a3 100644 --- a/man/position_nudge_center.Rd +++ b/man/position_nudge_center.Rd @@ -3,7 +3,6 @@ \name{position_nudge_center} \alias{position_nudge_center} \alias{position_nudge_centre} -\alias{position_nudge_keep} \title{Nudge labels away from a central point} \usage{ position_nudge_center( @@ -25,13 +24,10 @@ position_nudge_centre( obey_grouping = NULL, kept.origin = c("original", "none") ) - -position_nudge_keep(x = 0, y = 0) } \arguments{ \item{x, y}{Amount of vertical and horizontal distance to move. A numeric -vector of length 1, or of the same length as rows there are in \code{data}, -with nudge values in data rows order.} +vector, that is recycled if shorter than the number of rows in \code{data}.} \item{center_x, center_y}{The coordinates of the virtual origin out from which nudging radiates or splits in opposite directions. A numeric vector of @@ -66,11 +62,6 @@ but extends it by adding support for nudging that varies across the plotting region, either in opposite directions or radially from a virtual \emph{center point}. -The wrapper \code{position_nudge_keep()} with exactly the same signature and -behaviour as \code{\link[ggplot2]{position_nudge}} provides an easier to remember name -when the desire is only to have access to both the original and nudged -coordinates. - Positive values as arguments to \code{x} and \code{y} are added to the original position along either axis. If no arguments are passed to \code{center_x}, \code{center_y} or \code{direction}, the nudging is @@ -100,7 +91,7 @@ Some situations are handled as special cases. When \code{direction = which case \code{x} or \code{y} are adjusted to ensure these segments are of the same lengths as those at other angles. - This position is most useful when labeling points forming a cloud or + This position is most useful when labelling points forming a cloud or grouped along vertical or horizontal lines or "divides". } \examples{ @@ -198,15 +189,6 @@ df <- data.frame( group = rep(c("a", "b"), rep(c(11, 10))) ) -ggplot(df, aes(x, z)) + - geom_point() + - geom_line() + - geom_text_s(aes(label = y), - position = position_nudge_center(x = -0.9, - y = -2.7, - center_x = mean, - center_y = max)) - ggplot(df, aes(x, z)) + geom_point() + geom_line() + @@ -216,16 +198,6 @@ ggplot(df, aes(x, z)) + center_x = mean, center_y = max)) -above_max <- function(x) {1.2 * max(x)} -ggplot(df, aes(x, z)) + - geom_point() + - geom_line() + - geom_text_s(aes(label = y), - position = position_nudge_center(x = -1.2, - y = -3, - center_x = mean, - center_y = above_max)) - ggplot(df, aes(x, z, color = group)) + geom_point() + geom_line(color = "black", linetype = "dotted") + @@ -233,7 +205,7 @@ ggplot(df, aes(x, z, color = group)) + position = position_nudge_center(x = -1.2, y = -3, center_x = 0, - center_y = above_max)) + center_y = "above_max")) ggplot(df, aes(x, z, color = group)) + geom_point() + @@ -253,6 +225,7 @@ ggplot(df, aes(x, z, color = group)) + Other position adjustments: \code{\link{position_dodgenudge}()}, \code{\link{position_jitternudge}()}, +\code{\link{position_nudge_keep}()}, \code{\link{position_nudge_line}()}, \code{\link{position_nudge_to}()}, \code{\link{position_stacknudge}()} diff --git a/man/position_nudge_keep.Rd b/man/position_nudge_keep.Rd new file mode 100644 index 0000000..66d9d69 --- /dev/null +++ b/man/position_nudge_keep.Rd @@ -0,0 +1,86 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/position-nudge-center.R +\name{position_nudge_keep} +\alias{position_nudge_keep} +\title{Nudge points a fixed distance} +\usage{ +position_nudge_keep( + x = 0, + y = 0, + obey_grouping = NULL, + kept.origin = c("original", "none") +) +} +\arguments{ +\item{x, y}{Amount of vertical and horizontal distance to move. A numeric +vector of length 1, or of the same length as rows there are in \code{data}, +with nudge values in data rows order.} + +\item{obey_grouping}{A logical flag indicating whether to obey or not +groupings of the observations. By default, grouping is obeyed when both of +the variables mapped to \emph{x} and \emph{y} are continuous numeric and +ignored otherwise.} + +\item{kept.origin}{One of \code{"original"} or \code{"none"}.} +} +\value{ +A \code{"Position"} object. +} +\description{ +The function \code{position_nudge_keep()} has an additional parameters +compared to \code{\link[ggplot2]{position_nudge}}, \code{obey_grouping} and +by default the same behaviour when the values passed as arguments to \code{x} +and \code{y} have length one. +} +\details{ +When \code{x} or \code{y} have length > 1, they are treated + specially. If the lengths is the same as there are rows in data, the nudges + are applied in the order of the rows in data. When they are shorter, they + are recycled and applied to the data values after ordering. This makes it + possible to have alternating mudging right and left or up and down. If + \code{obey_grouping = TRUE} is passed in the call, the alternation will + take place within groups. + + As other position functions from package 'ggpp', \code{position_nudge_keep()} + by default renames and keeps the original positions of the observations in + \code{data} making it possible to draw connecting segments or conencting + arrows. +} +\note{ +Irrespective of the action, the ordering of rows in \code{data} is + preserved. +} +\examples{ +df <- data.frame( + x = c(1,3,2,5,4,2.5), + y = c("abc","cd","d","c","bcd","a") +) + +# Plain nudging, same as with ggplot2::position_nudge() + +ggplot(df, aes(x, y, label = y)) + + geom_point() + + geom_text_s(hjust = "left", vjust = "bottom", + position = position_nudge_keep(x = 0.2, y = 0.2)) + +# alternating nudging +ggplot(df, aes(x, y, label = y)) + + geom_point() + + geom_text_s(position = position_nudge_keep(x = c(0.2, -0.2))) + +# direct nudging +ggplot(df, aes(x, y, label = y)) + + geom_point() + + geom_text_s(position = position_nudge_keep(x = rep_len(c(0.2, -0.2), 6))) + +} +\seealso{ +Other position adjustments: +\code{\link{position_dodgenudge}()}, +\code{\link{position_jitternudge}()}, +\code{\link{position_nudge_center}()}, +\code{\link{position_nudge_line}()}, +\code{\link{position_nudge_to}()}, +\code{\link{position_stacknudge}()} +} +\concept{position adjustments} diff --git a/man/position_nudge_line.Rd b/man/position_nudge_line.Rd index c18230d..bad981c 100644 --- a/man/position_nudge_line.Rd +++ b/man/position_nudge_line.Rd @@ -18,8 +18,7 @@ position_nudge_line( } \arguments{ \item{x, y}{Amount of vertical and horizontal distance to move. A numeric -vector of length 1, or of the same length as rows there are in \code{data}, -with nudge values in data rows order.} +vector of length 1.} \item{xy_relative}{Nudge relative to \emph{x} and \emph{y} data expanse, ignored unless \code{x} and \code{y} are both \code{NA}s.} @@ -180,6 +179,7 @@ Other position adjustments: \code{\link{position_dodgenudge}()}, \code{\link{position_jitternudge}()}, \code{\link{position_nudge_center}()}, +\code{\link{position_nudge_keep}()}, \code{\link{position_nudge_to}()}, \code{\link{position_stacknudge}()} } diff --git a/man/position_nudge_to.Rd b/man/position_nudge_to.Rd index 381975c..e808ac6 100644 --- a/man/position_nudge_to.Rd +++ b/man/position_nudge_to.Rd @@ -15,8 +15,7 @@ position_nudge_to( \arguments{ \item{x, y}{Coordinates of the destination position. A vector of mode \code{numeric}, that is extended if needed, to the same length as rows -there are in \code{data}. The values are applied in the order of the -observations in data. The default, \code{NULL}, leaves the original +there are in \code{data}. The default, \code{NULL}, leaves the original coordinates unchanged.} \item{x.action, y.action}{character string, one of \code{"none"}, or @@ -40,10 +39,16 @@ preserves the original position to allow the text to be linked back to its original position with a segment or arrow. } \details{ -The nudged \code{x} or \code{y} replace the original ones in +The nudged to \code{x} and/or \code{y} values replace the original ones in \code{data}, while the original coordinates are returned in \code{x_orig} and \code{y_orig}. Values supported are those of \emph{mode} numeric, thus including dates and times. + + If the length of \code{x} and/or \code{y} is more than one but less than + rows are present in the data, the vector is both recycled and reordered so + that the nudges are applied sequentially based on the data values. If their + length matches the number of rows in data, they are assumed to be already + in data order. } \note{ Irrespective of the action, the ordering of rows in \code{data} is @@ -56,25 +61,54 @@ df <- data.frame( label = c("abc","cd","d","c","bcd","a") ) +# default does nothing +ggplot(df, aes(x, y, label = label)) + + geom_point() + + geom_text(position = position_nudge_to()) + +# a single y (or x) value nudges all observations to this data value ggplot(df, aes(x, y, label = label)) + geom_point() + geom_text(position = position_nudge_to(y = 3)) +# with a suitable geom, segments or arrows can be added ggplot(df, aes(x, y, label = label)) + geom_point() + geom_text_s(position = position_nudge_to(y = 3)) +# alternating in y value order because y has fewer values than rows in data +ggplot(df, aes(x, y, label = label)) + + geom_point() + + geom_text_s(position = position_nudge_to(y = c(3, 0))) + +ggplot(df, aes(x, y, label = label)) + + geom_point() + + geom_text_s(position = position_nudge_to(y = c(0, 3))) + +# in data row order because y has as many values as rows in data +ggplot(df, aes(x, y, label = label)) + + geom_point() + + geom_text_s(position = position_nudge_to(y = rep_len(c(0, 3), 6))) + +# spread the values at equal distance within the available space ggplot(df, aes(x, y, label = label)) + geom_point() + geom_text_s(position = position_nudge_to(y = 3, x.action = "spread")) +# spread the values at equal distance within the range given by x ggplot(df, aes(x, y, label = label)) + geom_point() + geom_text_s(position = position_nudge_to(y = 3, x = c(2,4), x.action = "spread"), hjust = "center") +ggplot(df, aes(x, y, label = label)) + + geom_point() + + geom_text_s(position = + position_nudge_to(y = 3, x = c(0,6), x.action = "spread"), + hjust = "center") + } \seealso{ \code{\link[ggplot2]{position_nudge}}, @@ -84,6 +118,7 @@ Other position adjustments: \code{\link{position_dodgenudge}()}, \code{\link{position_jitternudge}()}, \code{\link{position_nudge_center}()}, +\code{\link{position_nudge_keep}()}, \code{\link{position_nudge_line}()}, \code{\link{position_stacknudge}()} } diff --git a/man/position_stacknudge.Rd b/man/position_stacknudge.Rd index c2c8018..fb65052 100644 --- a/man/position_stacknudge.Rd +++ b/man/position_stacknudge.Rd @@ -180,6 +180,7 @@ Other position adjustments: \code{\link{position_dodgenudge}()}, \code{\link{position_jitternudge}()}, \code{\link{position_nudge_center}()}, +\code{\link{position_nudge_keep}()}, \code{\link{position_nudge_line}()}, \code{\link{position_nudge_to}()} } diff --git a/tests/testthat/Rplots.pdf b/tests/testthat/Rplots.pdf index 88de6a0f0a149398ad71437987fecb949618acad..045254636809486662a4f139742c75c2d1bce0ff 100644 GIT binary patch delta 29 ecmZ24w_a|71-qGnfw8fH*+hF$7;|Iv6+QrX+Xx5% delta 29 ecmZ24w_a|71-q%CrGcr5=|p=`7;|Iv6+QrZ1_&Mi