ggplot2 geom_density2d() in R: 2D Density Contour Lines
The geom_density2d() function in ggplot2 estimates 2D kernel density from raw points and draws contour LINES of equal density. It is the contour-only sister of geom_density_2d_filled() and complements scatter plots.
ggplot(df, aes(x, y)) + geom_density_2d() geom_density_2d(bins = 10) # control contour density geom_density_2d_filled() # filled bands instead geom_hex() # rectangular alternative geom_point() + geom_density_2d() # raw points + density
Need explanation? Read on for examples and pitfalls.
What geom_density2d() does in one sentence
geom_density_2d() estimates a 2D kernel density from raw (x, y) points and draws contour lines of equal density. It overlays scatter plots to show density patterns hidden by overplotting.
Syntax
geom_density_2d(bins = NULL, contour_var = "density", h = NULL, ...). Requires aes(x, y); raw points (no z).
geom_density_2d_filled() for filled bands instead of lines. The two-mode old-faithful dataset shows clear contours either way.Five common patterns
1. Density on top of scatter
2. Filled density bands
3. Custom number of contours
4. With facets
5. Adjust bandwidth
geom_density_2d is the kernel-density VERSION of geom_contour. geom_contour needs pre-computed z; geom_density_2d estimates density from raw 2D points first, then draws contours.geom_density2d() vs geom_hex() vs geom_contour()
| Function | Input | Output |
|---|---|---|
geom_density_2d() |
Raw 2D points | Smooth contour lines |
geom_density_2d_filled() |
Raw 2D points | Filled bands |
geom_hex() |
Raw 2D points | Hex-bin counts |
geom_contour() |
(x, y, z) grid | Contours from given z |
When to use which:
- density_2d for smooth density lines.
- hex / bin2d for count-based bins.
- contour when z is already a grid.
A practical workflow
Use density contours to reveal cluster structure in dense scatter.
Two clear density modes (small vs large diamonds) hidden in the overplot become obvious with contours.
Common pitfalls
Pitfall 1: too few points. Density estimation needs many points. With <30, contours look jagged or empty. Use geom_point alone for sparse data.
Pitfall 2: bandwidth choice. Default bandwidth is auto-estimated. For multimodal data, the auto choice may oversmooth. Set h = c(bw_x, bw_y) manually if needed.
geom_density_2d, not geom_density2d. Both work as aliases in current ggplot2, but the underscore form is canonical.Try it yourself
Try it: Plot density contours over a scatter of mtcars wt vs mpg. Save to ex_plot.
Click to reveal solution
Explanation: Scatter plus density contour shows clusters in the wt-mpg plane.
Related ggplot2 functions
After mastering geom_density_2d, look at:
geom_density_2d_filled(): filled bandsgeom_hex()/geom_bin2d(): bin-based densitygeom_contour(): contours from given z gridstat_density_2d(): same stat, different geomMASS::kde2d(): underlying density estimation
FAQ
What does geom_density_2d do in ggplot2?
geom_density_2d() estimates 2D kernel density from raw points and draws contour lines of equal density.
What is the difference between geom_density_2d and geom_contour?
geom_density_2d estimates density from raw points. geom_contour needs a pre-computed (x, y, z) grid.
How do I get filled density bands?
Use geom_density_2d_filled(). It uses the same density estimate but fills between contours.
How do I control the number of contour lines?
Pass bins = N. Default is 10. Smaller for fewer levels.
Is there a "geom_density2d" without underscore?
Both work. geom_density_2d (with underscore) is canonical; geom_density2d is an alias.