ggplot2::scale_color_manual
scale_colour_manual() lets you specify exactly which colour each group in your data gets. Unlike scale_colour_brewer() where you pick a palette and ggplot2 assigns colours, manual scales give you full control — you decide which value gets which colour.
Signature
scale_colour_manual(
...,
values,
aesthetics = "colour",
breaks = waiver(),
na.value = "grey50"
)
Parameters:
values— a vector of colours (named or unnamed)aesthetics— which aesthetic(s) to apply to (default"colour")breaks— which factor levels display in the legendna.value— colour for missing values
Returns: Scale object
For scale_fill_manual(), use aesthetics = "fill". Other manual scales: scale_size_manual(), scale_shape_manual(), scale_linetype_manual(), scale_alpha_manual(), scale_linewidth_manual().
Named Vector Mapping
Use a named vector so ggplot2 matches by name, not position:
cols <- c("4" = "#E41A1C", "6" = "#377EB8", "8" = "#4DAF4A")
ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
geom_point(size = 3) +
scale_colour_manual(values = cols)
With a named vector, "4" maps to "#E41A1C" regardless of alphabetical ordering. This approach is safer — the mapping stays correct even when some groups are absent from the data.
Unnamed Vector Mapping
Without names, ggplot2 matches by order of factor levels:
ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
geom_point(size = 3) +
scale_colour_manual(values = c("red", "blue", "green"))
levels(factor(mtcars$cyl)) is c("4", "6", "8"), so red maps to 4, blue to 6, green to 8. This breaks when any level is absent from the data.
Combining Colour and Fill
Use aesthetics = c("colour", "fill") for both aesthetics at once:
cols <- c("4" = "#E41A1C", "6" = "#377EB8", "8" = "#4DAF4A")
ggplot(mtc3ars, aes(x = mpg, fill = factor(cyl), colour = factor(cyl)) +
geom_point(shape = 21, size = 3, alpha = 0.6) +
scale_colour_manual(values = cols, aesthetics = c("colour", "fill"))
This avoids repeating values for each aesthetic.
Custom Legend Labels
Provide labels for the legend:
ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
geom_point(size = 3) +
scale_colour_manual(
values = cols,
labels = c("Four cylinder", "Six cylinder", "Eight cylinder")
)
Subsetting the Legend
breaks controls which levels appear in the legend without changing the data. limits controls which data values are included in the scale:
ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
geom_point(size = 3) +
scale_colour_manual(
values = c("4" = "#E41A1C", "6" = "#377EB8"),
breaks = c("4", "6"),
labels = c("Four", "Six")
)
breaks hides "8" from the legend without filtering the data. limits filters the data to include only those values.
Handling Missing Values
Missing values get na.value (default "grey50"):
ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
geom_point(size = 3) +
scale_colour_manual(values = cols, na.value = "black")
Programmatic Colour Generation
For many groups, generate colours with scales functions:
n_groups <- length(levels(factor(mtcars$cyl))
pal <- scales::seq_gradient_pal("low" = "#132B43", "high" = "#2C7FB8", space = "Lab")(seq(0, 1, length.out = n_groups)
ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
geom_point(size = 3) +
scale_colour_manual(values = pal)
For colourblind-safe palettes across many groups, use viridis or RColorBrewer:
# viridis palette
pal_vir <- viridis::viridis_palette(3, option = "D")
# c("4" = "#440154", "6" = "#21908D", "8" = "#FDE725")
ggplot(mtcars, aes(mpg, wt, colour = factor(cyl)) +
geom_point(size = 3) +
scale_colour_manual(values = pal_vir)
Other Manual Scales
The same pattern works for size, shape, linetype, alpha, and linewidth:
ggplot(mtcars, aes(mpg, wt, colour = factor(cyl), linetype = factor(cyl)) +
geom_line() +
scale_colour_manual(values = cols) +
scale_linetype_manual(values = c("solid", "dashed", "dotted"))
| Scale | Aesthetic | Typical values |
|---|---|---|
scale_size_manual() | size | numeric values |
scale_shape_manual() | shape | 0-25 integer codes |
scale_linetype_manual() | linetype | names or integers |
scale_alpha_manual() | alpha | 0-1 numeric |
scale_linewidth_manual() | linewidth | numeric |
Common Mistakes
Unnamed vectors break when groups are absent from the data. Named vectors are always safer.
Confusing breaks with limits. breaks filters legend entries; limits filters the data. Both commonly misused.
Duplicating values for colour and fill. Use aesthetics = c("colour", "fill") instead.
See Also
- /reference/tidyverse/ggplot2_aes/ — aesthetic mappings in ggplot2
- /reference/tidyverse/ggplot2_theme/ — non-data visual elements
- /tutorials/r-data-visualization/ggplot2-basics/ — ggplot2 foundations