Skip to content

Commit 0e465b0

Browse files
committed
Merge branch 'main' into flexible_palettes
2 parents dd1247b + 97edd62 commit 0e465b0

17 files changed

+140
-25
lines changed

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
* The default colour and fill scales have a new `palette` argument
44
(@teunbrand, #6064).
5+
* `scale_{x/y}_discrete(continuous.limits)` is a new argument to control the
6+
display range of discrete scales (@teunbrand, #4174, #6259).
7+
* `geom_ribbon()` now appropriately warns about, and removes, missing values
8+
(@teunbrand, #6243).
59
* `guide_*()` can now accept two inside legend theme elements:
610
`legend.position.inside` and `legend.justification.inside`, allowing inside
711
legends to be placed at different positions. Only inside legends with the same

R/coord-radial.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#' @param end Position from 12 o'clock in radians where plot ends, to allow
55
#' for partial polar coordinates. The default, `NULL`, is set to
66
#' `start + 2 * pi`.
7-
#' @param expand If `TRUE`, the default, adds a small expansion factor the
7+
#' @param expand If `TRUE`, the default, adds a small expansion factor to
88
#' the limits to prevent overlap between data and axes. If `FALSE`, limits
99
#' are taken directly from the scale.
1010
#' @param r.axis.inside One of the following:

R/geom-point.R

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@
8585
#' ggplot(mtcars, aes(wt, mpg)) +
8686
#' geom_point(shape = 21, colour = "black", fill = "white", size = 5, stroke = 5)
8787
#'
88+
#' # The default shape in legends is not filled, but you can override the shape
89+
#' # in the guide to reflect the fill in the legend
90+
#' ggplot(mtcars, aes(wt, mpg, fill = factor(carb), shape = factor(cyl))) +
91+
#' geom_point(size = 5, stroke = 1) +
92+
#' scale_shape_manual(values = 21:25) +
93+
#' scale_fill_ordinal(guide = guide_legend(override.aes = list(shape = 21)))
94+
#'
8895
#' \donttest{
8996
#' # You can create interesting shapes by layering multiple points of
9097
#' # different sizes

R/geom-ribbon.R

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,31 @@ GeomRibbon <- ggproto("GeomRibbon", Geom,
126126

127127
draw_key = draw_key_polygon,
128128

129-
handle_na = function(data, params) {
129+
handle_na = function(self, data, params) {
130+
131+
vars <- vapply(
132+
strsplit(self$required_aes, "|", fixed = TRUE),
133+
`[[`, i = 1, character(1)
134+
)
135+
if (params$flipped_aes || any(data$flipped_aes) %||% FALSE) {
136+
vars <- switch_orientation(vars)
137+
}
138+
vars <- c(vars, self$non_missing_aes)
139+
140+
missing <- detect_missing(data, vars, finite = FALSE)
141+
if (!any(missing)) {
142+
return(data)
143+
}
144+
# We're rearranging groups to account for missing values
145+
data$group <- vec_identify_runs(data_frame0(missing, data$group))
146+
data <- vec_slice(data, !missing)
147+
148+
if (!params$na.rm) {
149+
cli::cli_warn(
150+
"Removed {sum(missing)} row{?s} containing missing values or values \\
151+
outside the scale range ({.fn {snake_class(self)}})."
152+
)
153+
}
130154
data
131155
},
132156

@@ -135,7 +159,6 @@ GeomRibbon <- ggproto("GeomRibbon", Geom,
135159
flipped_aes = FALSE, outline.type = "both") {
136160
data <- check_linewidth(data, snake_class(self))
137161
data <- flip_data(data, flipped_aes)
138-
if (na.rm) data <- data[stats::complete.cases(data[c("x", "ymin", "ymax")]), ]
139162
data <- data[order(data$group), ]
140163

141164
# Check that aesthetics are constant

R/guides-.R

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,14 @@ NULL
6868
#' }
6969
guides <- function(...) {
7070
args <- list2(...)
71-
if (length(args) > 0) {
72-
if (is.list(args[[1]]) && !is.guide(args[[1]])) args <- args[[1]]
73-
args <- rename_aes(args)
71+
# If there are no guides do nothing
72+
if (length(args) == 0) {
73+
return(NULL)
7474
}
7575

76+
if (is.list(args[[1]]) && !inherits(args[[1]], "guide")) args <- args[[1]]
77+
args <- rename_aes(args)
78+
7679
idx_false <- vapply(args, isFALSE, FUN.VALUE = logical(1L))
7780
if (isTRUE(any(idx_false))) {
7881
deprecate_warn0("3.3.4", "guides(`<scale>` = 'cannot be `FALSE`. Use \"none\" instead')")
@@ -84,11 +87,6 @@ guides <- function(...) {
8487
return(guides_list(guides = args))
8588
}
8689

87-
# If there are no guides, do nothing
88-
if (length(args) == 0) {
89-
return(NULL)
90-
}
91-
9290
# Raise warning about unnamed guides
9391
nms <- names(args)
9492
if (is.null(nms)) {

R/limits.R

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
#' scales. By default, any values outside the limits specified are replaced with
55
#' `NA`. Be warned that this will remove data outside the limits and this can
66
#' produce unintended results. For changing x or y axis limits \strong{without}
7-
#' dropping data observations, see [coord_cartesian()].
7+
#' dropping data observations, see
8+
#' [`coord_cartesian(xlim, ylim)`][coord_cartesian], or use a full scale with
9+
#' [`oob = scales::oob_keep`][scales::oob_keep].
810
#'
911
#' @param ... For `xlim()` and `ylim()`: Two numeric values, specifying the left/lower
1012
#' limit and the right/upper limit of the scale. If the larger value is given first,

R/scale-discrete-.R

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
#' argument (the number of levels in the scale) returns the numerical values
1717
#' that they should take.
1818
#' @param sec.axis [dup_axis()] is used to specify a secondary axis.
19+
#' @param continuous.limits One of:
20+
#' * `NULL` to use the default scale range
21+
#' * A numeric vector of length two providing a display range for the scale.
22+
#' Use `NA` to refer to the existing minimum or maximum.
23+
#' * A function that accepts the limits and returns a numeric vector of
24+
#' length two.
1925
#' @rdname scale_discrete
2026
#' @family position scales
2127
#' @seealso
@@ -69,7 +75,8 @@
6975
#' }
7076
scale_x_discrete <- function(name = waiver(), ..., palette = seq_len,
7177
expand = waiver(), guide = waiver(),
72-
position = "bottom", sec.axis = waiver()) {
78+
position = "bottom", sec.axis = waiver(),
79+
continuous.limits = NULL) {
7380
sc <- discrete_scale(
7481
aesthetics = ggplot_global$x_aes, name = name,
7582
palette = palette, ...,
@@ -78,13 +85,15 @@ scale_x_discrete <- function(name = waiver(), ..., palette = seq_len,
7885
)
7986

8087
sc$range_c <- ContinuousRange$new()
88+
sc$continuous_limits <- continuous.limits
8189
set_sec_axis(sec.axis, sc)
8290
}
8391
#' @rdname scale_discrete
8492
#' @export
8593
scale_y_discrete <- function(name = waiver(), ..., palette = seq_len,
8694
expand = waiver(), guide = waiver(),
87-
position = "left", sec.axis = waiver()) {
95+
position = "left", sec.axis = waiver(),
96+
continuous.limits = NULL) {
8897
sc <- discrete_scale(
8998
aesthetics = ggplot_global$y_aes, name = name,
9099
palette = palette, ...,
@@ -93,6 +102,7 @@ scale_y_discrete <- function(name = waiver(), ..., palette = seq_len,
93102
)
94103

95104
sc$range_c <- ContinuousRange$new()
105+
sc$continuous_limits <- continuous.limits
96106
set_sec_axis(sec.axis, sc)
97107
}
98108

@@ -106,6 +116,8 @@ scale_y_discrete <- function(name = waiver(), ..., palette = seq_len,
106116
#' @usage NULL
107117
#' @export
108118
ScaleDiscretePosition <- ggproto("ScaleDiscretePosition", ScaleDiscrete,
119+
continuous_limits = NULL,
120+
109121
train = function(self, x) {
110122
if (is.discrete(x)) {
111123
self$range$train(x, drop = self$drop, na.rm = !self$na.translate)

R/scale-expansion.R

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,7 @@ expand_range4 <- function(limits, expand) {
8181

8282
# Calculate separate range expansion for the lower and
8383
# upper range limits, and then combine them into one vector
84-
lower <- expand_range(limits, expand[1], expand[2])[1]
85-
upper <- expand_range(limits, expand[3], expand[4])[2]
86-
c(lower, upper)
84+
expand_range(limits, expand[c(1, 3)], expand[c(2, 4)])
8785
}
8886

8987
#' Calculate the default expansion for a scale
@@ -153,7 +151,8 @@ expand_limits_scale <- function(scale, expand = expansion(0, 0), limits = waiver
153151
scale$map(limits),
154152
expand,
155153
coord_limits,
156-
range_continuous = scale$range_c$range
154+
range_continuous = scale$range_c$range,
155+
continuous_limits = scale$continuous_limits
157156
)
158157
} else {
159158
# using the inverse transform to resolve the NA value is needed for date/datetime/time
@@ -170,7 +169,20 @@ expand_limits_continuous <- function(limits, expand = expansion(0, 0), coord_lim
170169
}
171170

172171
expand_limits_discrete <- function(limits, expand = expansion(0, 0), coord_limits = c(NA, NA),
173-
range_continuous = NULL) {
172+
range_continuous = NULL, continuous_limits = NULL) {
173+
if (is.function(continuous_limits)) {
174+
continuous_limits <- continuous_limits(limits)
175+
}
176+
if (!is.null(continuous_limits)) {
177+
if (!anyNA(continuous_limits)) {
178+
continuous_limits <- range(continuous_limits)
179+
}
180+
check_numeric(continuous_limits, arg = "continuous.limits")
181+
check_length(continuous_limits, 2L, arg = "continuous.limits")
182+
missing <- is.na(continuous_limits)
183+
limits <- ifelse(missing, range(limits), continuous_limits)
184+
}
185+
174186
limit_info <- expand_limits_discrete_trans(
175187
limits,
176188
expand,

man/coord_polar.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/geom_point.Rd

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)