rguides

seq()

seq(from = 1, to = 1, by = ((to - from)/(length.out - 1)), length.out = NULL, along.with = NULL)

seq() creates sequences of numbers with precise control over start, end, length, and step size. It’s essential for looping, creating index vectors, and generating test data.

Syntax

seq(from = 1, to = 1, by = ((to - from)/(length.out - 1)), length.out = NULL, along.with = NULL)

Parameters

ParameterTypeDefaultDescription
fromnumeric1Starting value of the sequence
tonumeric1Ending value (inclusive)
bynumeric(to-from)/(length.out-1)Step size (increment)
length.outintegerNULLDesired length of the sequence
along.withvectorNULLTake length from this object

Examples

Basic sequence from 1 to 10

seq(1, 10)
#  [1]  1  2  3  4  5  6  7  8  9 10

Sequence with custom increment

When you need equal-sized steps between values, using by lets you set the exact gap. This is particularly useful for generating bin boundaries in histograms, creating evenly spaced tick marks on plots, or iterating over numeric ranges where the step size matters more than the total count of values. R computes the number of elements automatically from from, to, and by.

seq(0, 100, by = 10)
#  [1]   0  10  20  30  40  50  60  70  80  90 100

seq(1, 9, by = 2)
# [1] 1 3 5 7 9

Sequence with specified length

When the total number of points matters more than the step size, length.out gives you direct control over how many values the sequence contains. This is the safer choice for creating grid points in numerical integration, generating evenly spaced interpolation knots, or producing axis labels where you need exactly N ticks regardless of the numeric range. R divides the interval into length.out equal segments, guaranteeing both endpoints appear in the output.

seq(0, 1, length.out = 5)
# [1] 0.00 0.25 0.50 0.75 1.00

seq(0, 10, length.out = 3)
# [1]  0  5 10

Using along.with to match another vector’s length

Rather than computing the desired length manually, along.with takes it directly from an existing vector, which keeps your code DRY when the sequence length must match some data you already have. Common uses include generating row indices for a data frame, creating position markers for a set of labels, or producing match indices alongside a vector of values that may change size across runs.

x <- c("a", "b", "c", "d")
seq(along.with = x)
# [1] 1 2 3 4

seq(10, 50, along.with = x)
# [1] 10 23 36 50

Negative sequences

Descending sequences require an explicit negative by value because seq() defaults to incrementing upward. When from exceeds to but by remains positive, seq() returns an empty sequence rather than guessing direction. The length.out variant works for both ascending and descending ranges, which makes it easier to write general-purpose code that handles either case without conditional logic on the sign of by.

seq(10, 1, by = -1)
#  [1] 10  9  8  7  6  5  4  3  2  1

seq(5, -5, length.out = 11)
#  [1]  5  4  3  2  1  0 -1 -2 -3 -4 -5

Fractional sequences

Fractional steps are useful for generating probability thresholds, quantile levels, or any domain where values lie between 0 and 1. Because floating-point arithmetic can introduce slight endpoint drift, seq(0, 1, by = 0.1) might return either 10 or 11 values depending on how the last computed step compares to to. When you need both endpoints guaranteed in the output, prefer length.out over by for fractional ranges.

seq(0, 1, by = 0.1)
#  [1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

Common patterns

Creating index vectors for loops

Beyond standalone sequences, seq_along() and seq_len() are the standard tools for loop indexing in R. They avoid the classic 1:length(x) bug where an empty x produces c(1, 0) instead of an empty integer vector. Writing for (i in seq_along(df$col)) ensures the loop body runs zero times when the column is empty, rather than twice with invalid indices. This defensive pattern appears throughout well-tested R package code.

n <- 5
for (i in seq_len(n)) {
  print(i)
}
# [1] 1
# [1] 2
# [1] 3
# [1] 4
# [1] 5

Generating test data frames

Sequences also simplify generating synthetic data for testing and prototyping. By combining seq() with other vectorized operations like exponentiation or sampling, you can build structured data frames without importing external datasets. The seq() output forms predictable ID columns or evenly spaced numeric ranges, while transformations applied to those sequences create realistic-looking patterns that exercise your analysis pipeline before real data arrives.

df <- data.frame(
  id = seq(1, 100),
  value = seq(0, 99) ^ 2
)
head(df)
#   id value
# 1  1     0
# 2  2     1
# 3  3     4
# 4  4     9
# 5  5    16
# 6  6    25

Creating time-based sequences

Date sequences rely on S3 method dispatch: seq() calls seq.Date() when passed Date objects, interpreting the by argument as a time interval string. Supported intervals include "day", "week", "month", "quarter", and "year", plus multiples like "3 days" or "2 weeks". This makes seq.Date() a concise way to build calendars, time series axes, or scheduling tables where you need the first day of each month.

# Daily sequence for a week
seq(as.Date("2026-01-01"), as.Date("2026-01-07"), by = "day")
# [1] "2026-01-01" "2026-01-02" "2026-01-03" "2026-01-04" "2026-01-05"
# [6] "2026-01-06" "2026-01-07"

Reproducible sequences with set.seed

Sequences are deterministic by nature, but combining them with random number generation using set.seed() makes your results reproducible across sessions. Setting the seed before calling sample() alongside seq_len() ensures that other researchers or your future self can regenerate identical output. This combination is common in simulations, bootstrapping, and Monte Carlo studies where you need a fixed scaffold of indices combined with reproducible random variation.

set.seed(42)
seq_len(5) + sample(0:10, 5)
# [1]  3  8 12  7 11

seq() in practice

seq() has three main calling conventions: seq(from, to, by) for step sequences, seq(from, to, length.out) for evenly-spaced sequences of a specific length, and seq_len(n) for the common 1:n case. Each produces a different kind of sequence with different guarantees.

seq(0, 1, by = 0.1) does not always return exactly 11 values due to floating-point arithmetic, the last value may be slightly above or below 1. Use seq(0, 1, length.out = 11) when you need exactly 11 evenly-spaced values including both endpoints. The length.out form guarantees the count and endpoint, while the by form guarantees the step size but may produce a variable count.

seq_len(n) and seq_along(x) are safer alternatives to 1:n and 1:length(x) in for loops. 1:length(x) fails when x is empty because 1:0 gives c(1, 0) rather than an empty sequence, causing the loop to execute twice with invalid indices. seq_along(x) returns integer(0) when x is empty, skipping the loop entirely. This is a common source of subtle bugs in defensive code.

seq.Date() generates sequences of dates: seq(as.Date("2024-01-01"), as.Date("2024-12-31"), by = "month") gives the first day of each month. The by argument accepts “day”, “week”, “month”, “quarter”, and “year” as string values.

seq() is the general sequence generator. seq_len(n) generates 1:n and is safer when n might be zero — 1:0 gives c(1, 0), but seq_len(0) gives integer(0). seq_along(x) generates a sequence of the same length as x, equivalent to seq_len(length(x)).

The by and length.out arguments are mutually exclusive: use by when you know the step size, and length.out when you know the number of points. seq(0, 1, length.out = 11) generates 11 evenly spaced points from 0 to 1 inclusive.

When from and to are doubles, seq() can produce rounding artifacts near the endpoints. If you need exact endpoints, use seq_len() with manual scaling: seq_len(n) / n for fractions.

seq.Date() and seq.POSIXt() extend seq() to date and datetime objects, accepting by as a string like "day", "month", "year", or a difftime object.

See also

  • c()
  • rep()
  • sample() seq.int() is a faster primitive version of seq() for integer sequences with simple from/to/by arguments.