Menu

Tutorial7 min read

How to Add a Secondary Y-Axis in ggplot2 (sec_axis)

By Francesco Villasmunta
How to Add a Secondary Y-Axis in ggplot2 (sec_axis)

Secondary y-axes can be controversial in data visualization because they can obscure scaling ratios. However, displaying dual variables over time (such as Temperature and Precipitation) is a common requirement. In ggplot2, secondary axes must be defined as a direct mathematical transformation of the primary axis.

In This Tutorial

0.Live Code: Dual Axis Plot

1.The Philosophy of Dual Axes

2.Defining a Secondary Scale (sec_axis)

3.Correct Scaling Transformations

4.Alternative Designs (Panel Facets)

0. Live Code: Dual Axis Plot

Process tracking. Customize parameters using Python below, or upload your data to run R directly.

1. The Philosophy of Dual Axes in ggplot2

Unlike matplotlib (which allows arbitrary scale overlay using `twinx()`), ggplot2 strictly forces the secondary axis to be a transformation of the primary axis (e.g. multiplying by a constant scale factor). This maintains the underlying coordinate layout integrity.

2. Defining a Secondary Scale (sec_axis)

Try it

Try it now: turn this method into your next figure

Apply the same approach to your own dataset and generate clean, publication-ready code and plots in minutes.

Open in Plotivy Analyze

Newsletter

Get a weekly Python plotting tip

One concise tip each week for cleaner, faster scientific figures. Built for researchers who publish.

No spam. Unsubscribe anytime.

Map the primary variable. Then define `sec.axis` inside the primary scale function, indicating the mathematical formula:

R / ggplot2

# Secondary axis mapping (e.g. Celsius to Fahrenheit: * 1.8 + 32)
ggplot(df, aes(x = time, y = temp_celsius)) +
  geom_line() +
  scale_y_continuous(
    name = "Celsius (°C)",
    sec.axis = sec_axis(trans = ~ . * 1.8 + 32, name = "Fahrenheit (°F)")
  )

3. Correct Scaling Transformations

To plot two completely different units (e.g. Temperature 0–40 and Rain 0–200), transform the rain column values in the `geom` layer mapping, and apply the inverse scale transformation in `sec_axis`:

R / ggplot2

# Rain scale is 5x Temp scale. Shrink rain values to fit on temp axis.
ggplot(df, aes(x = day)) +
  geom_line(aes(y = temperature, color = "Temp")) +
  geom_col(aes(y = precipitation / 5, fill = "Rain"), alpha = 0.3) +
  scale_y_continuous(
    name = "Temperature (°C)",
    sec.axis = sec_axis(trans = ~ . * 5, name = "Precipitation (mm)")
  )

4. Alternative Designs: Panel Facets

If axes scaling represents completely unrelated entities, consider using `facet_wrap(..., scales = "free_y")` to stack plots vertically. This is often cleaner and avoids scaling confusion.

Chart gallery

Explore related formats

Review dual formats.

Browse all chart types →
Multi-line graph showing temperature trends for 3 cities over a year
Time Seriesmatplotlib, seaborn
From the chart galleryStock price tracking over time

Line Graph

Displays data points connected by straight line segments to show trends over time.

Sample code / prompt

import matplotlib.pyplot as plt
import numpy as np

# Generate temperature data for 3 major US cities over 12 months
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
nyc = [30, 32, 40, 52, 65, 75, 82, 81, 74, 63, 50, 38]
miami = [65, 66, 70, 76, 82, 87, 90, 90, 87, 80, 72, 66]
chicago = [25, 27, 35, 48, 62, 72, 80, 79, 71, 60, 45, 32]

# Create figure with enhanced styling
Bar chart comparing average scores across 5 groups with error bars
Comparisonmatplotlib, seaborn
From the chart galleryComparing performance across categories

Bar Chart

Compares categorical data using rectangular bars with heights proportional to values.

Sample code / prompt

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats

# Generate performance scores for 5 treatment groups
np.random.seed(42)
groups = ['Control', 'Treatment A', 'Treatment B', 'Treatment C', 'Treatment D']
n_samples = 30

Build This Dual Axis Plot Online

Upload your data and describe the design. Plotivy writes the ggplot2 code and executes it instantly.

Use R Analyzer
Tags:#R#ggplot2#secondary y axis#sec_axis#dual axis

Found this helpful? Share it with your network.

FV
Francesco Villasmunta

Experimental Physicist & Photonics Researcher

Hands-on experience in silicon photonics, semiconductor fabrication (DRIE/ICP-RIE), optical simulation, and data-driven analysis. Built Plotivy to help researchers focus on discoveries instead of data struggles.

More about the author

Visualize your own data

Apply the techniques from this article to your own datasets. Upload CSV, Excel, or paste data directly.

Start Analyzing - Free