15
15
# ' not line-up, and hence you won't be able to stack density values.
16
16
# ' This parameter only matters if you are displaying multiple densities in
17
17
# ' one plot or if you are manually adjusting the scale limits.
18
+ # ' @param bounds Known lower and upper bounds for estimated data. Default
19
+ # ' `c(-Inf, Inf)` means that there are no (finite) bounds.
18
20
# ' @section Computed variables:
19
21
# ' \describe{
20
22
# ' \item{density}{density estimate}
@@ -35,6 +37,7 @@ stat_density <- function(mapping = NULL, data = NULL,
35
37
n = 512 ,
36
38
trim = FALSE ,
37
39
na.rm = FALSE ,
40
+ bounds = c(- Inf , Inf ),
38
41
orientation = NA ,
39
42
show.legend = NA ,
40
43
inherit.aes = TRUE ) {
@@ -54,6 +57,7 @@ stat_density <- function(mapping = NULL, data = NULL,
54
57
n = n ,
55
58
trim = trim ,
56
59
na.rm = na.rm ,
60
+ bounds = bounds ,
57
61
orientation = orientation ,
58
62
...
59
63
)
@@ -84,7 +88,8 @@ StatDensity <- ggproto("StatDensity", Stat,
84
88
extra_params = c(" na.rm" , " orientation" ),
85
89
86
90
compute_group = function (data , scales , bw = " nrd0" , adjust = 1 , kernel = " gaussian" ,
87
- n = 512 , trim = FALSE , na.rm = FALSE , flipped_aes = FALSE ) {
91
+ n = 512 , trim = FALSE , na.rm = FALSE , bounds = c(- Inf , Inf ),
92
+ flipped_aes = FALSE ) {
88
93
data <- flip_data(data , flipped_aes )
89
94
if (trim ) {
90
95
range <- range(data $ x , na.rm = TRUE )
@@ -93,15 +98,17 @@ StatDensity <- ggproto("StatDensity", Stat,
93
98
}
94
99
95
100
density <- compute_density(data $ x , data $ weight , from = range [1 ],
96
- to = range [2 ], bw = bw , adjust = adjust , kernel = kernel , n = n )
101
+ to = range [2 ], bw = bw , adjust = adjust , kernel = kernel , n = n ,
102
+ bounds = bounds )
97
103
density $ flipped_aes <- flipped_aes
98
104
flip_data(density , flipped_aes )
99
105
}
100
106
101
107
)
102
108
103
109
compute_density <- function (x , w , from , to , bw = " nrd0" , adjust = 1 ,
104
- kernel = " gaussian" , n = 512 ) {
110
+ kernel = " gaussian" , n = 512 ,
111
+ bounds = c(- Inf , Inf )) {
105
112
nx <- length(x )
106
113
if (is.null(w )) {
107
114
w <- rep(1 / nx , nx )
@@ -122,8 +129,15 @@ compute_density <- function(x, w, from, to, bw = "nrd0", adjust = 1,
122
129
), n = 1 ))
123
130
}
124
131
125
- dens <- stats :: density(x , weights = w , bw = bw , adjust = adjust ,
126
- kernel = kernel , n = n , from = from , to = to )
132
+ if (all(is.infinite(bounds ))) {
133
+ dens <- stats :: density(x , weights = w , bw = bw , adjust = adjust ,
134
+ kernel = kernel , n = n , from = from , to = to )
135
+ } else {
136
+ dens <- stats :: density(x , weights = w , bw = bw , adjust = adjust ,
137
+ kernel = kernel , n = n )
138
+
139
+ dens <- reflect_density(dens = dens , bounds = bounds , from = from , to = to )
140
+ }
127
141
128
142
new_data_frame(list (
129
143
x = dens $ x ,
@@ -134,3 +148,26 @@ compute_density <- function(x, w, from, to, bw = "nrd0", adjust = 1,
134
148
n = nx
135
149
), n = length(dens $ x ))
136
150
}
151
+
152
+ reflect_density <- function (dens , bounds , from , to ) {
153
+ if (all(is.infinite(bounds ))) {
154
+ return (dens )
155
+ }
156
+
157
+ f_dens <- stats :: approxfun(
158
+ x = dens $ x , y = dens $ y , method = " linear" , yleft = 0 , yright = 0
159
+ )
160
+
161
+ out_x <- intersection_grid(dens $ x , bounds , from , to )
162
+ out_y <- f_dens(out_x ) + f_dens(bounds [1 ] + (bounds [1 ] - out_x )) +
163
+ f_dens(bounds [2 ] + (bounds [2 ] - out_x ))
164
+
165
+ list (x = out_x , y = out_y )
166
+ }
167
+
168
+ intersection_grid <- function (grid , bounds , from , to ) {
169
+ left <- max(from , bounds [1 ])
170
+ right <- min(to , bounds [2 ])
171
+
172
+ seq(from = left , to = right , length.out = length(grid ))
173
+ }
0 commit comments