Skip to content

Commit 351eb41

Browse files
dkahlethomasp85
authored andcommitted
Allow rlang-style lambda expressions in stat_summary functions (#3569)
1 parent 660aad2 commit 351eb41

File tree

6 files changed

+97
-2
lines changed

6 files changed

+97
-2
lines changed

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@
118118
the limits of the scale and ignore the order of any `breaks` provided. Note
119119
that this may change the appearance of plots that previously relied on the
120120
unordered behaviour (#2429, @idno0001).
121+
122+
* `stat_summary()` and related functions now support rlang-style lambda functions
123+
(#3568, @dkahle).
124+
121125

122126
# ggplot2 3.2.1
123127

R/stat-summary-2d.r

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@
3030
#'
3131
#' # Specifying function
3232
#' d + stat_summary_2d(fun = function(x) sum(x^2))
33+
#' d + stat_summary_2d(fun = ~ sum(.x^2))
3334
#' d + stat_summary_2d(fun = var)
3435
#' d + stat_summary_2d(fun = "quantile", fun.args = list(probs = 0.1))
3536
#'
3637
#' if (requireNamespace("hexbin")) {
3738
#' d + stat_summary_hex()
39+
#' d + stat_summary_hex(fun = ~ sum(.x^2))
3840
#' }
3941
stat_summary_2d <- function(mapping = NULL, data = NULL,
4042
geom = "tile", position = "identity",
@@ -98,6 +100,7 @@ StatSummary2d <- ggproto("StatSummary2d", Stat,
98100
xbin <- cut(data$x, xbreaks, include.lowest = TRUE, labels = FALSE)
99101
ybin <- cut(data$y, ybreaks, include.lowest = TRUE, labels = FALSE)
100102

103+
fun <- as_function(fun)
101104
f <- function(x) {
102105
do.call(fun, c(list(quote(x)), fun.args))
103106
}

R/stat-summary-bin.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ make_summary_fun <- function(fun.data, fun, fun.max, fun.min, fun.args) {
9696

9797
if (!is.null(fun.data)) {
9898
# Function that takes complete data frame as input
99-
fun.data <- match.fun(fun.data)
99+
fun.data <- as_function(fun.data)
100100
function(df) {
101101
do.call(fun.data, c(list(quote(df$y)), fun.args))
102102
}
@@ -105,6 +105,7 @@ make_summary_fun <- function(fun.data, fun, fun.max, fun.min, fun.args) {
105105

106106
call_f <- function(fun, x) {
107107
if (is.null(fun)) return(NA_real_)
108+
fun <- as_function(fun)
108109
do.call(fun, c(list(quote(x)), fun.args))
109110
}
110111

@@ -116,7 +117,7 @@ make_summary_fun <- function(fun.data, fun, fun.max, fun.min, fun.args) {
116117
))
117118
}
118119
} else {
119-
message("No summary function supplied, defaulting to `mean_se()")
120+
message("No summary function supplied, defaulting to `mean_se()`")
120121
function(df) {
121122
mean_se(df$y)
122123
}

R/stat-summary-hex.r

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ StatSummaryHex <- ggproto("StatSummaryHex", Stat,
4646
try_require("hexbin", "stat_summary_hex")
4747

4848
binwidth <- binwidth %||% hex_binwidth(bins, scales)
49+
fun <- as_function(fun)
4950
hexBinSummarise(data$x, data$y, data$z, binwidth,
5051
fun = fun, fun.args = fun.args, drop = drop)
5152
}

man/stat_summary_2d.Rd

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

tests/testthat/test-stat-summary.R

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
context("stat_summary")
2+
3+
test_that("stat_summary(_bin) work with lambda expressions", {
4+
# note: stat_summary and stat_summary_bin both use
5+
# make_summary_fun, so this tests both
6+
7+
dat <- data_frame(
8+
x = c(1, 1, 2, 2, 3, 3),
9+
y = c(0, 2, 1, 3, 2, 4)
10+
)
11+
12+
p1 <- ggplot(dat, aes(x, y)) +
13+
stat_summary(fun.data = mean_se)
14+
15+
16+
# test fun.data
17+
p2 <- ggplot(dat, aes(x, y)) +
18+
stat_summary(fun.data = ~ {
19+
mean <- mean(.x)
20+
se <- sqrt(stats::var(.x) / length(.x))
21+
data_frame(y = mean, ymin = mean - se, ymax = mean + se)
22+
})
23+
24+
expect_equal(
25+
layer_data(p1),
26+
layer_data(p2)
27+
)
28+
29+
30+
# fun, fun.min, fun.max
31+
p3 <- ggplot(dat, aes(x, y)) +
32+
stat_summary(
33+
fun = ~ mean(.x),
34+
fun.min = ~ mean(.x) - sqrt(stats::var(.x) / length(.x)),
35+
fun.max = ~ mean(.x) + sqrt(stats::var(.x) / length(.x))
36+
)
37+
38+
expect_equal(
39+
layer_data(p1),
40+
layer_data(p3)
41+
)
42+
43+
})
44+
45+
46+
47+
48+
test_that("stat_summary_(2d|hex) work with lambda expressions", {
49+
50+
dat <- data_frame(
51+
x = c(0, 0, 0, 0, 1, 1, 1, 1),
52+
y = c(0, 0, 1, 1, 0, 0, 1, 1),
53+
z = c(1, 1, 2, 2, 2, 2, 3, 3)
54+
)
55+
56+
57+
# stat_summary_2d
58+
p1 <- ggplot(dat, aes(x, y, z = z)) +
59+
stat_summary_2d(fun = function(x) mean(x))
60+
61+
p2 <- ggplot(dat, aes(x, y, z = z)) +
62+
stat_summary_2d(fun = ~ mean(.x))
63+
64+
expect_equal(
65+
layer_data(p1),
66+
layer_data(p2)
67+
)
68+
69+
70+
71+
# stat_summary_hex
72+
# this plot is a bit funky, but easy to reason through
73+
p1 <- ggplot(dat, aes(x, y, z = z)) +
74+
stat_summary_hex(fun = function(x) mean(x))
75+
76+
p2 <- ggplot(dat, aes(x, y, z = z)) +
77+
stat_summary_hex(fun = ~ mean(.x))
78+
79+
expect_equal(
80+
layer_data(p1),
81+
layer_data(p2)
82+
)
83+
84+
})

0 commit comments

Comments
 (0)