Skip to content

Commit d147a92

Browse files
committed
add a proper stat_density_2d_filled(). Closes tidyverse#3846.
1 parent 743bad0 commit d147a92

File tree

5 files changed

+104
-47
lines changed

5 files changed

+104
-47
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,9 @@ export(stat_contour_filled)
594594
export(stat_count)
595595
export(stat_density)
596596
export(stat_density2d)
597+
export(stat_density2d_filled)
597598
export(stat_density_2d)
599+
export(stat_density_2d_filled)
598600
export(stat_ecdf)
599601
export(stat_ellipse)
600602
export(stat_function)

R/geom-density2d.r

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
#' @inheritParams geom_point
1818
#' @inheritParams geom_path
1919
#' @param contour_var Character string identifying the variable to contour
20-
#' by. Can be one of `"density"`, `"ndensity"`, or `"count"`. See [stat_density_2d()]
21-
#' for details.
20+
#' by. Can be one of `"density"`, `"ndensity"`, or `"count"`. See the section
21+
#' on computed variables for details.
2222
#' @export
2323
#' @examples
2424
#' m <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
@@ -86,7 +86,6 @@ geom_density_2d <- function(mapping = NULL, data = NULL,
8686
linejoin = linejoin,
8787
linemitre = linemitre,
8888
contour = TRUE,
89-
contour_type = "lines",
9089
contour_var = contour_var,
9190
na.rm = na.rm,
9291
...
@@ -128,7 +127,6 @@ geom_density_2d_filled <- function(mapping = NULL, data = NULL,
128127
params = list(
129128
na.rm = na.rm,
130129
contour = TRUE,
131-
contour_type = "bands",
132130
contour_var = contour_var,
133131
...
134132
)

R/stat-density-2d.r

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
#' @export
22
#' @rdname geom_density_2d
33
#' @param contour If `TRUE`, contour the results of the 2d density
4-
#' estimation
5-
#' @param contour_type When `contour = TRUE`, specifies whether the output
6-
#' is contour lines (`contour_type = "lines"`) or contour bands
7-
#' (`contour_type = "bands"`). For filled contours, you need to specify
8-
#' bands.
4+
#' estimation.
95
#' @param contour_var Character string identifying the variable to contour
106
#' by. Can be one of `"density"`, `"ndensity"`, or `"count"`. See the section
117
#' on computed variables for details.
12-
#' @param n number of grid points in each direction
8+
#' @param n Number of grid points in each direction.
139
#' @param h Bandwidth (vector of length two). If `NULL`, estimated
1410
#' using [MASS::bandwidth.nrd()].
1511
#' @param adjust A multiplicative bandwidth adjustment to be used if 'h' is
1612
#' 'NULL'. This makes it possible to adjust the bandwidth while still
1713
#' using the a bandwidth estimator. For example, `adjust = 1/2` means
1814
#' use half of the default bandwidth.
1915
#' @section Computed variables:
20-
#' `stat_density_2d()` computes different variables depending on whether
21-
#' contouring is turned on or off. With contouring off (`contour = FALSE`),
22-
#' the following variables are provided:
16+
#' `stat_density_2d()` and `stat_density_2d_filled()` compute different
17+
#' variables depending on whether contouring is turned on or off. With
18+
#' contouring off (`contour = FALSE`), both stats behave the same, and the
19+
#' following variables are provided:
2320
#' \describe{
2421
#' \item{`density`}{The density estimate.}
2522
#' \item{`ndensity`}{Density estimate, scaled to a maximum of 1.}
@@ -29,38 +26,33 @@
2926
#'
3027
#' With contouring on (`contour = TRUE`), either [stat_contour()] or
3128
#' [stat_contour_filled()] (for contour lines or contour bands,
32-
#' respectively) is run after the density estimate is calculated,
29+
#' respectively) is run after the density estimate has been obtained,
3330
#' and the computed variables are determined by these stats.
31+
#' Contours are calculated for one of the three types of density estimates
32+
#' obtained before contouring, `density`, `ndensity`, and `count`. Which
33+
#' of those should be used is determined by the `contour_var` parameter.
3434
stat_density_2d <- function(mapping = NULL, data = NULL,
3535
geom = "density_2d", position = "identity",
3636
...,
3737
contour = TRUE,
38-
contour_type = "lines",
3938
contour_var = "density",
4039
n = 100,
4140
h = NULL,
4241
adjust = c(1, 1),
4342
na.rm = FALSE,
4443
show.legend = NA,
4544
inherit.aes = TRUE) {
46-
if (isTRUE(contour_type == "bands")) {
47-
stat <- StatDensity2dFilled
48-
} else {
49-
stat <- StatDensity2d
50-
}
51-
5245
layer(
5346
data = data,
5447
mapping = mapping,
55-
stat = stat,
48+
stat = StatDensity2d,
5649
geom = geom,
5750
position = position,
5851
show.legend = show.legend,
5952
inherit.aes = inherit.aes,
6053
params = list(
6154
na.rm = na.rm,
6255
contour = contour,
63-
contour_type = contour_type,
6456
contour_var = contour_var,
6557
n = n,
6658
h = h,
@@ -70,11 +62,50 @@ stat_density_2d <- function(mapping = NULL, data = NULL,
7062
)
7163
}
7264

73-
#' @export
7465
#' @rdname geom_density_2d
7566
#' @usage NULL
67+
#' @export
7668
stat_density2d <- stat_density_2d
7769

70+
#' @rdname geom_density_2d
71+
#' @export
72+
stat_density_2d_filled <- function(mapping = NULL, data = NULL,
73+
geom = "density_2d_filled", position = "identity",
74+
...,
75+
contour = TRUE,
76+
contour_var = "density",
77+
n = 100,
78+
h = NULL,
79+
adjust = c(1, 1),
80+
na.rm = FALSE,
81+
show.legend = NA,
82+
inherit.aes = TRUE) {
83+
layer(
84+
data = data,
85+
mapping = mapping,
86+
stat = StatDensity2dFilled,
87+
geom = geom,
88+
position = position,
89+
show.legend = show.legend,
90+
inherit.aes = inherit.aes,
91+
params = list(
92+
na.rm = na.rm,
93+
contour = contour,
94+
contour_var = contour_var,
95+
n = n,
96+
h = h,
97+
adjust = adjust,
98+
...
99+
)
100+
)
101+
}
102+
103+
#' @rdname geom_density_2d
104+
#' @usage NULL
105+
#' @export
106+
stat_density2d_filled <- stat_density_2d_filled
107+
108+
78109
#' @rdname ggplot2-ggproto
79110
#' @format NULL
80111
#' @usage NULL
@@ -85,24 +116,20 @@ StatDensity2d <- ggproto("StatDensity2d", Stat,
85116
required_aes = c("x", "y"),
86117

87118
extra_params = c(
88-
"na.rm", "contour", "contour_type", "contour_var",
119+
"na.rm", "contour", "contour_var",
89120
"bins", "binwidth", "breaks"
90121
),
91122

123+
# stat used for contouring
124+
contour_stat = StatContour,
125+
92126
compute_layer = function(self, data, params, layout) {
93127
# first run the regular layer calculation to infer densities
94128
data <- ggproto_parent(Stat, self)$compute_layer(data, params, layout)
95129

96130
# if we're not contouring we're done
97131
if (!isTRUE(params$contour)) return(data)
98132

99-
# otherwise, simulate last part compute_layer() in StatContour or StatContourFilled
100-
if (isTRUE(params$contour_type == "bands")) {
101-
cont_stat <- StatContourFilled
102-
} else {
103-
cont_stat <- StatContour
104-
}
105-
106133
# set up data and parameters for contouring
107134
contour_var <- params$contour_var %||% "density"
108135
if (!isTRUE(contour_var %in% c("density", "ndensity", "count"))) {
@@ -119,7 +146,7 @@ StatDensity2d <- ggproto("StatDensity2d", Stat,
119146
args <- c(list(data = quote(data), scales = quote(scales)), params)
120147
dapply(data, "PANEL", function(data) {
121148
scales <- layout$get_scales(data$PANEL[1])
122-
tryCatch(do.call(cont_stat$compute_panel, args), error = function(e) {
149+
tryCatch(do.call(self$contour_stat$compute_panel, args), error = function(e) {
123150
warn(glue("Computation failed in `{snake_class(self)}()`:\n{e$message}"))
124151
new_data_frame()
125152
})
@@ -153,11 +180,15 @@ StatDensity2d <- ggproto("StatDensity2d", Stat,
153180
}
154181
)
155182

183+
184+
156185
#' @rdname ggplot2-ggproto
157186
#' @format NULL
158187
#' @usage NULL
159188
#' @export
160189
StatDensity2dFilled <- ggproto("StatDensity2dFilled", StatDensity2d,
161-
default_aes = aes(colour = NA, fill = after_stat(level))
190+
default_aes = aes(colour = NA, fill = after_stat(level)),
191+
192+
contour_stat = StatContourFilled
162193
)
163194

man/geom_density_2d.Rd

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

tests/testthat/test-stat-density2d.R

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ test_that("stat_density2d can produce contour and raster data", {
2020
p <- ggplot(faithful, aes(x = eruptions, y = waiting))
2121

2222
p_contour_lines <- p + stat_density_2d()
23-
p_contour_bands <- p + stat_density_2d(contour_type = "bands")
23+
p_contour_bands <- p + stat_density_2d_filled()
2424
p_raster <- p + stat_density_2d(contour = FALSE)
2525

2626
d_lines <- layer_data(p_contour_lines)
@@ -40,6 +40,16 @@ test_that("stat_density2d can produce contour and raster data", {
4040
expect_true(unique(d_raster$level) == 1)
4141
expect_true(unique(d_raster$piece) == 1)
4242

43+
# stat_density_2d() and stat_density_2d_filled() produce identical
44+
# density output with `contour = FALSE`
45+
# (`fill` and `colour` will differ due to different default aesthetic mappings)
46+
d_raster2 <- layer_data(p + stat_density_2d_filled(contour = FALSE))
47+
expect_identical(d_raster$x, d_raster2$x)
48+
expect_identical(d_raster$y, d_raster2$y)
49+
expect_identical(d_raster$density, d_raster2$density)
50+
expect_identical(d_raster$ndensity, d_raster2$ndensity)
51+
expect_identical(d_raster$count, d_raster2$count)
52+
4353
# stat_density_2d() with contouring is the same as stat_contour() on calculated density
4454
p_lines2 <- ggplot(d_raster, aes(x, y, z = density)) + stat_contour()
4555
d_lines2 <- layer_data(p_lines2)

0 commit comments

Comments
 (0)