Reputation: 3947
With lots of overlapping data to show, it can be helpful to jitter points so they can be better identified. For discrete (not necessarily factor
) data it can be helpful to offset the entire series slightly. This can now be done quite neatly using ggplot2::position_nudge()
. As far as I can tell, that function does not accept NSE variables from the ggplot()
call, so (and by all means correct me if I'm wrong) one needs to specify the data source if providing a variable offset from some data structure.
If, say, we want to nudge a value by an amount dependent on another value, we could do it this way:
library(ggplot2)
p <- ggplot(mtcars, aes(x = mpg, y = factor(cyl))) +
geom_point(aes(col = factor(am)),
position = position_nudge(y = 0.1*mtcars$am))
p
Trying to use just am
in the position_nudge()
call fails ("non-numeric argument to binary operator"
). Otherwise this is acceptable.
The problem with this is that facet_wrap
on such a call breaks the effect since, presumably, the faceting code knows nothing about this explicit data
p + facet_wrap(~gear)
Is there a way to allow this to happen (short of converting to continuous, shifting manually, then relabelling axis), or a more suitable function? If nothing comes up I'll raise an issue in the repo, but I figured I should consult the hivemind first.
Upvotes: 1
Views: 710
Reputation: 93871
UPDATE: Based on the discussion in the comments, maybe the best approach is to just add fake data in a location outside the plot area so that all factor levels will have at least one data point. This will result in every point being dodged in every facet. For example:
First, we'll add data points for all unique values of all variables that will become factors in the plot, but we'll assign them an mpg
value of -1, which is outside the plot area of the real data.
library(ggplot2)
library(ggstance)
library(dplyr)
mtcars = bind_rows(mtcars,
expand.grid(mpg=-1,
cyl=unique(mtcars$cyl),
am=unique(mtcars$am),
gear=unique(mtcars$gear)))
Now we can plot with vertical dodging as in the original answer, but all points will be dodged:
ggplot(mtcars, aes(x = mpg, y = factor(cyl))) +
geom_point(aes(col = factor(am)), position = position_dodgev(0.5)) +
facet_wrap(~gear) +
coord_cartesian(xlim=range(mtcars$mpg[mtcars$mpg>0]))
Original Answer
You can use position_dodge
if you switch your x
and y
aesthetics, and then add coord_flip
to put cyl
back on the vertical axis:
p = ggplot(mtcars, aes(y = mpg, x = factor(cyl))) +
geom_point(aes(col = factor(am)), position = position_dodge(0.3)) +
coord_flip()
p + facet_wrap(~gear)
Another, option is to use position_dodgev
from the ggstance
package, which provides vertical dodging, and avoids the need to flip the axes:
library(ggstance)
p = ggplot(mtcars, aes(x = mpg, y = factor(cyl))) +
geom_point(aes(col = factor(am)), position = position_dodgev(0.3))
Although it's unnecessary, something like your original method could work if you do it inside aes
so that all the ggplot components know about the mapping. For example:
ggplot(mtcars, aes(x = mpg, y = cyl + 0.2*(am - mean(am)))) +
geom_point(aes(col = factor(am))) +
scale_y_continuous(breaks=unique(mtcars$cyl), minor_breaks=NULL) +
facet_wrap(~ gear)
Upvotes: 2