rguides

ggplot2::geom_line()

geom_line(mapping = NULL, data = NULL, stat = "identity", position = "identity", ...)

geom_line() connects data points in the order they appear in the data with a line. It’s the standard choice for time series, sequential measurements, and anywhere the x-axis represents time or a meaningful sequence rather than independent categories.

Syntax

geom_line(mapping = NULL, data = NULL, stat = "identity", position = "identity", ...)
ArgumentWhat it does
mappingAesthetic mappings from aes()
dataData frame for this layer
statAlmost always "identity" — leave data as-is
positionUsually "identity"
na.rmRemove missing values silently
orientation"x" (default), "y", or NA (auto-detect)

Basic Usage

library(ggplot2)

# Simple line plot
ggplot(economics, aes(x = date, y = psavert)) +
  geom_line()

The x-axis is the date and the y-axis is personal savings rate. The line shows the trend over time. economics is a built-in ggplot2 dataset with US economic time series data.

Why Order Matters

geom_line() connects points in the order they appear in the data. This is different from geom_path(), which connects them in the order they appear on the screen. If your data is not sorted by the x-axis variable, the line will zigzag incorrectly:

# Sort first — otherwise the line jumps all over the place
ggplot(economics, aes(x = date, y = psavert)) +
  geom_line() +
  geom_point()  # shows the actual data positions

Always sort your data by the x-axis variable before plotting lines if the data comes from an unsorted source.

Mapping Colour to a Group

For multiple lines (e.g., different categories over time), map colour to the grouping variable:

# Multiple time series coloured by category
ggplot(peaks, aes(x = year, y = elevation, colour = peak)) +
  geom_line()

This draws one line per unique colour value, all on the same axes. ggplot2 automatically splits and colours the lines.

Line Aesthetic Options

Control the appearance with standard aesthetics:

ggplot(economics, aes(x = date, y = psavert)) +
  geom_line(
    colour = "steelblue",
    linewidth = 0.8,
    linetype = "solid"
  )
  • linewidth (or deprecated size) controls thickness in mm
  • linetype accepts: "solid", "dashed", "dotted", "dotdash", "longdash", "twodash"
  • colour accepts colour names, hex codes, or mapped variables

Combining with Points

Often you want both — line for the trend, points for the individual observations:

ggplot(economics, aes(x = date, y = psavert)) +
  geom_line() +
  geom_point(size = 0.8, alpha = 0.5)

Small points with some transparency let you see both the line and the raw data without clutter.

Multiple Series with Facets

When you have multiple series that shouldn’t share a y-axis, facet_wrap() is cleaner than overlaying many coloured lines:

ggplot(gafa_stock, aes(x = Date, y = Close)) +
  geom_line() +
  facet_wrap(~ Symbol, scales = "free_y")

Each stock gets its own panel with its own y-axis scale.

Handling Missing Data

geom_line() has na.rm = FALSE by default — it breaks the line at missing values rather than interpolating. This is usually what you want for time series, but be aware:

# Line breaks at NA values
df <- data.frame(
  x = 1:5,
  y = c(1, 2, NA, 4, 5)
)
ggplot(df, aes(x, y)) + geom_line()
# Line goes 1 -> 2, breaks, resumes 4 -> 5

Use geom_line(na.rm = TRUE) to silently skip missing values and connect across them.

Orientation

When the x-axis is categorical or the data orientation is ambiguous, set orientation:

# Horizontal line plot
ggplot(mtcars, aes(y = mpg)) +
  geom_line(group = 1)  # one line across all cars

Or orientation = "y" to treat the y variable as the grouping axis.

Line vs Path

geom_line() connects points in data order. geom_path() connects them in the order they appear on the plot (as if you drew the line on screen):

# geom_line: x-axis order determines connection
ggplot(df, aes(x = x, y = y)) + geom_line()

# geom_path: follows the actual path drawn on screen
# Useful for directional data, GPS tracks, etc.
ggplot(gps_data, aes(x = lon, y = lat)) + geom_path()

For time series, geom_line() is almost always what you want.

Parameters

ParameterTypeDefaultDescription
mappingaestheticNULLAesthetic mappings
datadata.frameNULLLayer data
statstring"identity"Leave as-is
positionstring/position"identity"Position adjustment
na.rmlogicalFALSESkip missing values silently
orientationstringNA"x", "y", or NA for auto
show.legendlogical/NANAShow in legend
inherit.aeslogicalTRUEInherit global aesthetics

See Also