To prevent over-plotting, I would like an alternative to jitter that simply stacks points one on top of the other around the mean (offsets them vertically by increments of given amount rather than add a random value as jitter does).
From this:
example <- data.frame(x=c(1,1,1,2,2,2,2), y=c(1,1,1,2,2,2,2))
qplot(data=example,x=x, y=y)
What I would like is something like this:
This should probably be possible with stat_bindot()
.
Any suggestions?
None of the built in positions quite do this, and geom_dotplot
isn't quite right either because it only works in one dimension. I've cobbled together a new position which does the right sort of thing, but requires manual tweaking to get everything right.
library("proto")
PositionNudge <- proto(ggplot2:::Position, {
objname <- "nudge"
adjust <- function(., data) {
trans_x <- function(x) {
lx <- length(x)
if (lx > 1) {
x + .$width*(seq_len(lx) - ((lx+1)/2))
} else {
x
}
}
trans_y <- function(y) {
ly <- length(y)
if (ly > 1) {
y + .$height*(seq_len(ly) - ((ly+1)/2))
} else {
y
}
}
ddply(data, .(group), transform_position, trans_x=trans_x, trans_y=trans_y)
}
})
position_nudge <- function(width = 0, height = 0) {
PositionNudge$new(width = width, height = height)
}
I called it nudge, because the the other things I though of were already taken (stack, dodge)
To use it
ggplot(example, aes(x, y)) +
geom_point(aes(group=interaction(x,y)), size=5,
position=position_nudge(height=0.03))
You can also nudge in the horizontal direction
ggplot(example, aes(x, y)) +
geom_point(aes(group=interaction(x,y)), size=5,
position=position_nudge(width=0.03))
You have to specify the group interaction as an aesthetic to the geom_point
and the exact width/height needed to be passed to the position_nudge
depend on the size of the points and the size of the output. For example, with a 4x6 output, you need
ggplot(example, aes(x, y)) +
geom_point(aes(group=interaction(x,y)), size=10,
position=position_nudge(height=0.13))
The value of 0.13 was just trial and error until it looked right. Some of the code and things learned from geom_dotplot
could probably be re-used here to make this more robust. Also, I've not tested this with any data other than the example
given, so it may break it some interesting ways. But it is, if nothing else, a start.
Far from the sophistication of @Brian Diggs' nice answer, but a quick alternative where I use the 'dodge data' in the object created by geom_dotplot
to generate new y values. Some manual tweeking is needed to get the scaling of 'dodge' right.
# create ggplot object with geom_dotplot
g1 <- ggplot(example, aes(x = x, y = y)) +
geom_dotplot(stackdir = "center")
# grab dodge values from plot object
dodge <- ggplot_build(g1)[["data"]][[1]]$stackpos
# create new y values that are adjusted by 'dodge'
example$y2 <- example$y + dodge * 0.05
# plot using new y values
ggplot(example, aes(x = x, y = y2)) +
geom_point(size = 5)
# seems to work decently on grouped data as well
df2 <- rbind(example, example)
df2$grp <- rep(c("a", "b"), each = 7)
df2
g2 <- ggplot(df2, aes(x = x, y = y, colour = grp)) +
geom_dotplot(stackdir = "center", stackgroups = FALSE)
dodge <- ggplot_build(g2)[["data"]][[1]]$stackpos
df2$y2 <- df2$y + dodge * 0.04
ggplot(df2, aes(x = x, y = y2, colour = grp)) +
geom_point(size = 5, position = position_dodge(width = 0.05))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With