Skip to main content
v2026.11,858 entries · CC-BY 4.0
LAC HealthMedical & Research SuppliesReagents, PPE & instruments — chain-of-custody documented.Fast, traceable sourcing built for regulated research environments, from bench consumables to instrumentation.Shop lac.us CodeCASRAIlac.us

ggplot Point Shapes: Numbers 0-25, Fill and Colour

ggplot point shapes: the shape values ggplot2 accepts, why fill only works on shapes 21-25, and how to map or set shapes with scale_shape_manual().

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:

  1. 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).
  2. 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".
  3. A single character. That character is used as the symbol.
  4. ".". The smallest rectangle that is visible, usually one pixel.
  5. 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

  1. Pass shape outside aes():
    ggplot(mtcars, aes(wt, mpg)) +
      geom_point(shape = 17, size = 3)

2. Fill vs colour (shapes 21 to 25)

  1. Use a fillable shape and set both colours. Per the ggplot2 specifications, colour applies to all shapes while fill applies only to shapes 21 to 25:
    ggplot(mtcars, aes(wt, mpg)) +
      geom_point(shape = 21, colour = "black", fill = "white",
                 size = 3, stroke = 1.5)
  2. Control size with two arguments. “The size of the filled part is controlled by size, the size of the stroke is controlled by stroke. Each is measured in mm, and the total size of the point is the sum of the two.”

3. Map shape to a variable

  1. Put shape inside aes(), using a factor so ggplot2 treats it as discrete:
    ggplot(mtcars, aes(wt, mpg, shape = factor(cyl))) +
      geom_point(size = 3)
  2. Rename the legend with scale_shape(name = "Cylinders"). Use scale_shape(solid = FALSE) to switch the automatic palette to hollow shapes (solid asks whether shapes should be solid, TRUE, or hollow, FALSE).

4. Choose the shapes yourself

  1. 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)

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about ggplot Point Shapes: Numbers 0-25, Fill and Colour

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →