Skip to content

Commit f0b9693

Browse files
authored
Merge branch 'main' into attr_stability
2 parents 0705333 + a641f5d commit f0b9693

File tree

137 files changed

+2159
-876
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+2159
-876
lines changed

DESCRIPTION

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ Collate:
242242
'scales-.R'
243243
'stat-align.R'
244244
'stat-bin.R'
245+
'stat-summary-2d.R'
245246
'stat-bin2d.R'
246247
'stat-bindot.R'
247248
'stat-binhex.R'
@@ -263,7 +264,6 @@ Collate:
263264
'stat-smooth-methods.R'
264265
'stat-smooth.R'
265266
'stat-sum.R'
266-
'stat-summary-2d.R'
267267
'stat-summary-bin.R'
268268
'stat-summary-hex.R'
269269
'stat-summary.R'

NAMESPACE

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ S3method(c,mapped_discrete)
2020
S3method(drawDetails,zeroGrob)
2121
S3method(element_grob,element_blank)
2222
S3method(element_grob,element_line)
23+
S3method(element_grob,element_point)
24+
S3method(element_grob,element_polygon)
2325
S3method(element_grob,element_rect)
2426
S3method(element_grob,element_text)
2527
S3method(format,ggproto)
@@ -346,6 +348,8 @@ export(element_blank)
346348
export(element_geom)
347349
export(element_grob)
348350
export(element_line)
351+
export(element_point)
352+
export(element_polygon)
349353
export(element_rect)
350354
export(element_render)
351355
export(element_text)

NEWS.md

+43-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,33 @@
22

33
* (internal) layer data can be attenuated with parameter attributes
44
(@teunbrand, #3175).
5+
* Date scales silently coerce <POSIXct> to <Date> and datetime scales silently
6+
coerce <Date> to <POSIXct> (@laurabrianna, #3533)
7+
* New parameters for `geom_label()` (@teunbrand and @steveharoz, #5365):
8+
* The `linewidth` aesthetic is now applied and replaces the `label.size`
9+
argument.
10+
* The `linetype` aesthetic is now applied.
11+
* New `border.colour` argument to set the colour of borders.
12+
* New `text.colour` argument to set the colour of text.
13+
* New `element_point()` and `element_polygon()` that can be given to
14+
`theme(point, polygon)` as an extension point (@teunbrand, #6248).
15+
* Turned off fallback for `size` to `linewidth` translation in
16+
`geom_bar()`/`geom_col()` (#4848).
17+
* `coord_radial()` now displays no axis instead of throwing an error when
18+
a scale has no breaks (@teunbrand, #6271).
19+
* The `fatten` argument has been deprecated in `geom_boxplot()`,
20+
`geom_crossbar()` and `geom_pointrange()` (@teunbrand, #4881).
21+
* Axis labels are now preserved better when using `coord_sf(expand = TRUE)` and
22+
graticule lines are straight but do not meet the edge (@teunbrand, #2985).
23+
* Attempt to boost detail in `coord_polar()` and `coord_radial()` near the
24+
center (@teunbrand, #5023)
25+
* Scale names, guide titles and aesthetic labels can now accept functions
26+
(@teunbrand, #4313)
27+
* Binned scales with zero-width data expand the default limits by 0.1
28+
(@teunbrand, #5066)
29+
* New default `geom_qq_line(geom = "abline")` for better clipping in the
30+
vertical direction. In addition, `slope` and `intercept` are new computed
31+
variables in `stat_qq_line()` (@teunbrand, #6087).
532
* Position adjustments can now have auxiliary aesthetics (@teunbrand).
633
* `position_nudge()` gains `nudge_x` and `nudge_y` aesthetics (#3026, #5445).
734
* `position_dodge()` gains `order` aesthetic (#3022, #3345)
@@ -253,7 +280,7 @@
253280
* The ellipsis argument is now checked in `fortify()`, `get_alt_text()`,
254281
`labs()` and several guides (@teunbrand, #3196).
255282
* `stat_summary_bin()` no longer ignores `width` parameter (@teunbrand, #4647).
256-
* Added `keep.zeroes` argument to `stat_bin()` (@teunbrand, #3449)
283+
* Reintroduced `drop` argument to `stat_bin()` (@teunbrand, #3449)
257284
* (internal) removed barriers for using 2D structures as aesthetics
258285
(@teunbrand, #4189).
259286
* `coord_sf()` no longer errors when dealing with empty graticules (@teunbrand, #6052)
@@ -270,6 +297,21 @@
270297
aesthetics (@teunbrand, #2800).
271298
* Stricter check on `register_theme_elements(element_tree)` (@teunbrand, #6162)
272299
* Added `weight` aesthetic for `stat_ellipse()` (@teunbrand, #5272)
300+
* Fixed a bug where the `guide_custom(order)` wasn't working (@teunbrand, #6195)
301+
* All binning stats now use the `boundary`/`center` parametrisation rather
302+
than `origin`, following in `stat_bin()`'s footsteps (@teunbrand).
303+
* `stat_summary_2d()` and `stat_bin_2d()` now deal with zero-range data
304+
more elegantly (@teunbrand, #6207).
305+
* Munching in `coord_polar()` and `coord_radial()` now adds more detail,
306+
particularly for data-points with a low radius near the center
307+
(@teunbrand, #5023).
308+
* All scales now expose the `aesthetics` parameter (@teunbrand, #5841)
309+
* Staged expressions are handled more gracefully if legends cannot resolve them
310+
(@teunbrand, #6264).
311+
* New `theme(legend.key.justification)` to control the alignment of legend keys
312+
(@teunbrand, #3669).
313+
* Added `scale_{x/y}_time(date_breaks, date_minor_breaks, date_labels)`
314+
(@teunbrand, #4335).
273315

274316
# ggplot2 3.5.1
275317

R/axis-secondary.R

+2-2
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ AxisSecondary <- ggproto("AxisSecondary", NULL,
329329
scale$train(range)
330330
scale
331331
},
332-
make_title = function(title) {
333-
title
332+
make_title = function(...) {
333+
ScaleContinuous$make_title(...)
334334
}
335335
)

R/bin.R

+94-15
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,12 @@ bin_breaks <- function(breaks, closed = c("right", "left")) {
5454

5555
bin_breaks_width <- function(x_range, width = NULL, center = NULL,
5656
boundary = NULL, closed = c("right", "left")) {
57-
check_length(x_range, 2L)
5857

59-
# binwidth seems to be the argument name supplied to width. (stat-bin and stat-bindot)
60-
check_number_decimal(width, min = 0, allow_infinite = FALSE, arg = "binwidth")
61-
62-
if (!is.null(boundary) && !is.null(center)) {
63-
cli::cli_abort("Only one of {.arg boundary} and {.arg center} may be specified.")
64-
} else if (is.null(boundary)) {
58+
if (is.null(boundary)) {
6559
if (is.null(center)) {
6660
# If neither edge nor center given, compute both using tile layer's
6761
# algorithm. This puts min and max of data in outer half of their bins.
6862
boundary <- width / 2
69-
7063
} else {
7164
# If center given but not boundary, compute boundary.
7265
boundary <- center - width / 2
@@ -75,9 +68,6 @@ bin_breaks_width <- function(x_range, width = NULL, center = NULL,
7568

7669
# Find the left side of left-most bin: inputs could be Dates or POSIXct, so
7770
# coerce to numeric first.
78-
x_range <- as.numeric(x_range)
79-
width <- as.numeric(width)
80-
boundary <- as.numeric(boundary)
8171
shift <- floor((x_range[1] - boundary) / width)
8272
origin <- boundary + shift * width
8373

@@ -104,9 +94,7 @@ bin_breaks_width <- function(x_range, width = NULL, center = NULL,
10494

10595
bin_breaks_bins <- function(x_range, bins = 30, center = NULL,
10696
boundary = NULL, closed = c("right", "left")) {
107-
check_length(x_range, 2L)
10897

109-
check_number_whole(bins, min = 1)
11098
if (zero_range(x_range)) {
11199
# 0.1 is the same width as the expansion `default_expansion()` gives for 0-width data
112100
width <- 0.1
@@ -128,6 +116,56 @@ bin_breaks_bins <- function(x_range, bins = 30, center = NULL,
128116

129117
# Compute bins ------------------------------------------------------------
130118

119+
compute_bins <- function(x, scale = NULL, breaks = NULL, binwidth = NULL, bins = NULL,
120+
center = NULL, boundary = NULL,
121+
closed = c("right", "left")) {
122+
123+
range <- if (is.scale(scale)) scale$dimension() else range(x)
124+
check_length(range, 2L)
125+
126+
if (!is.null(breaks)) {
127+
breaks <- allow_lambda(breaks)
128+
if (is.function(breaks)) {
129+
breaks <- breaks(x)
130+
}
131+
if (is.scale(scale) && !scale$is_discrete()) {
132+
breaks <- scale$transform(breaks)
133+
}
134+
check_numeric(breaks)
135+
bins <- bin_breaks(breaks, closed)
136+
return(bins)
137+
}
138+
139+
check_number_decimal(boundary, allow_infinite = FALSE, allow_null = TRUE)
140+
check_number_decimal(center, allow_infinite = FALSE, allow_null = TRUE)
141+
if (!is.null(boundary) && !is.null(center)) {
142+
cli::cli_abort("Only one of {.arg boundary} and {.arg center} may be specified.")
143+
}
144+
145+
if (!is.null(binwidth)) {
146+
binwidth <- allow_lambda(binwidth)
147+
if (is.function(binwidth)) {
148+
binwidth <- binwidth(x)
149+
}
150+
check_number_decimal(binwidth, min = 0, allow_infinite = FALSE)
151+
bins <- bin_breaks_width(
152+
range, binwidth,
153+
center = center, boundary = boundary, closed = closed
154+
)
155+
return(bins)
156+
}
157+
158+
bins <- allow_lambda(bins)
159+
if (is.function(bins)) {
160+
bins <- bins(x)
161+
}
162+
check_number_whole(bins, min = 1, allow_infinite = FALSE)
163+
bin_breaks_bins(
164+
range, bins,
165+
center = center, boundary = boundary, closed = closed
166+
)
167+
}
168+
131169
bin_vector <- function(x, bins, weight = NULL, pad = FALSE) {
132170
check_object(bins, is_bins, "a {.cls ggplot2_bins} object")
133171

@@ -141,8 +179,7 @@ bin_vector <- function(x, bins, weight = NULL, pad = FALSE) {
141179
weight[is.na(weight)] <- 0
142180
}
143181

144-
bin_idx <- cut(x, bins$fuzzy, right = bins$right_closed,
145-
include.lowest = TRUE)
182+
bin_idx <- bin_cut(x, bins)
146183
bin_count <- as.numeric(tapply(weight, bin_idx, sum, na.rm = TRUE))
147184
bin_count[is.na(bin_count)] <- 0
148185

@@ -170,6 +207,10 @@ bin_vector <- function(x, bins, weight = NULL, pad = FALSE) {
170207
bin_out(bin_count, bin_x, bin_widths)
171208
}
172209

210+
bin_cut <- function(x, bins) {
211+
cut(x, bins$fuzzy, right = bins$right_closed, include.lowest = TRUE)
212+
}
213+
173214
bin_out <- function(count = integer(0), x = numeric(0), width = numeric(0),
174215
xmin = x - width / 2, xmax = x + width / 2) {
175216
density <- count / width / sum(abs(count))
@@ -186,3 +227,41 @@ bin_out <- function(count = integer(0), x = numeric(0), width = numeric(0),
186227
.size = length(count)
187228
)
188229
}
230+
231+
bin_loc <- function(x, id) {
232+
left <- x[-length(x)]
233+
right <- x[-1]
234+
235+
list(
236+
left = left[id],
237+
right = right[id],
238+
mid = ((left + right) / 2)[id],
239+
length = diff(x)[id]
240+
)
241+
}
242+
243+
fix_bin_params = function(params, fun, version) {
244+
245+
if (!is.null(params$origin)) {
246+
args <- paste0(fun, c("(origin)", "(boundary)"))
247+
deprecate_warn0(version, args[1], args[2])
248+
params$boudnary <- params$origin
249+
params$origin <- NULL
250+
}
251+
252+
if (!is.null(params$right)) {
253+
args <- paste0(fun, c("(right)", "(closed)"))
254+
deprecate_warn0(version, args[1], args[2])
255+
params$closed <- if (isTRUE(params$right)) "right" else "left"
256+
params$right <- NULL
257+
}
258+
259+
if (is.null(params$breaks %||% params$binwidth %||% params$bins)) {
260+
cli::cli_inform(
261+
"{.fn {fun}} using {.code bins = 30}. Pick better value {.arg binwidth}."
262+
)
263+
params$bins <- 30
264+
}
265+
266+
params
267+
}

R/coord-polar.R

+3-3
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ CoordPolar <- ggproto("CoordPolar", Coord,
8484

8585
is_free = function() TRUE,
8686

87-
distance = function(self, x, y, details) {
87+
distance = function(self, x, y, details, boost = 0.75) {
8888
arc <- self$start + c(0, 2 * pi)
8989
dir <- self$direction
9090
if (self$theta == "x") {
@@ -94,8 +94,8 @@ CoordPolar <- ggproto("CoordPolar", Coord,
9494
r <- rescale(x, from = details$r.range)
9595
theta <- theta_rescale_no_clip(y, details$theta.range, arc, dir)
9696
}
97-
98-
dist_polar(r, theta)
97+
# The ^boost boosts detailed munching when r is small
98+
dist_polar(r^boost, theta)
9999
},
100100

101101
backtransform_range = function(self, panel_params) {

R/coord-radial.R

+27-19
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ CoordRadial <- ggproto("CoordRadial", Coord,
120120

121121
is_free = function() TRUE,
122122

123-
distance = function(self, x, y, details) {
123+
distance = function(self, x, y, details, boost = 0.75) {
124124
arc <- details$arc %||% c(0, 2 * pi)
125125
if (self$theta == "x") {
126126
r <- rescale(y, from = details$r.range, to = self$inner_radius / 0.4)
@@ -129,8 +129,8 @@ CoordRadial <- ggproto("CoordRadial", Coord,
129129
r <- rescale(x, from = details$r.range, to = self$inner_radius / 0.4)
130130
theta <- theta_rescale_no_clip(y, details$theta.range, arc)
131131
}
132-
133-
dist_polar(r, theta)
132+
# The ^boost boosts detailed munching when r is small
133+
dist_polar(r^boost, theta)
134134
},
135135

136136
backtransform_range = function(self, panel_params) {
@@ -250,11 +250,18 @@ CoordRadial <- ggproto("CoordRadial", Coord,
250250
names(gdefs) <- aesthetics
251251

252252
# Train theta guide
253-
for (t in intersect(c("theta", "theta.sec"), aesthetics[!empty])) {
254-
gdefs[[t]] <- guides[[t]]$train(gdefs[[t]], panel_params[[t]])
255-
gdefs[[t]] <- guides[[t]]$transform(gdefs[[t]], self, panel_params)
256-
gdefs[[t]] <- guides[[t]]$get_layer_key(gdefs[[t]], layers)
257-
}
253+
t <- intersect(c("theta", "theta.sec"), aesthetics[!empty])
254+
gdefs[t] <- Map(
255+
function(guide, guide_param, scale) {
256+
guide_param$theme_suffix <- "theta"
257+
guide_param <- guide$train(guide_param, scale)
258+
guide_param <- guide$transform(guide_param, self, panel_params)
259+
guide_param <- guide$get_layer_key(guide_param, layers)
260+
},
261+
guide = guides[t],
262+
guide_param = gdefs[t],
263+
scale = panel_params[t]
264+
)
258265

259266
if (!isFALSE(self$r_axis_inside)) {
260267
# For radial axis, we need to pretend that rotation starts at 0 and
@@ -269,17 +276,18 @@ CoordRadial <- ggproto("CoordRadial", Coord,
269276
temp <- modify_list(panel_params, mod)
270277

271278
# Train radial guide
272-
for (r in intersect(c("r", "r.sec"), aesthetics[!empty])) {
273-
gdefs[[r]] <- guides[[r]]$train(gdefs[[r]], panel_params[[r]])
274-
gdefs[[r]] <- guides[[r]]$transform(gdefs[[r]], self, temp) # Use temp
275-
gdefs[[r]] <- guides[[r]]$get_layer_key(gdefs[[r]], layers)
276-
}
277-
278-
# Set theme suffixes
279-
gdefs$theta$theme_suffix <- "theta"
280-
gdefs$theta.sec$theme_suffix <- "theta"
281-
gdefs$r$theme_suffix <- "r"
282-
gdefs$r.sec$theme_suffix <- "r"
279+
r <- intersect(c("r", "r.sec"), aesthetics[!empty])
280+
gdefs[r] <- Map(
281+
function(guide, guide_param, scale) {
282+
guide_param$theme_suffix <- "r"
283+
guide_param <- guide$train(guide_param, scale)
284+
guide_param <- guide$transform(guide_param, self, temp)
285+
guide_param <- guide$get_layer_key(guide_param, layers)
286+
},
287+
guide = guides[r],
288+
guide_param = gdefs[r],
289+
scale = panel_params[r]
290+
)
283291

284292
panel_params$guides$update_params(gdefs)
285293
panel_params

R/coord-sf.R

+8
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,14 @@ view_scales_from_graticule <- function(graticule, scale, aesthetic,
721721
accept_start <- graticule[[orth_start]] < thres
722722
accept_end <- graticule[[orth_end]] < thres
723723
}
724+
if (!any(accept_start | accept_end)) {
725+
eps <- sqrt(.Machine$double.xmin)
726+
subtract <- switch(position, top = , bottom = 90, 0)
727+
straight <-
728+
abs(graticule$angle_start - subtract) < eps &
729+
abs(graticule$angle_end - subtract) < eps
730+
accept_start <- straight
731+
}
724732

725733
# Parsing the information of the `label_axes` argument:
726734
# should we label the meridians ("E") or parallels ("N")?

R/facet-grid-.R

+3-1
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,9 @@ FacetGrid <- ggproto("FacetGrid", Facet,
309309
params$margins
310310
)
311311
# Apply recycling on original data to fit margins
312-
data <- vec_slice(data, facet_vals$.index)
312+
# We're using base subsetting here because `data` might have a superclass
313+
# that isn't handled well by vctrs::vec_slice
314+
data <- data[facet_vals$.index, , drop = FALSE]
313315
facet_vals$.index <- NULL
314316
}
315317

0 commit comments

Comments
 (0)