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. Once the data order is correct, you can add colour mappings to draw multiple lines in a single plot, with each unique value of the grouping variable producing its own coloured trajectory across the same coordinate space.

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. Beyond mapping colour to a data variable, you have full control over the visual style of each line — its colour as a literal value, its thickness measured in millimetres, and its pattern such as dashed or dotted — all set directly inside the geom_line() call.

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. When your dataset contains multiple series that operate on very different scales — for instance stock prices for companies with vastly different market capitalisations — overlaying them on a single panel can make the smaller series invisible. Faceting splits the data into separate small-multiple panels, each with its own y-axis range, so every series gets equal visual weight.

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. In some plots the direction of the line — horizontal versus vertical — is determined automatically by ggplot2, but when your data has a categorical variable on one axis you may need to give the geom an explicit hint about which dimension defines the grouping.

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. While the orientation parameter helps ggplot2 interpret which way your line should run, a separate but related distinction is whether the line respects the order of rows in your data frame or follows the visual path traced across the plotting surface. This matters most when you are working with spatial data, GPS tracks, or any geometry where the drawing order on screen has meaning.

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