Written and maintained by CASRAI Editorial Board
Last updated
In ggplot2, the shape argument of geom_point() sets the symbol drawn for each point. It accepts an integer from 0 to 25, a shape name such as "circle open", a single character, "." or NA. The detail most tutorials skip is which shapes take a fill: only 21 to 25 do, so a fill colour set on any other shape has no visible effect.
What it is used for in research
Point shapes let a scatterplot carry a grouping variable without colour, which matters when figures are printed in greyscale or must stay readable for readers with colour-vision differences. They also let you separate overlapping series (for example treatment and control) in a single panel. This page covers the shape values themselves; for building the plot around them see scatterplots in R, and for naming the legend that a shape mapping creates see labelling a ggplot legend.
Install or access (official source only)
ggplot2 is an R package distributed through CRAN and documented at ggplot2.tidyverse.org. In an R session:
install.packages("ggplot2")
library(ggplot2)
No other package is needed for the examples below; they use the mtcars data set that ships with R.
Shape numbers and names
The ggplot2 aesthetic specifications describe five kinds of value that shape accepts:
- Integers 0 to 25. Shapes 0 to 14 are open (line) shapes, 15 to 19 are solid, and 21 to 25 are filled shapes (colour is the border, fill is the interior).
- Names. Strings such as
"circle","circle open","circle filled","circle cross","circle plus","circle small","bullet","square","diamond","triangle","triangle down","plus","cross"and"asterisk". - A single character. That character is used as the symbol.
".". The smallest rectangle that is visible, usually one pixel.NA. Draws nothing.
To see every shape rendered, the ggplot2 reference for scale_shape() builds a shape chart with scale_shape_identity(), a facet and theme_void(); running that example is the most reliable way to match a number to a symbol in your installed version.
Step-by-step task: set and map point shapes
1. Set one shape for every point
- Pass
shapeoutsideaes():ggplot(mtcars, aes(wt, mpg)) + geom_point(shape = 17, size = 3)
2. Fill vs colour (shapes 21 to 25)
- Use a fillable shape and set both colours. Per the ggplot2 specifications,
colourapplies to all shapes whilefillapplies only to shapes 21 to 25:ggplot(mtcars, aes(wt, mpg)) + geom_point(shape = 21, colour = "black", fill = "white", size = 3, stroke = 1.5) - Control size with two arguments. “The size of the filled part is controlled by
size, the size of the stroke is controlled bystroke. Each is measured in mm, and the total size of the point is the sum of the two.”
3. Map shape to a variable
- Put
shapeinsideaes(), using a factor so ggplot2 treats it as discrete:ggplot(mtcars, aes(wt, mpg, shape = factor(cyl))) + geom_point(size = 3) - Rename the legend with
scale_shape(name = "Cylinders"). Usescale_shape(solid = FALSE)to switch the automatic palette to hollow shapes (solidasks whether shapes should be solid,TRUE, or hollow,FALSE).
4. Choose the shapes yourself
- Use
scale_shape_manual()to assign a specific shape to each level:ggplot(mtcars, aes(wt, mpg, shape = factor(cyl))) + geom_point(size = 3) + scale_shape_manual(values = c(16, 17, 15))
More than six groups
The default shape scale handles six discrete levels. The ggplot2 reference states that the seventh and subsequent levels will not appear on the plot, so points from those groups silently go missing. The fix is to supply a shape for every level with scale_shape_manual(), for example a vector of seven or more values. The same reference advises against using shapes for an ordinal variable, because shape carries no order; use a colour or size scale instead.
Output and how to read it
Each distinct level of the mapped variable gets one symbol and one legend key. Shapes 0 to 14 are drawn as outlines, 15 to 19 as solid marks, and 21 to 25 as a filled interior with a coloured border. If two groups use similar shapes at small size they are hard to tell apart, so raise size and, for filled shapes, stroke before concluding that the groups overlap.
Troubleshooting
Why does fill do nothing on my points?
Because the shape does not support fill. Only shapes 21 to 25 use fill; shapes 0 to 20 are coloured with colour only. Switch to a shape from 21 to 25 or set colour instead.
Why are some groups missing from my plot?
You most likely have more than six levels mapped to shape. Supply a value for every level with scale_shape_manual().
Why is my shape argument ignored or turned into a legend entry?
A value written inside aes() is treated as data to map, not as a symbol. To apply one shape to all points, write it outside aes(), as in step 1.
Why do I see no points at all?
shape = NA draws nothing by design.
Free alternatives in base R (table)
Base R graphics has its own point-symbol argument, pch. The numbering overlaps with ggplot2 but is not identical in every respect.
| Feature | ggplot2 shape |
Base R pch (?points) |
|---|---|---|
| Integer symbols | 0 to 25 | 0 to 25 (0 to 18 are S-compatible vector symbols, 19 to 25 further R vector symbols) |
| Fill colour | fill, shapes 21 to 25 only |
bg, for pch 21 to 25; col gives the border colour |
| Other integers | Not described as symbols in the ggplot2 specifications | 26 to 31 unused and ignored; 32 to 127 are ASCII characters |
| Smallest point | ".", the smallest visible rectangle, usually 1 pixel |
"." (or 46), a 0.01-inch rectangle scaled by cex |
| No symbol | NA |
NA_integer_ |
A base R equivalent of the filled-shape example above is plot(mtcars$wt, mtcars$mpg, pch = 21, col = "black", bg = "white", cex = 1.5). For other base R scatter-style displays see stripcharts in R, and for a wider tool comparison see SPSS vs R.
FAQ
What shape values does ggplot2 accept?
Integers 0 to 25, shape names, a single character, "." and NA.
Which shapes can be filled?
Only 21 to 25. Colour applies to all shapes.
How do I make a point bigger including its outline?
Increase size and stroke; the total point size is their sum, in mm.
How many shapes can ggplot2 map automatically?
Six. Beyond that, use scale_shape_manual(). Related: adding titles to a ggplot, box plots in R.
Sources (official docs)
- ggplot2 aesthetic specifications: ggplot2.tidyverse.org/articles/ggplot2-specs.html (date checked: 2026-10-29)
- ggplot2
scale_shape()reference: ggplot2.tidyverse.org/reference/scale_shape.html (date checked: 2026-10-29) - R graphics
?points: stat.ethz.ch/R-manual/R-devel/library/graphics/html/points.html (date checked: 2026-10-29)







