dplyr::slice()
slice(.data, ...) The dplyr slice functions provide flexible ways to select rows from a data frame or tibble based on position, random sampling, or rank. While slice() selects rows by integer positions, slice_head() and slice_tail() take the first or last rows, slice_sample() randomly samples rows, and slice_min()/slice_max() select rows with extreme values in a specified column.
These functions are designed for readability and integration with the tidyverse pipeline (%>%). They are particularly useful for data exploration, creating training/test splits, and extracting top/bottom records.
Syntax
slice(.data, ...)
slice_head(.data, n, prop)
slice_tail(.data, n, prop)
slice_sample(.data, n, prop, replace = FALSE, weight_by = NULL)
slice_min(.data, order_by, n, prop, with_ties = TRUE)
slice_max(.data, order_by, n, prop, with_ties = TRUE)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
.data | tibble / data.frame | Required | The tibble or data frame to slice |
... | integer | Required (for slice()) | Row positions to select (positive integers select rows, negative integers drop rows) |
n | integer | Optional (for slice_head/tail/sample/min/max) | Number of rows to select. Either n or prop must be supplied |
prop | numeric | Optional | Proportion of rows to select (0 < prop ≤ 1). Either n or prop must be supplied |
replace | logical | FALSE (for slice_sample) | Sample with replacement? |
weight_by | numeric vector | NULL (for slice_sample) | Sampling weights; must be same length as number of rows |
order_by | unquoted column name | Required (for slice_min/max) | Column to rank rows by |
with_ties | logical | TRUE (for slice_min/max) | Keep ties? If TRUE, all rows with the same extreme value are returned |
Examples
Basic slice() usage
slice() selects rows by their integer position within the data frame. You can pass multiple row numbers to pick specific rows, use ranges like 1:5 to grab a contiguous block, or use negative indices to drop rows. This is the most fundamental row-subsetting verb in dplyr:
library(dplyr)
df <- tibble(
id = 1:10,
value = rnorm(10)
)
# Select rows 1, 3, 5
df %>% slice(1, 3, 5)
# Select first 5 rows (equivalent to head)
df %>% slice(1:5)
# Drop rows 2 and 4 (negative indexing)
df %>% slice(-2, -4)
slice_head() and slice_tail()
These convenience functions mirror head() and tail() but work naturally inside dplyr pipelines. slice_head(n = 3) takes the first three rows, while slice_tail(prop = 0.2) takes the last 20%. Using prop instead of n abstracts the row count away, which is useful in programmatic contexts where the data size varies:
# First 3 rows
df %>% slice_head(n = 3)
# Last 20% of rows
df %>% slice_tail(prop = 0.2)
# slice_tail with negative n (drops last rows)
df %>% slice_tail(n = -5) # drops last 5 rows, returns first (nrow - 5) rows
Random sampling with slice_sample()
slice_sample() draws rows at random, with or without replacement. The weight_by argument lets you bias the selection toward rows with larger absolute values in a particular column, which is useful for importance sampling or creating representative subsets from skewed distributions:
# Randomly select 5 rows without replacement
df %>% slice_sample(n = 5)
# Randomly select 10% of rows with replacement
df %>% slice_sample(prop = 0.1, replace = TRUE)
# Weighted sampling (e.g., probability proportional to value)
df %>% slice_sample(n = 2, weight_by = abs(value))
Selecting extreme values with slice_min() and slice_max()
These functions select rows with the smallest or largest values of a specified column. The with_ties argument controls whether rows sharing the same rank as the nth extreme value are included. Setting with_ties = FALSE guarantees exactly n rows are returned, which is important when you need a fixed-size output:
# 3 rows with smallest 'value'
df %>% slice_min(value, n = 3)
# Top 20% of rows by 'value'
df %>% slice_max(value, prop = 0.2)
# When ties exist, with_ties controls whether all ties are kept
df %>% slice_min(value, n = 1, with_ties = FALSE) # returns exactly 1 row
Common patterns
Creating training/test splits
slice_sample() makes a clean train/test split in one line by drawing a random fraction of rows. Setting the seed beforehand ensures reproducibility. The test set is the complement, drawn without specifying n or prop since it takes the remaining rows:
set.seed(123)
train <- df %>% slice_sample(prop = 0.7)
test <- df %>% slice_sample(prop = 0.3)
Removing the first/last rows after grouping
When data is grouped, negative indexing in slice() drops rows within each group independently. slice(-1) removes the first row of every group, which is a common pattern for discarding header rows or removing leading observations flagged as artifacts in the raw data:
df %>%
group_by(category) %>%
slice(-1) # drop first row of each group
Selecting top n per group
Combining slice_max() with group_by() selects the top rows by some metric within each group. This replaces the older pattern of top_n() and is more flexible because it supports both n for an absolute count and prop for a relative fraction of each group’s rows:
df %>%
group_by(category) %>%
slice_max(value, n = 3) # top 3 rows per group
Random permutation of rows
Calling slice_sample(prop = 1) without replacement shuffles every row into a random order. This is the pipe-friendly alternative to base R’s sample(nrow(df)) indexing and works correctly inside grouped pipelines where each group’s rows are permuted independently of the others:
df %>% slice_sample(prop = 1) # reshuffle all rows (no replacement)
Sampling with stratification
When you need a fixed number of samples from each level of a categorical variable, group by that variable and call slice_sample(n = 5). Every group contributes exactly the requested number of rows, which guarantees proportional representation regardless of the original group sizes:
df %>%
group_by(stratum) %>%
slice_sample(n = 5) # sample 5 rows from each stratum
Performance notes
slice()is very fast; it uses integer indexing.slice_sample()with replacement can be slower on large datasets.- For grouped operations, each group is sliced independently, which can be memory-intensive with many groups.
- When using
slice_min()/slice_max()on unsorted data, dplyr performs a partial sort; consider pre-sorting if performance is critical.
dplyr::slice() in practice
slice() selects rows by integer position. Unlike filter(), which selects by condition, slice() selects by index. slice(df, 1:5) returns the first five rows. slice(df, n()) returns the last row (n() evaluates to the row count within a slice context). Negative indices drop rows: slice(df, -1) removes the first row.
The helper functions slice_head(), slice_tail(), slice_min(), slice_max(), and slice_sample() are specialized variants. slice_head(df, n = 10) returns the first 10 rows. slice_max(df, order_by = col, n = 5) returns the 5 rows with the highest values in col. All helpers accept prop as an alternative to n for proportional selection: slice_sample(df, prop = 0.1) returns a random 10% sample.
When combined with group_by(), slice() and its variants operate per group. df |> group_by(category) |> slice_max(score, n = 1) returns the top-scoring row from each category — a common pattern for leaderboards and “best per group” queries.
slice_sample() with weight_by enables weighted sampling: rows with higher values in the weight column are more likely to be selected. This is useful for oversampling rare categories or implementing importance sampling in simulation workflows.
slice() selects rows by integer position. slice_head(n = 5) and slice_tail(n = 5) replace head() and tail() in pipelines. slice_sample() replaces sample_n() / sample_frac(). When used with group_by(), slicing operates within each group independently, making it easy to select the top-N rows per category.