rguides

range()

range(..., na.rm = FALSE)

The range() function returns a numeric vector of length two containing the minimum and maximum values of the input. It works on numeric vectors, integers, and character vectors (using alphabetical ordering).

Syntax

range(x, na.rm = FALSE, ...)

Parameters

ParameterTypeDefaultDescription
xnumeric, integer, or character,A vector for which to find the min and max
na.rmlogicalFALSEIf TRUE, remove NA values before computing
...additional arguments,Optional arguments passed to min() or max()

Examples

Basic usage

# Numeric vector
x <- c(3, 1, 4, 1, 5, 9, 2, 6)
range(x)
# [1] 1 9

range() makes a single pass over the data to compute both the minimum and maximum simultaneously. This is more efficient than calling min(x) and max(x) separately, which would scan the vector twice. For very large vectors where performance matters — such as streaming sensor data or genomic sequences with millions of entries — using range() instead of separate min() and max() calls cuts the number of comparisons roughly in half.

With NA values

# Without removing NAs (default)
y <- c(1, 2, NA, 4, 5)
range(y)
# [1] NA NA

# With na.rm = TRUE
range(y, na.rm = TRUE)
# [1] 1 5

When na.rm = FALSE (the default), any NA in the input causes range() to return c(NA, NA). This behavior is consistent with min() and max() and follows R’s convention of propagating missing values. Setting na.rm = TRUE strips NA values before computing the range, which is almost always what you want when working with real-world datasets that contain incomplete observations.

Character vectors

# Strings are sorted alphabetically
chars <- c("banana", "apple", "cherry", "date")
range(chars)
# [1] "apple"  "date"

When applied to character vectors, range() uses lexicographic ordering based on the current locale’s collation rules. This means the result depends on your system’s language settings — "Ä" may sort differently on a German locale than on an English one. For locale-independent string ordering, use sort() with method = "radix" or convert to factors with explicit level ordering before calling range().

Common patterns

Checking if values fall within a range

x <- c(1, 5, 10, 15, 20)
r <- range(x)
x >= r[1] & x <= r[2]
# [1] TRUE TRUE TRUE TRUE TRUE

The expression x >= r[1] & x <= r[2] creates a logical vector indicating which values fall within the observed range. Since every value in x is by definition within its own range(), the result is all TRUE for the original dataset. This pattern becomes useful when checking whether new observations or predicted values fall within the training data range — a quick validity check before extrapolating a model beyond its support.

Scaling data using range

# Normalize to 0-1 scale
x <- c(10, 20, 30, 40, 50)
r <- range(x)
(x - r[1]) / (r[2] - r[1])
# [1] 0.00 0.25 0.50 0.75 1.00

range() in practice

range() returns c(min(x), max(x)) in a single call, which is convenient when you need both bounds. It is equivalent to calling min() and max() separately but makes one pass over the data. diff(range(x)) computes the range (as a scalar measure of spread), which is also available as max(x) - min(x).

A common use case: setting axis limits in base R plots. xlim = range(x) sets the plot boundaries to exactly the data range. Adding a small buffer: range(x) + c(-1, 1) * 0.05 * diff(range(x)) extends the range by 5% on each side, a common pattern for readable axis margins.

range() accepts multiple vectors: range(x, y, z) returns the global minimum and maximum across all inputs. With na.rm = TRUE, it ignores missing values. range() on an empty vector returns c(Inf, -Inf), which is the convention for “no data”, the minimum starts at Inf (so any actual minimum will be smaller) and the maximum at -Inf.

For normalized scaling, (x - min(x)) / diff(range(x)) maps x to the range [0, 1]. This is min-max normalization, and range() provides both bounds in one call, making the code slightly more readable than calling min() and max() separately.

range() returns a length-2 vector c(min(x), max(x)). It is equivalent to calling both min() and max() but in one pass, making it slightly more efficient. The na.rm argument works the same as in min() and max(). diff(range(x)) computes the total range (spread) of a numeric vector. For ggplot2, scale_x_continuous(limits = range(x)) uses the range to set axis limits.

Setting axis limits with padding

# Simulated temperature data for a week
temps <- c(18.2, 19.5, 22.1, 25.3, 24.8, 21.0, 19.7)

# Compute range and add 10% padding
r <- range(temps)
pad <- 0.1 * diff(r)
padded_range <- c(r[1] - pad, r[2] + pad)
padded_range
# [1] 17.49 26.01

# Use in a base R plot
plot(temps, type = "b", xlab = "Day", ylab = "Temperature (°C)",
     ylim = padded_range, main = "Weekly Temperature")

# The built-in extendrange() does the same thing
extendrange(temps, f = 0.10)
# [1] 17.49 26.01

Adding padding to axis limits prevents data points from sitting exactly on the plot boundary, where they are hard to see and where axis labels can overlap with the outermost tick marks. The extendrange() function from the grDevices package automates this calculation, but the manual approach with diff(range(x)) makes the padding formula transparent. For ggplot2 users, expand_limits() or scale_y_continuous(expand = expansion(mult = 0.1)) achieves the same visual spacing without manual range arithmetic.

See also