ggplot2 scale_y_continuous() in R: Customize the Y Axis

The scale_y_continuous() function in ggplot2 customizes the Y axis on plots with a continuous (numeric) y mapping. It is the y-axis sister of scale_x_continuous().

⚡ Quick Answer
+ scale_y_continuous(breaks = seq(0, 100, 10))
+ scale_y_continuous(labels = scales::dollar)
+ scale_y_continuous(limits = c(0, 50))
+ scale_y_continuous(trans = "log10")
+ scale_y_continuous(name = "Revenue ($)")

Need explanation? Read on for examples and pitfalls.

📊 Is scale_y_continuous() the right tool?
STARTnumeric y axis customizationscale_y_continuous()log-scale yscale_y_log10()discrete yscale_y_discrete()date yscale_y_date()secondary axisscale_y_continuous(sec.axis = ...)

What scale_y_continuous() does in one sentence

scale_y_continuous() controls the Y axis: breaks (tick positions), labels (tick text), limits (range), transformations. Identical to scale_x_continuous but for y.

Syntax

scale_y_continuous(name = waiver(), breaks = waiver(), labels = waiver(), limits = NULL, trans = "identity", sec.axis = waiver(), ...).

Run live
Run live, no install needed. Every R block on this page runs in your browser. Click Run, edit the code, re-run instantly. No setup.
RDollar-formatted y axis
library(ggplot2) library(scales) ggplot(economics, aes(date, unemploy)) + geom_line() + scale_y_continuous( breaks = seq(0, 16000, 4000), labels = comma )

  
Tip
Use scales::dollar / scales::comma / scales::percent for common label formats. They're functions, not strings.

Five common patterns

1. Custom breaks

RTick every 1000
+ scale_y_continuous(breaks = seq(0, 10000, 1000))

  

2. Dollar-formatted labels

RMoney axis
+ scale_y_continuous(labels = scales::dollar)

  

3. Percent labels

RProportion to %
+ scale_y_continuous(labels = scales::percent)

  

4. Log scale

RLog y
+ scale_y_log10(labels = scales::comma)

  

5. Secondary y axis

RTwo y scales
+ scale_y_continuous( name = "Primary (count)", sec.axis = sec_axis(~ . / 100, name = "Secondary (%)") )

  
Key Insight
For a secondary axis, both must be linearly related. ggplot intentionally restricts dual y-axes to discourage misleading visualizations.

scale_y_continuous() vs scale_y_log10() vs coord_cartesian

Function Best for
scale_y_continuous() General y customization
scale_y_log10() Shortcut for log y
scale_y_sqrt() Square-root y
coord_cartesian(ylim) Zoom without dropping data

A practical workflow

Combine breaks, labels, and trans for production-ready axes.

RInteractive R
ggplot(sales, aes(month, revenue)) + geom_col() + scale_y_continuous( name = "Revenue (USD)", breaks = seq(0, 100000, 20000), labels = scales::dollar, expand = expansion(mult = c(0, 0.1)) )

  

Dollar formatting + breaks + tight bottom expansion.

Common pitfalls

Pitfall 1: limits drops data. Use coord_cartesian(ylim = c(0, 100)) to zoom without dropping.

Pitfall 2: dual axes can mislead. ggplot's sec.axis allows it but only with a linear transformation; don't combine unrelated scales.

Warning
scale_y_continuous(limits = ...) DROPS data outside; coord_cartesian(ylim = ...) ZOOMS only. Use coord_cartesian when stats need the full data.

Try it yourself

Try it: Plot mtcars wt vs hp with y axis log-transformed and comma labels. Save to ex_plot.

RYour turn: log y with commas
library(scales) ex_plot <- mtcars |> ggplot(aes(wt, hp)) + geom_point() + # your code here

  
Click to reveal solution
RSolution
ex_plot <- ggplot(mtcars, aes(wt, hp)) + geom_point() + scale_y_log10(labels = comma)

  

Explanation: scale_y_log10 with comma formatting.

After mastering scale_y_continuous, look at:

  • scale_x_continuous(): same for x
  • scale_y_log10() / scale_y_sqrt(): shortcuts
  • scale_y_date(): time-axis y
  • scale_y_discrete(): discrete y
  • coord_cartesian(ylim): zoom only
  • expand / expansion(): control axis padding

FAQ

What does scale_y_continuous do in ggplot2?

scale_y_continuous() customizes the Y axis when y is continuous numeric: breaks, labels, limits, transformations.

How do I add a secondary y axis?

Pass sec.axis = sec_axis(~ . / 100, name = "..."). The transformation must be linear.

What is the difference between scale_y_continuous limits and coord_cartesian?

limits drops data outside. coord_cartesian zooms only. Use coord_cartesian for stats integrity.

How do I format y as dollars?

scale_y_continuous(labels = scales::dollar). Use comma, percent similarly.

Can I log-transform with scale_y_continuous?

Yes: scale_y_continuous(trans = "log10"). Or use scale_y_log10() as a shortcut.