Skip to content

Commit c9f7ff2

Browse files
committed
Reimplement ribbon as a basic polygon. Fix #191. Fix #192.
1 parent 9902be5 commit c9f7ff2

File tree

3 files changed

+115
-37
lines changed

3 files changed

+115
-37
lines changed

R/trace_generation.R

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ layer2traces <- function(l, d, misc) {
2626
# geom_smooth() means geom_line() + geom_ribbon()
2727
# Note the line is always drawn, but ribbon is not if se = FALSE.
2828
if (g$geom == "smooth") {
29-
# If smoothLine has been compiled already, consider smoothRibbon.
29+
# If smoothLine has been compiled already, consider drawing the ribbon
3030
if (isTRUE(misc$smoothLine)) {
3131
misc$smoothLine <- FALSE
3232
if (isTRUE(l$stat_params$se == FALSE)) {
3333
return(NULL)
3434
} else {
3535
g$geom <- "smoothRibbon"
36+
# disregard colour
37+
g$data <- g$data[!grepl("^colour[.name]?", names(g$data))]
3638
}
3739
} else {
3840
misc$smoothLine <- TRUE
@@ -248,7 +250,6 @@ layer2traces <- function(l, d, misc) {
248250
if (length(unique(name.list)) < 2)
249251
tr$name <- as.character(name.list[[1]])
250252
}
251-
252253
dpd <- data.params$data
253254
if ("PANEL" %in% names(dpd) && nrow(dpd) > 0)
254255
{
@@ -335,6 +336,11 @@ toBasic <- list(
335336
g$geom <- "polygon"
336337
g
337338
},
339+
ribbon=function(g) {
340+
g$data <- ribbon_dat(g$data)
341+
g$geom <- "polygon"
342+
g
343+
},
338344
path=function(g) {
339345
group2NA(g, "path")
340346
},
@@ -410,8 +416,10 @@ toBasic <- list(
410416
group2NA(g, "path")
411417
},
412418
smoothRibbon=function(g) {
413-
if (is.null(g$params$alpha)) g$params$alpha <- 0.1
414-
group2NA(g, "ribbon")
419+
if (is.null(g$params$alpha)) g$params$alpha <- 0.2
420+
g$data <- ribbon_dat(g$data)
421+
g$geom <- "polygon"
422+
g
415423
}
416424
)
417425

@@ -493,6 +501,26 @@ make.errorbar <- function(data, params, xy){
493501
tr
494502
}
495503

504+
# function to transform geom_ribbon data into format plotly likes
505+
# (note this function is also used for geom_smooth)
506+
ribbon_dat <- function(dat) {
507+
n <- nrow(dat)
508+
o <- order(dat$x)
509+
o2 <- order(dat$x, decreasing = TRUE)
510+
used <- c("x", "ymin", "ymax")
511+
not_used <- setdiff(names(dat), used)
512+
# top-half of ribbon
513+
tmp <- dat[o, ]
514+
others <- tmp[not_used]
515+
dat1 <- cbind(x = tmp$x, y = tmp$ymax, others)
516+
dat1[n+1, ] <- cbind(x = tmp$x[n], y = tmp$ymin[n], others[n, ])
517+
# bottom-half of ribbon
518+
tmp2 <- dat[o2, ]
519+
others2 <- tmp2[not_used]
520+
dat2 <- cbind(x = tmp2$x, y = tmp2$ymin, others2)
521+
rbind(dat1, dat2)
522+
}
523+
496524
# Convert basic geoms to traces.
497525
geom2trace <- list(
498526
path=function(data, params) {
@@ -515,7 +543,8 @@ geom2trace <- list(
515543
mode="lines",
516544
line=paramORdefault(params, aes2line, polygon.line.defaults),
517545
fill="tozerox",
518-
fillcolor=toFill(params$fill))
546+
fillcolor=toFill(params$fill, ifelse(is.null(params$alpha), 1,
547+
params$alpha)))
519548
},
520549
point=function(data, params){
521550
L <- list(x=data$x,
@@ -667,15 +696,6 @@ geom2trace <- list(
667696
fillcolor=toFill(params$fill, ifelse(is.null(params$alpha), 1,
668697
params$alpha)))
669698
},
670-
ribbon=function(data, params) {
671-
list(x=c(data$x[1], data$x, rev(data$x)),
672-
y=c(data$ymin[1], data$ymax, rev(data$ymin)),
673-
type="scatter",
674-
line=paramORdefault(params, aes2line, ribbon.line.defaults),
675-
fill="tonexty",
676-
fillcolor=toFill(params$fill, ifelse(is.null(params$alpha), 1,
677-
params$alpha)))
678-
},
679699
abline=function(data, params) {
680700
list(x=c(params$xstart, params$xend),
681701
y=c(params$intercept + params$xstart * params$slope,

tests/testthat/test-ggplot-ribbon.R

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,51 @@
11
context("ribbon")
22

3-
huron <- data.frame(year=1875:1972, level=as.vector(LakeHuron))
3+
expect_traces <- function(gg, n.traces, name){
4+
stopifnot(is.ggplot(gg))
5+
stopifnot(is.numeric(n.traces))
6+
save_outputs(gg, paste0("ribbon-", name))
7+
L <- gg2list(gg)
8+
is.trace <- names(L) == ""
9+
all.traces <- L[is.trace]
10+
no.data <- sapply(all.traces, function(tr) {
11+
is.null(tr[["x"]]) && is.null(tr[["y"]])
12+
})
13+
has.data <- all.traces[!no.data]
14+
expect_equal(length(has.data), n.traces)
15+
list(traces=has.data, kwargs=L$kwargs)
16+
}
417

5-
rb <- ggplot(huron, aes(x=year)) + geom_ribbon(aes(ymin=level-1, ymax=level+1))
6-
L <- gg2list(rb)
18+
huron <- data.frame(year = 1875:1972, level = as.vector(LakeHuron))
19+
huron$decade <- with(huron, round(year/10) * 10)
20+
huron$diff <- huron$year - huron$decade
721

8-
test_that("sanity check for geom_ribbon", {
9-
expect_equal(length(L), 2)
10-
expect_identical(L[[1]]$type, "scatter")
11-
expect_equal(L[[1]]$x, c(huron$year[1], huron$year, rev(huron$year)))
12-
expect_equal(L[[1]]$y, c(huron$level[1]-1, huron$level+1, rev(huron$level-1)))
13-
expect_identical(L[[1]]$line$color, "transparent")
22+
p1 <- ggplot(data = huron) +
23+
geom_ribbon(aes(x = year, ymin = level-1, ymax = level+1),
24+
alpha = 0.1)
25+
26+
test_that("geom_ribbon() creates 1 trace & respects alpha transparency", {
27+
info <- expect_traces(p1, 1, "alpha")
28+
tr <- info$traces[[1]]
29+
expect_match(L2[[1]]$fillcolor, "0.1)", fixed=TRUE)
1430
})
1531

16-
save_outputs(rb, "ribbon")
32+
p2 <- ggplot(data = huron, aes(group = factor(decade))) +
33+
geom_ribbon(aes(x = diff, ymin = level-0.1, ymax = level+0.1))
1734

18-
rb2 <- ggplot(huron, aes(x=year)) +
19-
geom_ribbon(aes(ymin=level-1, ymax=level+1), alpha = 0.1)
20-
L2 <- gg2list(rb2)
35+
test_that("geom_ribbon() group aesthetic", {
36+
info <- expect_traces(p2, 1, "group")
37+
})
2138

22-
test_that("geom_ribbon respects alpha transparency", {
23-
expect_match(L2[[1]]$fillcolor, "0.1)", fixed=TRUE)
39+
p3 <- ggplot(data = huron, aes(colour = factor(decade))) +
40+
geom_ribbon(aes(x = diff, ymin = level-0.1, ymax = level+0.1))
41+
42+
test_that("geom_ribbon() colour aesthetic", {
43+
info <- expect_traces(p3, 1, "colour")
2444
})
2545

26-
save_outputs(rb2, "ribbon-alpha")
46+
p4 <- ggplot(data = huron, aes(fill = factor(decade))) +
47+
geom_ribbon(aes(x = diff, ymin = level-0.1, ymax = level+0.1))
48+
49+
test_that("geom_ribbon() fill aesthetic", {
50+
info <- expect_traces(p4, 1, "fill")
51+
})

tests/testthat/test-ggplot-smooth.R

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,51 @@
11
context("smooth")
22

3+
expect_traces <- function(gg, n.traces, name){
4+
stopifnot(is.ggplot(gg))
5+
stopifnot(is.numeric(n.traces))
6+
save_outputs(gg, paste0("smooth-", name))
7+
L <- gg2list(gg)
8+
is.trace <- names(L) == ""
9+
all.traces <- L[is.trace]
10+
no.data <- sapply(all.traces, function(tr) {
11+
is.null(tr[["x"]]) && is.null(tr[["y"]])
12+
})
13+
has.data <- all.traces[!no.data]
14+
expect_equal(length(has.data), n.traces)
15+
list(traces=has.data, kwargs=L$kwargs)
16+
}
17+
318
p <- ggplot(mtcars, aes(mpg, wt)) + geom_point() + geom_smooth()
419

520
test_that("geom_point() + geom_smooth() produces 3 traces", {
6-
info <- gg2list(p)
7-
expect_true(sum(names(info) == "") == 3)
8-
save_outputs(p, "smooth")
21+
expect_traces(p, 3, "basic")
922
})
1023

11-
p2 <- ggplot(mtcars, aes(mpg, wt)) + geom_point() + geom_smooth(se = FALSE)
24+
p2 <- ggplot(mtcars, aes(mpg, wt)) + geom_point() +
25+
geom_smooth(se = FALSE)
1226

1327
test_that("geom_point() + geom_smooth(se = FALSE) produces 2 traces", {
14-
info2 <- gg2list(p2)
15-
expect_true(sum(names(info2) == "") == 2)
16-
save_outputs(p2, "smooth-se-false")
28+
expect_traces(p2, 2, "se-false")
1729
})
1830

31+
d <- diamonds[sample(nrows(diamonds, 1000)), ]
32+
p3 <- qplot(carat, price, group = cut, data = d) + geom_smooth()
33+
34+
test_that("geom_smooth() respects group aesthetic", {
35+
# 1 trace for points
36+
# 5 traces for lines (1 for each group)
37+
# 5 traces for ribbons (1 for each group)
38+
expect_traces(p3, 11, "group")
39+
})
40+
41+
p4 <- qplot(carat, price, colour = cut, data = d) + geom_smooth()
42+
43+
test_that("geom_smooth() respects colour aesthetic", {
44+
expect_traces(p4, 11, "colour")
45+
})
46+
47+
p5 <- qplot(carat, price, fill = cut, data = d) + geom_smooth()
48+
49+
test_that("geom_smooth() respects fill aesthetic", {
50+
expect_traces(p5, 11, "fill")
51+
})

0 commit comments

Comments
 (0)