min()
min(..., na.rm = FALSE, na.last = TRUE, type = c("ordinary", "ordered"))
min() returns the minimum value of its arguments. It is among the most frequently used functions for exploratory data analysis and statistical summaries.
Syntax
min(..., na.rm = FALSE, na.last = TRUE, type = c("ordinary", "ordered"))
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
... | numeric | , | One or more vectors, or atomic values. Multiple arguments are combined element-wise. |
na.rm | logical | FALSE | If TRUE, NA values are removed before computing the result. |
na.last | logical | TRUE | For na.rm = FALSE, controls where NA is placed in the output. |
type | character | "ordinary" | Either "ordinary" for standard comparison, or "ordered" for ordered factors. |
Examples
Basic usage
The simplest use of min() passes a single numeric vector and receives the smallest value in return. This is the starting point for most data exploration: after loading a dataset, running min(df$column) gives you an immediate sense of the lower bound. Unlike summary() which produces a full five-number summary, min() isolates one statistic so you can pipe it into further calculations or conditional logic without extracting it from a named vector.
x <- c(3, 1, 4, 1, 5, 9, 2, 6)
min(x)
# [1] 1
Multiple arguments
When you supply several arguments directly instead of a single vector, min() concatenates them internally and then finds the smallest value across the combined set. This syntax is convenient for quick comparisons — for example, checking whether a computed value falls below several thresholds — but it is not vectorized element-wise. For pairwise minimums across two equal-length vectors, you need pmin() instead, which returns a vector of the same length with the smaller value at each position.
# Compare multiple vectors or values
min(1, 2, 3, 4, 5)
# [1] 1
min(c(1, 2), c(3, 4), 5)
# [1] 1
Handling missing values
Missing data is a reality in R, and min() follows the conservative default of propagating uncertainty: if any element is NA, the result is NA unless you explicitly request removal. This behaviour mirrors how arithmetic operations treat missing values and prevents you from accidentally computing a minimum that ignores critical gaps in your data. Setting na.rm = TRUE tells R to drop the missing entries before comparison, which is safe once you have verified that the NA values represent genuinely absent data rather than errors worth investigating.
y <- c(1, 2, NA, 4, 5)
min(y)
# [1] NA
min(y, na.rm = TRUE)
# [1] 1
With different data types
R’s comparison operators work across atomic types, so min() extends naturally beyond numeric vectors. For character strings, the comparison follows locale-aware alphabetical ordering — “apple” comes before “banana” because R uses the collation rules of your system locale, which matters when working with non-ASCII text. For logical vectors, R treats TRUE as 1 and FALSE as 0 during coercion, so min() on a logical vector returns 0 unless every element is TRUE. This implicit coercion is reliable but worth knowing about when debugging unexpected results from mixed-type data.
# Character vectors (uses alphabetical order)
chars <- c("apple", "banana", "cherry")
min(chars)
# [1] "apple"
# Logical vectors (TRUE = 1, FALSE = 0)
min(c(TRUE, FALSE, TRUE, FALSE))
# [1] 0
Common patterns
Finding the range of a vector
The spread of a numeric vector is often as informative as its central tendency, and min() combined with max() gives you the range directly. While range() is a built-in convenience that returns c(min(x), max(x)) in one call, computing max(data) - min(data) manually expresses the span as a single number — the total width of the distribution — which is useful when you need to feed the range into a scaling formula or a conditional check rather than inspecting the endpoints separately.
data <- c(3, 1, 4, 1, 5, 9, 2, 6)
range_val <- range(data)
range_val
# [1] 1 9
max(data) - min(data)
# [1] 8
Identifying which element is min
Knowing the minimum value is often less useful than knowing where it occurs, especially when the vector is tied to metadata by position. which.min() returns the index of the first occurrence of the minimum value, and combining it with subsetting retrieves the actual element at that position. This pattern is especially common in row-wise operations on data frames: df[which.min(df$price), ] gives you the full row corresponding to the cheapest item, preserving all associated columns without manual extraction.
scores <- c(85, 92, 78, 91, 88)
which.min(scores)
# [1] 3
# Get the actual value
scores[which.min(scores)]
# [1] 78
Normalizing by scaling to 0-1
Min-max normalization remaps a vector onto the [0, 1] interval by subtracting the minimum and dividing by the range, a transformation that preserves the relative ordering of values while making them comparable across variables measured on different scales. This is a standard preprocessing step before feeding numeric features into distance-based algorithms such as k-nearest neighbours or k-means clustering, where variables with larger raw magnitudes would otherwise dominate the distance calculation purely because of their units rather than their informativeness.
values <- c(10, 20, 30, 40, 50)
# Scale to 0-1 range
scaled <- (values - min(values)) / (max(values) - min(values))
scaled
# [1] 0.00 0.25 0.50 0.75 1.00
min() in practice
min() returns the smallest value across all of its arguments, with the same behavior as max() in terms of NA handling, empty inputs, and vectorization. min() on an empty vector returns Inf (the identity for minimum), while max() on an empty vector returns -Inf. These conventions ensure that min(c(Inf, x)) always equals min(x).
For element-wise minimum of two vectors, use pmin(a, b) rather than min(a, b): pmin(c(1,5), c(3,2)) returns c(1,2). The parallel variants pmin() and pmax() are vectorized over position and return a vector of the same length as their inputs, while min() and max() return a scalar.
which.min(x) returns the index of the first minimum value. Combined with subsetting, it locates the row with the smallest value in a column: df[which.min(df$col), ] extracts the row with the minimum value.
For clipping values to a lower bound, pmax(x, lower_limit) is the idiomatic approach. pmax(x, 0) removes negative values by replacing them with 0. Similarly, pmin(x, upper_limit) clips from above. Combined, pmax(pmin(x, upper), lower) clips to a range, which is equivalent to the common “winsorization” operation in data cleaning.
min() with na.rm = FALSE (default) returns NA if any element is NA. With na.rm = TRUE, it ignores missing values. Called with an empty vector and no other arguments, min() returns Inf and issues a warning; min(integer(0)) returns Inf. When called with multiple arguments, min(a, b, c) is equivalent to min(c(a, b, c)). For element-wise minimum between two same-length vectors, use pmin(x, y).
Handling empty vectors
min() on an empty numeric vector returns Inf with a warning: “no non-missing arguments to min; returning Inf.” This behavior enables correct accumulation in minimum-finding loops — min(Inf, any_number) equals any_number — but the Inf result is surprising if the empty input is unexpected. Check length(x) > 0 before calling min() on inputs that might be empty.
For finding the index of the minimum value rather than the minimum value itself, use which.min(). It returns the index of the first minimum value in the vector, which is useful for selecting the element or for table lookups where you need to retrieve associated data from the same position. which.min() returns integer(0) for empty or all-NA input, which can be tested with length().
Clipping values to a lower bound
# Use pmax() to enforce a floor
raw_values <- c(3.2, 5.1, -2.7, 8.4, -1.1, 6.3)
# Replace negatives with zero
clipped <- pmax(raw_values, 0)
clipped
# [1] 3.2 5.1 0.0 8.4 0.0 6.3
# Double-ended clipping: keep values between 2 and 7
capped <- pmax(pmin(raw_values, 7), 2)
capped
# [1] 3.2 5.1 2.0 7.0 2.0 6.3
The parallel-minimum function pmin() and parallel-maximum function pmax() operate element-wise across their arguments, making them the idiomatic way to clamp numeric vectors. pmax(x, 0) replaces every negative element with zero — a ReLU operation — while pmax(pmin(x, upper), lower) enforces both an upper and lower bound simultaneously. This double-ended clipping is faster than the equivalent ifelse() chain and handles NA values correctly through the na.rm argument without introducing logical branching.