How to use crossing() in R

tidyr
crossing()
Learn how to use crossing() in R with practical examples. Step-by-step guide with code you can copy and run immediately.
Published

February 21, 2026

Introduction

The crossing() function from the tidyr package creates all possible combinations of values from multiple vectors or data frames. This is particularly useful when you need to generate a complete grid of combinations for experimental designs, parameter testing, or data modeling scenarios.

Getting Started

library(tidyverse)
library(palmerpenguins)

Example 1: Basic Usage

The Problem

Imagine you want to create all possible combinations of penguin species and islands to ensure your analysis covers every potential group. You need a systematic way to generate these combinations without manually typing each one.

Step 1: Create simple vectors

First, let’s define the distinct values we want to combine.

species_list <- c("Adelie", "Chinstrap", "Gentoo")
island_list <- c("Biscoe", "Dream", "Torgersen")

We now have two vectors containing the unique species and islands from our penguin dataset.

Step 2: Generate all combinations

Now we’ll use crossing() to create every possible pairing.

all_combinations <- crossing(
  species = species_list,
  island = island_list
)

print(all_combinations)

This creates a tibble with 9 rows (3 × 3), showing every possible species-island combination.

Step 3: Examine the structure

Let’s verify we have the complete grid we expected.

all_combinations |>
  count(species, island) |>
  nrow()

The result confirms we have 9 unique combinations, which matches our expectation of 3 species × 3 islands.

Example 2: Practical Application

The Problem

You’re planning a comprehensive analysis of car performance across different ranges of horsepower and weight categories. You need to create a reference grid that covers all combinations of these factors to ensure no potential group is missed in your modeling approach.

Step 1: Create categorical ranges

We’ll create meaningful categories from the continuous variables in mtcars.

hp_categories <- c("Low_HP", "Medium_HP", "High_HP")
weight_categories <- c("Light", "Medium", "Heavy")
gear_options <- c(3, 4, 5)

These categories will help us systematically explore different car characteristics.

Step 2: Build the complete grid

Now we’ll generate all possible combinations using crossing().

car_analysis_grid <- crossing(
  horsepower = hp_categories,
  weight = weight_categories,
  gears = gear_options
)

print(car_analysis_grid)

This produces a 27-row tibble (3 × 3 × 3) containing every possible combination of our three factors.

Step 3: Add analysis framework

Let’s prepare this grid for actual analysis by adding placeholder columns.

analysis_template <- car_analysis_grid |>
  mutate(
    sample_size = NA_integer_,
    avg_mpg = NA_real_,
    study_complete = FALSE
  )

head(analysis_template)

Now we have a complete framework ready for systematic analysis, ensuring no combination is overlooked.

Step 4: Verify completeness

Let’s confirm our grid covers all intended scenarios.

analysis_template |>
  summarise(
    total_combinations = n(),
    hp_levels = n_distinct(horsepower),
    weight_levels = n_distinct(weight)
  )

This verification step ensures our experimental design is complete and balanced.

Summary

  • crossing() generates all possible combinations from multiple vectors or data frames
  • Perfect for creating complete experimental designs and ensuring comprehensive analysis coverage
  • Works with any number of variables, though the result size grows exponentially
  • Produces a clean tibble format that integrates seamlessly with other tidyverse functions
  • Essential for systematic approaches to data analysis, modeling, and experimental planning