Skip to content

Commit 541ae99

Browse files
mastropipaleolimbot
authored andcommitted
Add option to center the steps in geom_step() (#3435, closes #3348)
* Changes the stairstep function to create the option for a "mid" direction. In this case, the "step" (or vertical deflection) occurs halfway between point A and B.
1 parent 141c775 commit 541ae99

File tree

3 files changed

+65
-18
lines changed

3 files changed

+65
-18
lines changed

R/geom-path.r

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,9 @@ GeomLine <- ggproto("GeomLine", GeomPath,
264264
}
265265
)
266266

267-
#' @param direction direction of stairs: 'vh' for vertical then horizontal, or
268-
#' 'hv' for horizontal then vertical.
267+
#' @param direction direction of stairs: 'vh' for vertical then horizontal,
268+
#' 'hv' for horizontal then vertical, or 'mid' for step half-way between
269+
#' adjacent x-values.
269270
#' @export
270271
#' @rdname geom_path
271272
geom_step <- function(mapping = NULL, data = NULL, stat = "identity",
@@ -299,12 +300,12 @@ GeomStep <- ggproto("GeomStep", GeomPath,
299300
}
300301
)
301302

302-
# Calculate stairsteps
303-
# Used by [geom_step()]
304-
#
305-
# @keyword internal
306-
stairstep <- function(data, direction="hv") {
307-
direction <- match.arg(direction, c("hv", "vh"))
303+
#' Calculate stairsteps for `geom_step()`
304+
#' Used by `GeomStep()`
305+
#'
306+
#' @noRd
307+
stairstep <- function(data, direction = "hv") {
308+
direction <- match.arg(direction, c("hv", "vh", "mid"))
308309
data <- as.data.frame(data)[order(data$x), ]
309310
n <- nrow(data)
310311

@@ -316,16 +317,27 @@ stairstep <- function(data, direction="hv") {
316317
if (direction == "vh") {
317318
xs <- rep(1:n, each = 2)[-2*n]
318319
ys <- c(1, rep(2:n, each = 2))
319-
} else {
320+
} else if (direction == "hv") {
320321
ys <- rep(1:n, each = 2)[-2*n]
321322
xs <- c(1, rep(2:n, each = 2))
323+
} else if (direction == "mid") {
324+
xs <- rep(1:(n-1), each = 2)
325+
ys <- rep(1:n, each = 2)
326+
} else {
327+
stop("Parameter `direction` is invalid.")
328+
}
329+
330+
if (direction == "mid") {
331+
gaps <- data$x[-1] - data$x[-n]
332+
mid_x <- data$x[-n] + gaps/2 # map the mid-point between adjacent x-values
333+
x <- c(data$x[1], mid_x[xs], data$x[n])
334+
y <- c(data$y[ys])
335+
data_attr <- data[c(1,xs,n), setdiff(names(data), c("x", "y"))]
336+
} else {
337+
x <- data$x[xs]
338+
y <- data$y[ys]
339+
data_attr <- data[xs, setdiff(names(data), c("x", "y"))]
322340
}
323341

324-
new_data_frame(c(
325-
list(
326-
x = data$x[xs],
327-
y = data$y[ys]
328-
),
329-
data[xs, setdiff(names(data), c("x", "y"))]
330-
))
342+
new_data_frame(c(list(x = x, y = y), data_attr))
331343
}

man/geom_path.Rd

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

tests/testthat/test-geom-path.R

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,40 @@ test_that("keep_mid_true drops leading/trailing FALSE", {
88
})
99

1010

11+
# Tests on stairstep() ------------------------------------------------------------
12+
13+
test_that("stairstep() does not error with too few observations", {
14+
df <- data_frame(x = 1, y = 1)
15+
expect_silent(stairstep(df))
16+
})
17+
18+
test_that("stairstep() exists with error when an invalid `direction` is given", {
19+
df <- data_frame(x = 1:3, y = 1:3)
20+
expect_error(stairstep(df, direction="invalid"))
21+
})
22+
23+
test_that("stairstep() output is correct for direction = 'vh'", {
24+
df <- data_frame(x = 1:3, y = 1:3)
25+
stepped_expected <- data_frame(x = c(1L, 1L, 2L, 2L, 3L), y = c(1L, 2L, 2L, 3L, 3L))
26+
stepped <- stairstep(df, direction = "vh")
27+
expect_equal(stepped, stepped_expected)
28+
})
29+
30+
test_that("stairstep() output is correct for direction = 'hv'", {
31+
df <- data_frame(x = 1:3, y = 1:3)
32+
stepped_expected <- data_frame(x = c(1L, 2L, 2L, 3L, 3L), y = c(1L, 1L, 2L, 2L, 3L))
33+
stepped <- stairstep(df, direction = "hv")
34+
expect_equal(stepped, stepped_expected)
35+
})
36+
37+
test_that("stairstep() output is correct for direction = 'mid'", {
38+
df <- data_frame(x = 1:3, y = 1:3)
39+
stepped_expected <- data_frame(x = c(1, 1.5, 1.5, 2.5, 2.5, 3), y = c(1L, 1L, 2L, 2L, 3L, 3L))
40+
stepped <- stairstep(df, direction = "mid")
41+
expect_equal(stepped, stepped_expected)
42+
})
43+
44+
1145
# Visual tests ------------------------------------------------------------
1246

1347
test_that("geom_path draws correctly", {

0 commit comments

Comments
 (0)