The 'lag' Function in R: A Comprehensive Guide

R Published Updated 2 mins read By Leon Wei
The 'lag' Function in R: A Comprehensive Guide cover image

Quick summary

Summarize this blog with AI

Introduction

This guide uses dplyr::lag() to shift observed values by a number of positions. It is not the same interface as stats::lag(), which shifts a time-series time base. Neither operation automatically fills missing dates or imputes missing measurements.

Tested with R 4.5.1 and dplyr 1.1.4. Run the following code blocks in order. All inputs are included.

Shift a vector and keep its length

Use the namespace explicitly so an attached package cannot change which lag function you call.

x <- c(10, 20, NA_real_, 40)
previous <- dplyr::lag(x)
print(previous)  # NA 10 20 NA
stopifnot(identical(previous, c(NA_real_, 10, 20, NA_real_)))
stopifnot(identical(dplyr::lag(x, n = 2), c(NA_real_, NA_real_, 10, 20)))
stopifnot(identical(dplyr::lag(x, default = 0), c(0, 10, 20, NA_real_)))

The default pads newly exposed positions only; it does not replace the missing measurement already in x. Zero padding is a deliberate policy and is inappropriate when “no prior observation” means unknown.

Sort within groups before computing change

An event ID breaks same-day ties. The fixture includes a date gap to expose the difference between the previous row and the previous calendar day.

sales <- data.frame(store = c("A", "B", "A", "B", "A"),
                    day = as.Date(c("2026-01-03", "2026-01-01", "2026-01-01", "2026-01-02", "2026-01-04")),
                    event = c(2L, 4L, 1L, 5L, 3L), amount = c(15, 0, 10, 5, 18))
ordered <- sales |>
  dplyr::arrange(store, day, event) |>
  dplyr::group_by(store) |>
  dplyr::mutate(previous_amount = dplyr::lag(amount),
                days_since_previous = as.integer(day - dplyr::lag(day)),
                change = amount - previous_amount) |>
  dplyr::ungroup()
print(as.data.frame(ordered))
stopifnot(identical(ordered$previous_amount, c(NA_real_, 10, 15, NA_real_, 0)))
stopifnot(identical(ordered$days_since_previous, c(NA_integer_, 2L, 1L, NA_integer_, 1L)))

For A on January 3, the previous observed amount is January 1’s 10, two days earlier. A one-day change is unavailable unless that is the intended previous-day record. A twelve-row lag is a year-over-year lag only when rows actually represent consecutive months without omissions.

Keep invalid growth denominators missing

Absolute change can be defined when the previous amount is zero; percentage change cannot divide by zero. Assign only where the denominator is present and nonzero.

ordered$percent_change <- NA_real_
valid <- !is.na(ordered$previous_amount) & ordered$previous_amount != 0
ordered$percent_change[valid] <- 100 * ordered$change[valid] / ordered$previous_amount[valid]
print(ordered$percent_change)  # NA 50 20 NA NA
stopifnot(isTRUE(all.equal(ordered$percent_change, c(NA_real_, 50, 20, NA_real_, NA_real_))))

The B row after a zero baseline has a five-unit increase but an undefined percentage increase. Filling its previous value from a future row would alter the question and could leak future information into a forecast.

Use order_by without assuming it sorts the output

order_by chooses the order for shifting but returns values aligned to the original rows.

days <- c(3, 1, 2)
amounts <- c(30, 10, 20)
print(dplyr::lag(amounts, order_by = days))  # 20 NA 10
stopifnot(identical(dplyr::lag(amounts, order_by = days), c(20, NA_real_, 10)))
stopifnot(length(dplyr::lag(numeric(0))) == 0)
stopifnot(identical(dplyr::lag(5), NA_real_))
stopifnot(all(is.na(dplyr::lag(c(1, 2), n = 5))))

Sort explicitly when several columns define chronology. For prediction, compute features only from records available at the prediction time and respect group boundaries. Lagging is alignment; filling is a separate analytical decision.

Primary references

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles