ggplot2 geom_jitter() in R: Scatter With Random Jitter

The geom_jitter() function in ggplot2 adds random horizontal and vertical noise to scatter points to avoid overplotting on discrete or rounded data.

⚡ Quick Answer
ggplot(df, aes(x, y)) + geom_jitter()
ggplot(df, aes(x, y)) + geom_jitter(width = 0.2, height = 0)
ggplot(df, aes(x, y)) + geom_jitter(alpha = 0.5)
geom_point(position = position_jitter(width = 0.1))  # equivalent
geom_point()                                          # no jitter

Need explanation? Read on for examples and pitfalls.

📊 Is geom_jitter() the right tool?
STARTdiscrete x with overlapping pointsgeom_jitter()categorical-numeric scattergeom_jitter()continuous data, dense overlapgeom_point(alpha = 0.3) or geom_hexbox + raw pointsgeom_boxplot() + geom_jitterexact positions mattergeom_point()

What geom_jitter() does in one sentence

geom_jitter() is geom_point() with random noise added to x and y to spread overlapping points apart. Especially useful for categorical x data.

Syntax

geom_jitter(width = NULL, height = NULL, alpha = 1, ...). width and height control noise amount.

Run live
Run live, no install needed. Every R block on this page runs in your browser. Click Run, edit the code, re-run instantly. No setup.
RJitter cyl vs mpg
library(ggplot2) ggplot(mtcars, aes(factor(cyl), mpg)) + geom_jitter(width = 0.2)

  
Tip
Set width = 0.2 or smaller for categorical x. Default jitter width is 40% of resolution, often too wide.

Five common patterns

1. Categorical-numeric scatter

RCyl as factor + mpg
ggplot(mtcars, aes(factor(cyl), mpg)) + geom_jitter(width = 0.2)

  

2. Combined with boxplot

RShow distribution + raw
ggplot(mtcars, aes(factor(cyl), mpg)) + geom_boxplot(outlier.shape = NA) + geom_jitter(width = 0.2, alpha = 0.6)

  

3. Vertical-only jitter

RSpread y but keep x exact
ggplot(df, aes(x, y)) + geom_jitter(width = 0, height = 0.3)

  

4. With color and alpha

RMany groups; transparent
ggplot(mtcars, aes(factor(cyl), mpg, color = factor(gear))) + geom_jitter(width = 0.2, alpha = 0.7)

  

5. Reproducible jitter

Rset.seed for stable layout
set.seed(42) ggplot(mtcars, aes(factor(cyl), mpg)) + geom_jitter(width = 0.2)

  
Key Insight
Jitter adds RANDOM noise; without set.seed, the plot looks slightly different each time. For reproducible reports, set the seed before plotting.

geom_jitter() vs geom_point() vs position_jitter()

Approach Best for
geom_jitter() Direct jitter of points
geom_point(position = position_jitter(...)) More control over jitter position
geom_point() No jitter (exact positions)

A practical workflow

Pair geom_jitter with geom_boxplot to show distribution AND raw points.

RInteractive R
ggplot(mtcars, aes(factor(cyl), mpg)) + geom_boxplot(outlier.shape = NA, alpha = 0.5) + geom_jitter(width = 0.15, alpha = 0.7) + labs(x = "Cylinders", y = "MPG")

  

Box shows summary; jittered points show raw data.

Common pitfalls

Pitfall 1: too much jitter. Default width spreads points beyond their group. Set width = 0.1 to 0.2 for cleaner display.

Pitfall 2: irreproducible plots. Without set.seed, jitter is random per run. Use set.seed() before for reports.

Warning
geom_jitter() MOVES the actual point positions. Don't use it for exact-coordinate plots like maps or precise scatters.

Try it yourself

Try it: Plot mtcars cyl vs mpg with jittered points and a transparent boxplot. Save to ex_plot.

RYour turn: box + jitter
ex_plot <- mtcars |> # your code here

  
Click to reveal solution
RSolution
ex_plot <- ggplot(mtcars, aes(factor(cyl), mpg)) + geom_boxplot(alpha = 0.4, outlier.shape = NA) + geom_jitter(width = 0.15, alpha = 0.7)

  

Explanation: Boxplot for summary, jitter for raw data; outlier.shape = NA prevents double-marking.

After mastering geom_jitter, look at:

  • geom_point(): exact positions
  • geom_boxplot(): distribution summary
  • geom_violin(): distribution shape
  • geom_dotplot(): stacked dots
  • position_jitter() / position_jitterdodge(): positioning helpers
  • geom_hex() / geom_bin2d(): heatmap for many points

FAQ

What does geom_jitter do in ggplot2?

geom_jitter() adds random horizontal and vertical noise to scatter points to spread overlapping points apart.

How do I control how much jitter?

Set width and height: geom_jitter(width = 0.2, height = 0). Default is 40% of resolution.

Should I use geom_jitter or geom_point?

geom_jitter for discrete/categorical x with overlapping points. geom_point for continuous x or when exact positions matter.

How do I make jitter reproducible?

Call set.seed(N) before the plot. Without it, jitter is random per render.

Can I combine geom_jitter with geom_boxplot?

Yes. geom_boxplot(outlier.shape = NA) + geom_jitter() shows summary AND raw data without double-marking outliers.