Skip to content

Commit 4255cd8

Browse files
huftishadley
authored andcommitted
Allow separate expansion values for lower and upper range limits (v2) (#1805)
* Allow separate expansion values for lower and upper range limits. The `expand` argument for `scale_*_continuous()` and `scale_*_discrete()` now accepts separate expansion constants for the lower and upper range limits. This is useful for creating bar charts where the bottom of the bars are flush with the x axis but the bars still have some (automatically calculated amount of) space above them: ```R ggplot(mtcars) + geom_bar(aes(x = factor(cyl))) + scale_y_continuous(expand = c(0, 0, 0.1, 0)) ``` It can also be useful for line charts, e.g. for counts over time, where one wants to have a ’hard’ lower limit of y = 0, but leave the upper limit unspecified (and perhaps differing between panels), but with some extra space above the highest point on the line. (With symmetrical limits, the extra space above the highest point could cause the lower limit to be negative.) The syntax for the multiplicative and additive expansion constants has been changed from `c(m, a)` to `c(m_lower, a_lower, m_uppper, a_upper)`. The old syntax will still work, as length 2 vectors `c(m, a)` are expanded to `c(m, a, m, a)` and length 3 vectors are expanded from `c(m1, a1, m2)` to `c(m1, a2, m2, a1)`. (@huftis, #1669) * Added `expand_scale()` function for easier generation of scale expansion vectors. Instead of having to manually specify an `expand` argument using a somewhat confusing syntax (a vector of 2, 3 or 4 numeric values), it’s now possible to use the user-friendly (and documented) `expand_scale()` function. This commit also cleans up the documentation related to the `expand` argument, which was duplicated in several functions. * Added UTF-8 character encoding declaration to DESCRIPTION. The documentation for one of the functions had a no-breaking space, (between a number and the word ‘units’), which caused R CMD check to complain about ‘non-ASCII input and no declared encoding’. This adds a character encoding declaration of UTF-8 to the DESCRIPTION file to fix this problem. * Fixed some style issues. * Updated and regenerated documentation. * Specify character encoding used for documentation. * Minor grammar improvement in documentation. * Don’t generate documentation for internal function expand_range4().
1 parent 32e699e commit 4255cd8

17 files changed

+217
-63
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,4 @@ Collate:
226226
VignetteBuilder: knitr
227227
RoxygenNote: 6.0.1
228228
Roxygen: list(markdown = TRUE)
229+
Encoding: UTF-8

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ export(element_line)
258258
export(element_rect)
259259
export(element_text)
260260
export(expand_limits)
261+
export(expand_scale)
261262
export(facet_grid)
262263
export(facet_null)
263264
export(facet_wrap)

NEWS.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,31 @@
6363

6464
* `geom_smooth`'s message for `method="auto"` now reports the formula used,
6565
in addition to the name of the smoothing function (@davharris #1951).
66+
67+
* The `expand` argument for `scale_*_continuous()` and `scale_*_discrete()`
68+
now accepts separate expansion values for the lower and upper range
69+
limits. The expansion limits can be specified using the convenience
70+
function `expand_scale()`.
71+
72+
Separate expansion limits may be useful for bar charts, e.g. if one
73+
wants to have the bottom of the bars being flush with the x axis but
74+
still leave some (automatically calculated amount of) space above them:
75+
76+
```R
77+
ggplot(mtcars) +
78+
geom_bar(aes(x = factor(cyl))) +
79+
scale_y_continuous(expand = expand_scale(mult = c(0, .1)))
80+
```
81+
82+
It can also be useful for line charts, e.g. for counts over time,
83+
where one wants to have ahardlower limit of y = 0 but leave the
84+
upper limit unspecified (and perhaps differing between panels),
85+
but with some extra space above the highest point on the line.
86+
(With symmetrical limits, the extra space above the highest point
87+
could in some cases cause the lower limit to be negative.)
88+
89+
The old syntax for the `expand` argument will of course continue
90+
to work. (@huftis, #1669)
6691

6792
* `print.ggplot()` now returns the original ggplot object, instead of the output from `ggplot_build()`. Also, the object returned from `ggplot_build()` now has the class `"ggplot_built"`. (#2034)
6893

R/coord-.r

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ Coord <- ggproto("Coord",
114114
#' @keywords internal
115115
is.Coord <- function(x) inherits(x, "Coord")
116116

117-
expand_default <- function(scale, discrete = c(0, 0.6), continuous = c(0.05, 0)) {
117+
expand_default <- function(scale, discrete = c(0, 0.6, 0, 0.6), continuous = c(0.05, 0, 0.05, 0)) {
118118
scale$expand %|W|% if (scale$is_discrete()) discrete else continuous
119119
}
120120

R/scale-.r

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ Scale <- ggproto("Scale", NULL,
121121
},
122122

123123
# The physical size of the scale.
124-
# This always returns a numeric vector of length 2, giving the physical
124+
# This always returns a numeric vector of length 4, giving the physical
125125
# dimensions of a scale.
126-
dimension = function(self, expand = c(0, 0)) {
126+
dimension = function(self, expand = c(0, 0, 0, 0)) {
127127
stop("Not implemented", call. = FALSE)
128128
},
129129

@@ -224,8 +224,8 @@ ScaleContinuous <- ggproto("ScaleContinuous", Scale,
224224
ifelse(!is.na(scaled), scaled, self$na.value)
225225
},
226226

227-
dimension = function(self, expand = c(0, 0)) {
228-
expand_range(self$get_limits(), expand[1], expand[2])
227+
dimension = function(self, expand = c(0, 0, 0, 0)) {
228+
expand_range4(self$get_limits(), expand)
229229
},
230230

231231
get_breaks = function(self, limits = self$get_limits()) {
@@ -397,8 +397,8 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
397397
}
398398
},
399399

400-
dimension = function(self, expand = c(0, 0)) {
401-
expand_range(length(self$get_limits()), expand[1], expand[2])
400+
dimension = function(self, expand = c(0, 0, 0, 0)) {
401+
expand_range4(length(self$get_limits()), expand)
402402
},
403403

404404
get_breaks = function(self, limits = self$get_limits()) {
@@ -532,11 +532,7 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
532532
#' A function used to scale the input values to the range \eqn{[0, 1]}.
533533
#' @param oob Function that handles limits outside of the scale limits
534534
#' (out of bounds). The default replaces out of bounds values with NA.
535-
#' @param expand A numeric vector of length two giving multiplicative and
536-
#' additive expansion constants. These constants ensure that the data is
537-
#' placed some distance away from the axes. The defaults are
538-
#' `c(0.05, 0)` for continuous variables, and `c(0, 0.6)` for
539-
#' discrete variables.
535+
#' @inheritParams scale_x_discrete
540536
#' @param na.value Missing values will be replaced with this value.
541537
#' @param trans Either the name of a transformation object, or the
542538
#' object itself. Built-in transformations include "asn", "atanh",

R/scale-discrete-.r

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
#' at integer positions). This is what allows jittering to work.
88
#'
99
#' @inheritDotParams discrete_scale -expand -position
10-
#' @param expand a numeric vector of length two giving multiplicative and
11-
#' additive expansion constants. These constants ensure that the data is
12-
#' placed some distance away from the axes.
10+
#' @param expand Vector of range expansion constants used to add some
11+
#' padding around the data, to ensure that they are placed some distance
12+
#' away from the axes. Use the convenience function [expand_scale()]
13+
#' to generate the values for the `expand` argument. The defaults are to
14+
#' expand the scale by 5\% on each side for continuous variables, and by
15+
#' 0.6 units on each side for discrete variables.
1316
#' @param position The position of the axis. `left` or `right` for y
1417
#' axes, `top` or `bottom` for x axes
1518
#' @rdname scale_discrete
@@ -105,20 +108,20 @@ ScaleDiscretePosition <- ggproto("ScaleDiscretePosition", ScaleDiscrete,
105108
}
106109
},
107110

108-
dimension = function(self, expand = c(0, 0)) {
111+
dimension = function(self, expand = c(0, 0, 0, 0)) {
109112
c_range <- self$range_c$range
110113
d_range <- self$get_limits()
111114

112115
if (self$is_empty()) {
113116
c(0, 1)
114117
} else if (is.null(self$range$range)) { # only continuous
115-
expand_range(c_range, expand[1], expand[2] , 1)
118+
expand_range4(c_range, expand)
116119
} else if (is.null(c_range)) { # only discrete
117-
expand_range(c(1, length(d_range)), expand[1], expand[2], 1)
120+
expand_range4(c(1, length(d_range)), expand)
118121
} else { # both
119122
range(
120-
expand_range(c_range, expand[1], 0 , 1),
121-
expand_range(c(1, length(d_range)), 0, expand[2], 1)
123+
c_range,
124+
expand_range4(c(1, length(d_range)), expand)
122125
)
123126
}
124127
},

R/utilities.r

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,77 @@ rescale01 <- function(x) {
172172
(x - rng[1]) / (rng[2] - rng[1])
173173
}
174174

175+
#' Similar to expand_range(), but taking a vector ‘expand’
176+
#' of *four* expansion values, where the 1st and 2nd
177+
#' elements are used for the lower limit, and the 3rd and
178+
#' 4th elements are used for the upper limit).
179+
#'
180+
#' The ‘expand’ argument can also be of length 2,
181+
#' and the expansion values for the lower limit
182+
#' are then reused for the upper limit.
183+
#
184+
#' @noRd
185+
#' @keywords internal
186+
expand_range4 <- function(limits, expand) {
187+
stopifnot(is.numeric(expand) && (length(expand) %in% c(2,4)))
188+
# If only two expansion constants are given (i.e. the old syntax),
189+
# reuse them to generate a four-element expansion vector
190+
if (length(expand) == 2) { expand <- c(expand, expand) }
191+
192+
# Calculate separate range expansion for the lower and
193+
# upper range limits, and then combine them into one vector
194+
lower <- expand_range(limits, expand[1], expand[2])[1]
195+
upper <- expand_range(limits, expand[3], expand[4])[2]
196+
c(lower, upper)
197+
}
198+
199+
#' Generate expansion vector for scales.
200+
#'
201+
#' This is a convenience function for generating scale expansion vectors
202+
#' for the \code{expand} argument of
203+
#' \code{\link[=scale_x_continuous]{scale_*_continuous}} and
204+
#' \code{\link[=scale_x_discrete]{scale_*_discrete}}.
205+
#' The expansions vectors are used to add some space between
206+
#' the data and the axes.
207+
#'
208+
#' @export
209+
#' @param mult vector of multiplicative range expansion factors.
210+
#' If length 1, both the lower and upper limits of the scale
211+
#' are expanded outwards by \code{mult}. If length 2, the lower limit
212+
#' is expanded by \code{mult[1]} and the upper limit by \code{mult[2]}.
213+
#' @param add vector of additive range expansion constants.
214+
#' If length 1, both the lower and upper limits of the scale
215+
#' are expanded outwards by \code{add} units. If length 2, the
216+
#' lower limit is expanded by \code{add[1]} and the upper
217+
#' limit by \code{add[2]}.
218+
#' @examples
219+
#' # No space below the bars but 10% above them
220+
#' ggplot(mtcars) +
221+
#' geom_bar(aes(x = factor(cyl))) +
222+
#' scale_y_continuous(expand = expand_scale(mult = c(0, .1)))
223+
#'
224+
#' # Add 2 units of space on the left and right of the data
225+
#' ggplot(subset(diamonds, carat > 2), aes(cut, clarity)) +
226+
#' geom_jitter() +
227+
#' scale_x_discrete(expand = expand_scale(add = 2))
228+
#'
229+
#' # Reproduce the default range expansion used
230+
#' # when the ‘expand’ argument is not specified
231+
#' ggplot(subset(diamonds, carat > 2), aes(cut, price)) +
232+
#' geom_jitter() +
233+
#' scale_x_discrete(expand = expand_scale(add = .6)) +
234+
#' scale_y_continuous(expand = expand_scale(mult = .05))
235+
expand_scale = function(mult = 0, add = 0) {
236+
stopifnot(is.numeric(mult) && is.numeric(add))
237+
stopifnot((length(mult) %in% 1:2) && (length(add) %in% 1:2))
238+
239+
mult <- rep(mult, length.out = 2)
240+
add <- rep(add, length.out = 2)
241+
c(mult[1], add[1], mult[2], add[2])
242+
}
243+
244+
245+
175246
#' Give a deprecation error, warning, or message, depending on version number.
176247
#'
177248
#' Version numbers have the format <major>.<minor>.<subminor>, like 0.9.2.

man/continuous_scale.Rd

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

man/discrete_scale.Rd

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

man/expand_scale.Rd

Lines changed: 46 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)