Skip to content

geom_smooth() #183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Mar 12, 2015
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: plotly
Type: Package
Title: Interactive, publication-quality graphs online.
Version: 0.5.24
Version: 0.5.25
Authors@R: c(person("Chris", "Parmer", role = c("aut", "cre"),
email = "[email protected]"),
person("Scott", "Chamberlain", role = "aut",
Expand Down
8 changes: 6 additions & 2 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
0.5.25 -- 10 March 2015

Implemented geom_smooth() #183

0.5.24 -- 10 March 2015

Implemented #167
Implemented facet_wrap(scales="free") #167

0.5.23 -- 10 March 2015.

geom_ribbon now respects alpha transparency
geom_ribbon() now respects alpha transparency

0.5.22 -- 2 March 2015.

Expand Down
20 changes: 18 additions & 2 deletions R/ggplotly.R
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,24 @@ gg2list <- function(p){
}

# This extracts essential info for this geom/layer.
traces <- layer2traces(L, df, misc)

if (L$geom$objname == "smooth") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this geom_smooth-dependent code should be moved inside of trace_generation.R. Is there some reason why you did not put it there with the other geom-specific code?

Can't you add a smooth= function to the toBasic list? https://github.com/ropensci/plotly/blob/master/R/trace_generation.R#L290-L382

Or add a smooth= function to the geom2trace list? https://github.com/ropensci/plotly/blob/master/R/trace_generation.R#L438

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code in trace_generation.R seems to assume a layer consists of one geom (is that right?), but a geom_smooth() is really geom_path() + geom_ribbon(). I suppose I could add smooth (instead of smoothRibbon & smoothLine) to these lists, but the downside is that we essentially have to copy/paste over ribbon and line into this spot (so, if we happen to fix/change ribbon or line, those changes won't propagate to smooth). Would you prefer to take this approach instead? Or maybe there is a better way?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well actually the approach with smoothRibbon and smoothLine is fine with me, but I just think it will be easier to maintain in the future if we put all the geom-specific code inside of layer2traces (rather than being inside of gg2list as it is now).

# smooth is really a line + ribbon geom
# draw ribbon (unless se = FALSE)
misc$smoothRibbon <- TRUE
trace1 <- if (isTRUE(L$stat_params$se == FALSE)) {
NULL
} else {
layer2traces(L, df, misc)
}
misc$smoothRibbon <- FALSE
# always draw the line
misc$smoothLine <- TRUE
trace2 <- layer2traces(L, df, misc)
trace2 <- lapply(trace2, function(x) { x$showlegend <- FALSE; x })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is confusing to use lapply with an anonymous function here where you could have used a for loop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdhock What?! I would say that, in R, you want to use *ply functions (in order to avoid for loops) as much as possible!!

traces <- c(trace1, trace2)
} else {
traces <- layer2traces(L, df, misc)
}
possible.legends <- markLegends[[L$geom$objname]]
actual.legends <- possible.legends[possible.legends %in% names(L$mapping)]
layer.legends[[paste(i)]] <- actual.legends
Expand Down
13 changes: 12 additions & 1 deletion R/trace_generation.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ layer2traces <- function(l, d, misc) {
g <- list(geom=l$geom$objname,
data=not.na(d),
prestats.data=not.na(misc$prestats.data))
if (g$geom == "smooth") {
if (isTRUE(misc$smoothRibbon)) g$geom <- "smoothRibbon"
if (isTRUE(misc$smoothLine)) g$geom <- "smoothLine"
}
# needed for when group, etc. is an expression.
g$aes <- sapply(l$mapping, function(k) as.character(as.expression(k)))
# Partial conversion for geom_violin (Plotly does not offer KDE yet)
Expand Down Expand Up @@ -187,7 +191,6 @@ layer2traces <- function(l, d, misc) {
data.list <- structure(list(list(data=basic$data, params=basic$params)),
names=basic$params$name)
}

getTrace <- geom2trace[[basic$geom]]
if(is.null(getTrace)){
warning("Conversion not implemented for geom_",
Expand Down Expand Up @@ -378,6 +381,14 @@ toBasic <- list(
g$params$sizemax <- max(g$prestats.data$globsizemax)
}
g
},
smoothLine=function(g) {
if (length(unique(g$data$group)) == 1) g$params$colour <- "#3366FF"
group2NA(g, "path")
},
smoothRibbon=function(g) {
if (is.null(g$params$alpha)) g$params$alpha <- 0.1
group2NA(g, "ribbon")
}
)

Expand Down
74 changes: 74 additions & 0 deletions tests/testthat/test-cookbook-scatterplots.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
set.seed(955)
# Make some noisily increasing data
dat <- data.frame(cond = rep(c("A", "B"), each=10),
xvar = c(1.475957, -3.423712, 1.966129, 5.575364, 2.954719, 2.768286, 3.507499, 6.945000, 12.135050, 10.231673, 13.040393, 12.231689, 13.506993, 13.590874, 15.455178, 28.431185, 17.758937, 24.730797, 22.954238, 21.122766),
yvar = c(-1.315387, 3.323239, 4.452183, 4.597885, 5.697203, 5.991221, 5.764561, 10.163165, 14.805634, 11.447913, 12.163597, 10.930851, 13.491366, 11.800783, 19.246991, 13.870457, 11.031923, 22.700302, 24.877547, 22.520114))
# cond xvar yvar
# A -4.252354091 3.473157275
# A 1.702317971 0.005939612
# ...
# B 17.793359218 19.718587761
# B 19.319909163 19.647899863

g <- ggplot(dat, aes(x=xvar, y=yvar)) +
geom_point(shape=1) # Use hollow circles
save_outputs(g, "scatterplots-hollow")

g <- ggplot(dat, aes(x=xvar, y=yvar)) +
geom_point(shape=1) +
geom_smooth(method=lm) # Add linear regression line
save_outputs(g, "scatterplots-smooth-lm")

g <- ggplot(dat, aes(x=xvar, y=yvar)) +
geom_point(shape=1) +
geom_smooth(method=lm, se=FALSE) # Don't add shaded confidence region
save_outputs(g, "scatterplots-smooth-lm-se-false")


g <- ggplot(dat, aes(x=xvar, y=yvar)) +
geom_point(shape=1) + # Use hollow circles
geom_smooth() # Add a loess smoothed fit curve with confidence region
save_outputs(g, "scatterplots-loess")

# Set color by cond
g <- ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + geom_point(shape=1)
save_outputs(g, "scatterplots-color")

# # Same, but with different colors and add regression lines
g <- ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + geom_point(shape=1) +
scale_colour_hue(l=50) + # Use a slightly darker palette than normal
geom_smooth(method=lm, se=FALSE)
save_outputs(g, "scatterplots-scale-color-hue")

# Extend the regression lines beyond the domain of the data
g <- ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + geom_point(shape=1) +
scale_colour_hue(l=50) +
geom_smooth(method=lm, se=FALSE, fullrange=T)
save_outputs(g, "scatterplots-full-range")

# Set shape by cond
g <- ggplot(dat, aes(x=xvar, y=yvar, shape=cond)) + geom_point()
save_outputs(g, "scatterplots-shape")

# Same, but with different shapes
g <- ggplot(dat, aes(x=xvar, y=yvar, shape=cond)) + geom_point() +
scale_shape_manual(values=c(1,2)) # Use a hollow circle and triangle
save_outputs(g, "scatterplots-shape-manual")

# Round xvar and yvar to the nearest 5
dat$xrnd <- round(dat$xvar/5)*5
dat$yrnd <- round(dat$yvar/5)*5

# Make each dot partially transparent, with 1/4 opacity
# For heavy overplotting, try using smaller values
g <- ggplot(dat, aes(x=xrnd, y=yrnd)) +
geom_point(shape=19, # Use solid circles
alpha=1/4) # 1/4 opacity
save_outputs(g, "scatterplots-overlap")

# Jitter the points
# Jitter range is 1 on the x-axis, .5 on the y-axis
g <- ggplot(dat, aes(x=xrnd, y=yrnd)) +
geom_point(shape=1, # Use hollow circles
position=position_jitter(width=1,height=.5))
save_outputs(g, "scatterplots-jitter")
18 changes: 18 additions & 0 deletions tests/testthat/test-ggplot-smooth.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
context("smooth")

p <- ggplot(mtcars, aes(mpg, wt)) + geom_point() + geom_smooth()

test_that("geom_point() + geom_smooth() produces 3 traces", {
info <- gg2list(p)
expect_true(sum(names(info) == "") == 3)
save_outputs(p, "smooth")
})

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

test_that("geom_point() + geom_smooth(se = FALSE) produces 2 traces", {
info2 <- gg2list(p2)
expect_true(sum(names(info2) == "") == 2)
save_outputs(p2, "smooth-se-false")
})