ggplot2 scale_x_discrete() in R: Customize Discrete X Axis
The scale_x_discrete() function in ggplot2 customizes the X axis when x is a factor or character vector. It sets the order, labels, and gaps between categories.
+ scale_x_discrete(limits = c("Q1","Q2","Q3","Q4"))
+ scale_x_discrete(labels = c(a = "Alpha", b = "Beta"))
+ scale_x_discrete(breaks = c("a","c")) # show only these
+ scale_x_continuous() # different: numeric x
forcats::fct_relevel(...) # alternative: reorder factorNeed explanation? Read on for examples and pitfalls.
What scale_x_discrete() does in one sentence
scale_x_discrete() controls the X axis on plots where x is a factor or character: order, labels, breaks, and expansion.
Syntax
scale_x_discrete(name = waiver(), breaks = waiver(), labels = waiver(), limits = NULL, expand = waiver(), ...).
limits to reorder categories; use labels to rename them. Both accept named or positional vectors.Five common patterns
1. Reorder
2. Rename labels
3. Drop categories
4. Combine with forcats
5. Wide labels with rotation
forcats::fct_reorder on the data; for explicit reordering, use limits in scale_x_discrete. Both work; forcats is cleaner for data-driven order.scale_x_discrete() vs forcats::fct_reorder vs scale_x_continuous
| Approach | Best for |
|---|---|
scale_x_discrete(limits = ...) |
Manual ordering |
forcats::fct_reorder(x, by) |
Data-driven ordering |
scale_x_continuous() |
Numeric x |
coord_flip() |
Long labels (rotate plot) |
A practical workflow
For bar charts ordered by value, the cleanest pattern uses fct_reorder outside the plot.
Common pitfalls
Pitfall 1: limits drops categories. If your data has c("a","b","c") and limits is c("a","c"), b's data is dropped from the plot.
Pitfall 2: labels argument shape. Pass a named vector for safe mapping: labels = c(a = "Alpha", b = "Beta"). Positional works but is fragile.
scale_x_discrete order is determined by limits, FACTOR LEVELS, or alphabetical (last fallback). Always verify your factor's levels are what you expect.Try it yourself
Try it: Reorder mtcars cyl bars to be 8, 6, 4 (descending). Save to ex_plot.
Click to reveal solution
Explanation: limits sets the order explicitly.
Related ggplot2 / forcats functions
After mastering scale_x_discrete, look at:
scale_y_discrete(): same for yscale_x_continuous(): numeric xforcats::fct_reorder()/fct_infreq(): data-driven reordercoord_flip(): rotate to horizontaltheme(axis.text.x = ...): axis text styling
FAQ
What does scale_x_discrete do in ggplot2?
scale_x_discrete() customizes the X axis when x is a factor or character: order, labels, breaks.
How do I reorder bars in ggplot2?
Either scale_x_discrete(limits = c(...)) for manual order, or forcats::fct_reorder(x, value) for data-driven order before the plot.
How do I rename axis categories?
scale_x_discrete(labels = c(old = "New")). Use a named vector to avoid positional ambiguity.
Can I drop a category from the plot?
Yes. Omit it from limits. Note: this drops the data, not just the visual.
What is the difference between scale_x_discrete and scale_x_continuous?
discrete is for factors/characters (categorical). continuous is for numeric x.