Crosstab calculation in R
How to Create a Crosstab in R (Cross Tabulation Guide with Examples)
Cross-tabulation, commonly known as a crosstab, is one of the most widely used techniques in statistics, data analysis, business intelligence, and survey research. A crosstab allows you to summarize the relationship between two categorical variables by displaying their frequencies in a matrix format.
In R, crosstabs can be created using several methods, but the combination of dplyr and tidyr provides a flexible and modern approach that integrates seamlessly with the tidyverse ecosystem.
In this tutorial, you’ll learn:
- What a crosstab is
- How to create crosstabs using dplyr and tidyr
- How to customize rows and columns
- Alternative methods using
table()andxtabs() - Real-world applications of cross-tabulation
- Best practices for analyzing categorical data
What Is a Crosstab?
A crosstab (cross-tabulation table) summarizes the frequency distribution of two or more categorical variables.
For example, a crosstab can answer questions such as:
- How many employees belong to each department and gender?
- How many customers fall into each age group and region?
- How many players occupy different positions on each team?
- How many survey respondents selected each response category?
Cross-tabulation is commonly used in:
- Market research
- Survey analysis
- Business analytics
- Healthcare studies
- Social science research
- Quality control
Basic Syntax for Creating a Crosstab in R
Using dplyr and tidyr, the basic syntax is:
df %>%
group_by(var1, var2) %>%
tally() %>%
spread(var1, n)
This:
- Groups observations by two variables.
- Counts the number of occurrences.
- Reshapes the output into a crosstab format.
Example 1: Create a Simple Crosstab
Create a Sample Dataset
df <- data.frame(
team = c("X", "X", "X", "X",
"Y", "Y", "Y", "Y"),
position = c("A", "A", "B", "C",
"C", "C", "D", "D"),
points = c(107, 207, 208, 211,
213, 215, 219, 313)
)
df
Output:
team position points
1 X A 107
2 X A 207
3 X B 208
4 X C 211
5 Y C 213
6 Y C 215
7 Y D 219
8 Y D 313
Load Required Packages
library(dplyr)
library(tidyr)
Generate the Crosstab
df %>%
group_by(team, position) %>%
tally() %>%
spread(team, n)
Output:
# A tibble: 4 × 3
position X Y
<chr> <int> <int>
1 A 2 NA
2 B 1 NA
3 C 1 2
4 D NA 2
Interpreting the Crosstab
The table shows the frequency of each position within each team.
Team X
| Position | Count |
|---|---|
| A | 2 |
| B | 1 |
| C | 1 |
Team Y
| Position | Count |
|---|---|
| C | 2 |
| D | 2 |
For example:
- Team X has two players in Position A.
- Team X has one player in Position B.
- Team Y has two players in Position D.
Replace Missing Values with Zero
The default output displays missing combinations as NA.
Many analysts prefer displaying zeros instead.
df %>%
group_by(team, position) %>%
tally() %>%
spread(team, n, fill = 0)
Output:
position X Y
1 A 2 0
2 B 1 0
3 C 1 2
4 D 0 2
This format is often easier to interpret.
Example 2: Switch Rows and Columns
You can easily reverse the layout by changing the variable used in spread().
df %>%
group_by(team, position) %>%
tally() %>%
spread(position, n)
Output:
team A B C D
1 X 2 1 1 NA
2 Y NA NA 2 2
Adding fill = 0 produces:
df %>%
group_by(team, position) %>%
tally() %>%
spread(position, n, fill = 0)
Output:
team A B C D
1 X 2 1 1 0
2 Y 0 0 2 2
Modern Alternative: Using pivot_wider()
The spread() function has been superseded by pivot_wider() in newer versions of tidyr.
Recommended syntax:
df %>%
count(team, position) %>%
pivot_wider(
names_from = team,
values_from = n,
values_fill = 0
)
This approach is more flexible and future-proof.
Alternative Method Using table()
For quick cross-tabulations, base R provides the table() function.
table(df$position, df$team)
Output:
X Y
A 2 0
B 1 0
C 1 2
D 0 2
This is often the fastest way to create a frequency table.
Alternative Method Using xtabs()
The xtabs() function is useful for statistical modeling and contingency tables.
xtabs(~ position + team, data = df)
Output:
team
position X Y
A 2 0
B 1 0
C 1 2
D 0 2
Calculate Row Percentages
Sometimes percentages are more informative than raw counts.
tab <- table(df$position, df$team)
prop.table(tab, margin = 1)
This calculates row-wise percentages.
Calculate Column Percentages
prop.table(tab, margin = 2)
This calculates percentages within each team.
Real-World Applications of Crosstabs
Business Intelligence
- Customer segments by region
- Product categories by sales channel
Marketing Analytics
- Campaign performance by customer group
- Conversion rates by traffic source
Human Resources
- Employees by department and gender
- Training participation by job role
Healthcare Analytics
- Disease prevalence by age group
- Treatment outcomes by patient category
Survey Research
- Responses by demographic groups
- Opinion analysis
Common Mistakes
Using Numerical Variables Directly
Crosstabs are designed primarily for categorical variables.
If needed, convert numeric variables into categories first.
Ignoring Missing Values
Missing categories can distort results.
Always inspect your data:
summary(df)
Not Replacing NA Values
Using:
fill = 0
often improves readability.
Best Practices
- Use categorical variables whenever possible.
- Replace missing combinations with zeros.
- Consider percentages in addition to frequencies.
- Use
pivot_wider()for modern tidyverse workflows. - Validate totals against the original dataset.
Conclusion
Cross-tabulation is one of the most valuable tools for exploring relationships between categorical variables. In R, you can create crosstabs easily using dplyr, tidyr, table(), or xtabs().
Whether you’re analyzing survey data, customer behavior, employee records, or business performance metrics, crosstabs provide a quick and intuitive way to summarize and interpret categorical data.
By mastering cross-tabulation techniques in R, you’ll be able to perform deeper exploratory data analysis and generate more meaningful insights from your datasets.

