How to Use 'diff' Function in R

R Published Updated 2 mins read By Leon Wei
How to Use 'diff' Function in R cover image

Quick summary

Summarize this blog with AI

Introduction

diff() subtracts an earlier observation from a later one and returns a shorter result. The lag argument chooses their position gap; differences repeats the subtraction operation. Neither argument automatically measures calendar time, and na.rm is not a missing-value removal option for the default method.

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

Separate lag from repeated differences

Use a fixed sequence with visibly changing increments.

x <- c(10, 13, 19, 28)
print(diff(x))                  # 3 6 9
print(diff(x, lag = 2))         # 9 15
print(diff(x, differences = 2)) # 3 3
stopifnot(identical(diff(x), c(3, 6, 9)))
stopifnot(identical(diff(x, lag = 2), c(9, 15)))
stopifnot(identical(diff(x, differences = 2), c(3, 3)))

With lag 2, each result subtracts the value two positions earlier. A second difference instead differences the first-difference sequence. These are not interchangeable seasonal adjustments.

Align differences to their ending row

A difference belongs to a pair of observations. Retain timestamps so the interval remains visible.

daily <- data.frame(day = as.Date(c("2026-01-01", "2026-01-02", "2026-01-04", "2026-01-05")),
                    value = x)
daily$change <- c(NA_real_, diff(daily$value))
daily$elapsed_days <- c(NA_integer_, as.integer(diff(daily$day)))
print(daily)
stopifnot(identical(daily$elapsed_days, c(NA_integer_, 1L, 2L, 1L)))
stopifnot(identical(daily$change, c(NA_real_, 3, 6, 9)))

The six-unit change ending January 4 spans two days. Dividing by elapsed time produces an average rate over that interval, not an observed value for the missing date. Sort and separate independent entities before differencing.

Leave unknown differences visible

Removing a missing observation connects neighbors that were not originally adjacent. That is a different calculation, not a harmless cleanup.

with_na <- c(10, NA_real_, 19, 28)
print(diff(with_na))  # NA NA 9
stopifnot(identical(diff(with_na), c(NA_real_, NA_real_, 9)))
stopifnot(identical(diff(with_na, na.rm = TRUE), diff(with_na)))
print(diff(with_na[!is.na(with_na)]))  # 9 9: different adjacency
changes <- c(3, 0, -2, NA_real_)
direction <- ifelse(is.na(changes), NA_character_,
                    ifelse(changes > 0, "increase", ifelse(changes < 0, "decrease", "unchanged")))
stopifnot(identical(direction, c("increase", "unchanged", "decrease", NA_character_)))

The tested default method accepts extra arguments through ... but does not use na.rm to remove missing values. Use elementwise ifelse for a vector of direction labels; scalar if cannot classify several changes at once.

Test short vectors and matrix axes

For a numeric matrix, default diff works down rows within each column.

stopifnot(length(diff(numeric(0))) == 0, length(diff(10)) == 0)
stopifnot(length(diff(1:3, lag = 3)) == 0)
m <- cbind(a = c(1, 3, 6), b = c(10, 15, 21))
d <- diff(m)
print(d)  # rows: 2 5; 3 6
stopifnot(identical(dim(d), c(2L, 2L)), all(d[, "a"] == c(2, 3)))
err <- tryCatch(diff(c("a", "b")), error = function(e) e)
stopifnot(inherits(err, "error"))

For a time series object, method dispatch also preserves relevant time-series structure. State the class you are using and verify the output’s index and shape. See lagging without changing row count for a different alignment operation.

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