rguides

nrow()

nrow(x)

nrow() returns the number of rows in a matrix, array, or data frame. It returns NULL for atomic vectors since vectors have no dimensions.

Syntax

nrow(x)

Parameters

ParameterTypeDefaultDescription
xmatrix, array, data frame, or NULL,Input object to get row count from

Examples

Basic usage with a matrix

The most common application of nrow() is checking matrix dimensions. When you create a matrix with matrix(), the nrow argument specifies the row count, and nrow() retrieves it later. This is useful for verifying that reshaping or subsetting operations produced the expected dimensions.

# Create a 3x4 matrix
mat <- matrix(1:12, nrow = 3, ncol = 4)
nrow(mat)
# [1] 3

Working with data frames

For data frames, nrow() returns the number of observations — the dataset’s row count. This is the standard way to check how many records a data frame contains after loading from a CSV, filtering, or joining. Unlike length(), which returns the number of columns for a data frame, nrow() always gives the observation count.

df <- data.frame(
  name = c("Alice", "Bob", "Charlie"),
  age = c(25, 30, 35),
  score = c(85, 92, 78)
)

nrow(df)
# [1] 3

Using with NULL and vectors

A key behavior to remember: nrow() returns NULL for objects without a row dimension, including vectors and NULL itself. This distinguishes it from functions that throw errors on non-tabular input. When writing generic helper functions, use NROW() instead, which treats vectors as single-column matrices and returns length(x) rather than NULL.

# NULL has no dimensions
nrow(NULL)
# NULL

# Vectors have no dimensions
vec <- 1:10
nrow(vec)
# NULL

Common patterns

Loop over rows

nrow() is the natural choice when iterating over matrix rows with a for loop. Using 1:nrow(mat) as the loop index visits each row in sequence, and mat[i, ] extracts the i-th row for processing. This pattern works equally well with data frames, though for loops over data frame rows are less common in idiomatic R than apply-family alternatives.

mat <- matrix(1:12, nrow = 3)

# Process each row
for (i in 1:nrow(mat)) {
  print(mean(mat[i, ]))
}

Check if object has rows

A common defensive pattern is wrapping nrow() in a helper that checks whether an object has a row dimension at all. Testing !is.null(nrow(x)) distinguishes matrices and data frames from vectors, which is useful when a function must accept multiple input types and branch on the result.

has_rows <- function(x) {
  !is.null(nrow(x))
}

has_rows(matrix(1:4, 2, 2))
# [1] TRUE

has_rows(1:10)
# [1] FALSE

Get last row

nrow() combined with bracket indexing gives you the last row of a matrix or data frame: mat[nrow(mat), ]. This expression is self-documenting — it reads as “row number equal to the total number of rows.” The same pattern works for the last column with ncol() and for single-element access in either dimension.

mat <- matrix(1:12, nrow = 3, ncol = 4)

# Get last row
mat[nrow(mat), ]
# [1]  9 10 11 12

nrow() in practice

nrow() returns the number of rows in a matrix or data frame. For vectors, it returns NULL. For data frames, nrow(df) is the count of observations, making it the standard check for data size: cat("Loaded", nrow(df), "rows ").

In filtering operations, comparing nrow() before and after is the standard way to confirm how many rows were removed: n_before <- nrow(df); df <- df[condition, ]; cat("Removed", n_before - nrow(df), "rows "). The tidyverse equivalent nrow() works the same way on tibbles.

NROW() treats a vector as a single-column matrix, returning its length rather than NULL. This is useful in generic functions: NROW(x) returns the number of observations whether x is a vector or a data frame.

nrow() is equivalent to dim(x)[1]. For checking whether a data frame is empty, nrow(df) == 0 is the standard idiom. After joining or filtering, always check nrow() to verify the operation worked as expected — an inner join that returns 0 rows is a common sign of a key mismatch.

nrow() returns the number of rows in a matrix or data frame. For a vector, it returns NULL. NROW() treats vectors as single-column matrices and returns length(), useful in generic functions that should accept both vectors and matrices. In a pipeline, nrow() is often used at the end as a quick sanity check: df |> filter(...) |> nrow() counts matching rows without materializing a full result.

nrow() returns the number of rows in a matrix or data frame. For vectors, it returns NULL. NROW() treats vectors as single-column matrices, returning length() — useful in generic functions that must accept both vectors and matrices without branching.

In pipelines, nrow() is a count assertion: df |> filter(active == TRUE) |> nrow() counts active rows without materializing extra columns. The equivalent dplyr approach is count(), which is more informative when grouped counts are needed, but nrow() is faster for a simple total.

After rbind() or bind_rows(), verifying nrow() confirms the append produced the expected row count. If a join unexpectedly multiplies rows, nrow(result) > nrow(original) catches the problem early.

nrow(NULL) returns NULL, not 0. Code that must handle NULL inputs should use NROW() or guard with is.null(). For matrices, nrow() is equivalent to dim(m)[1].

Verifying data integrity after filtering

# Load-like scenario: filter and check row counts
records <- data.frame(
  id = 1:100,
  status = sample(c("active", "inactive", "pending"), 100, replace = TRUE),
  value = rnorm(100, mean = 50, sd = 15)
)

# Count before filtering
n_before <- nrow(records)

# Filter to active records only
active <- records[records$status == "active", ]
n_after <- nrow(active)

# Report the change
cat(sprintf("Filtered from %d to %d rows (removed %d)\n",
            n_before, n_after, n_before - n_after))
# Filtered from 100 to 33 rows (removed 67)

# Safety: check the row count didn't vanish entirely
stopifnot(nrow(active) > 0)

This pattern of capturing nrow() before and after a filtering operation is the standard way to audit data pipeline steps. If the stopifnot guard triggers, it means the filter removed every row, which usually signals a mis-specified condition or unexpected values in the status column. For multiple sequential filters, recording row counts at each stage helps you identify exactly which step loses data — an approach far more informative than discovering the problem only at the final output.

See also