Categorical variables are everywhere in real datasets: gender, product category, user type, churn status, or membership tiers. Knowing how many items fall into each category is often one of the first things an analyst does. The basic frequency table is simple—but turning that into a robust, reusable data frame, with clean handling of edge cases, large scale, and integration into dashboards or machine learning pipelines, takes a bit more thought in 2025.
This article shows you how to build frequency tables cleanly in R, handle more than one categorical variable, deal with missing or rare levels, scale to large data, and prepare for reuse in reporting or modeling.
Why Frequency Tables Still Ashore Your Analysis
- Baseline insights: Before modeling, you need to understand distribution of categories (sparsity, imbalance, rare levels).
- Feature engineering: Frequency counts can become features themselves or help guide grouping rare levels into “Other”.
- Dashboarding & reporting: Viewers expect tidy tables and counts, ideally sorted, labeled, and ready to display.
- Scaling & reproducibility: You want code that works fast and reliably with large datasets, reproducible across environments and over time.
What’s New in 2025: Improved Tools & Conventions
- Tidyverse / data.table blending: Analysts often combine tidy tools with high-performance code for large data.
- Handling rare categories: Automated grouping of low-frequency levels into “Other” to avoid clutter or overfitting.
- Dealing with missingness transparently: Missing values explicitly counted or filtered, not silently dropped.
- Enhanced speed & memory: Using packages optimized for large data frames, streaming, or chunked counting.
- Reusable functions & modular code: Create wrappers for frequency tables so that dashboards, model pipelines, and scripts use a standard implementation.
Step-by-Step: Building a Clean Frequency Table in R
Here’s a workflow with modern touches.
Step 1: Prepare Your Data
- Ensure the categorical variable is a factor (or convertible). If it’s a string / character, it’s often good to convert to factor or define levels.
- Decide how to handle missing values: keep them as a level (e.g. “Missing” or NA) or drop depending on context.
- Decide whether rare levels should be grouped.
library(dplyr)
df <- read.csv("your_data.csv")
Example: clean category var
df <- df %>%
mutate(cat_var = as.character(cat_var),
cat_var = if_else(is.na(cat_var), "Missing", cat_var),
cat_var = factor(cat_var))
Step 2: Basic Frequency with table() or count()
The simplest route:
tab <- table(df$cat_var)
tab_df <- as.data.frame(tab)
names(tab_df) <- c("category", "count")
Or using tidy style:
freq_tbl <- df %>%
count(cat_var, name = "count")
This gives you a data frame immediately.
Step 3: Sorting, Percentages, and Rare Levels
You’ll often want the counts sorted in descending order, plus showing proportions, and combining rare categories.
total <- nrow(df)
freq_tbl2 <- df %>%
count(cat_var, name = "count") %>%
arrange(desc(count)) %>%
mutate(
prop = count / total,
is_rare = count < 0.01 * total, # for example, less than 1%
category2 = if_else(is_rare, "Other", as.character(cat_var))
) %>%
group_by(category2) %>%
summarise(
count = sum(count),
prop = sum(prop)
) %>%
arrange(desc(count))
Here you end up with categories with enough data, plus an “Other” to catch tiny levels.
Step 4: Multiple Categorical Variables / N-way Tables
If you want frequency of combinations (cross categories), or more than one categorical variable:
freq_multi <- df %>%
count(cat_var1, cat_var2, name = "count") %>%
arrange(desc(count))
Or build contingency tables:
ctab <- xtabs(~ cat_var1 + cat_var2, data = df)
ctab_df <- as.data.frame(ctab)
names(ctab_df) <- c("cat1", "cat2", "count")
Step 5: Performance Considerations for Larger Datasets
When your dataset has millions of rows:
Use data.table for fast counting:
library(data.table)
dt <- as.data.table(df)
freq_dt <- dt[, .(count = .N), by = cat_var]
setorder(freq_dt, -count)
- For streaming or chunked workflows: read in chunks, aggregate partial frequency counts, merge.
- Avoid unnecessary factor levels to limit memory.
- Consider storing the results for reuse, e.g. in a database table or as a serialized RDS file.
Step 6: Wrapping as a Function for Reuse
Build a reusable function so that you and your team always get consistent output.
get_freq_table <- function(data, var, threshold = 0.01, drop_na = FALSE) {
df2 <- data %>%
mutate(var = as.character(.data[[var]]),
var = if_else(is.na(var), "Missing", var))
if (!drop_na) {
df2 <- df2
} else {
df2 <- df2 %>% filter(!is.na(var))
}
total <- nrow(df2)
freq_tbl <- df2 %>%
count(var, name = "count") %>%
mutate(prop = count / total,
is_rare = count < threshold * total,
category_clean = if_else(is_rare, "Other", var)) %>%
group_by(category_clean) %>%
summarise(
count = sum(count),
prop = sum(prop)
) %>%
arrange(desc(count))
return(freq_tbl)
}
You can apply this function in pipelines, dashboards, or modeling prep.
Governance, Ethics & Visualization Tips
- Label clearly: When you output "category" and "Other", make sure legends or table headers explain what "Other" covers.
- Track code version and definitions: If you change thresholds (for rarity), factor levels, or NA handling, version your function or document the change.
- Fairness check: Ensure that rare levels don’t hide important subgroups. Be mindful when "Other" hides minority classes.
- Visualization: Use bar plots or ordered tables; consider having the most frequent categories first. Show percentages to give context.
Summary Snapshot in Paragraph
Building a frequency table in modern R starts with cleaning and preparing your categorical variable (handling missing, converting to factor, deciding on rare level thresholds), then generating a basic table or using count() for compact syntax. From there, you’ll often sort descending, compute proportions, and group low-frequency categories into “Other.” If you have multiple categorical variables, create cross-tables or use xtabs to explore joint frequencies. For large datasets, one uses data.table, streaming or chunked aggregation, and avoids creating overly large factor levels. Wrapping this into a reusable function ensures consistency across analyses, dashboards, and modeling pipelines.
Final Thoughts
Frequency tables might seem simple—but in real data work, the details matter. Whether you’re prepping data for dashboards, balancing features for a model, exploring data distributions, or reporting to stakeholders, well-constructed frequency tables save time, avoid surprises, and make your work more trusted. In 2025, the best practice combines speed, clarity, and flexibility.
This article was originally published on Perceptive Analytics.
In Miami, our mission is simple — to enable businesses to unlock value in data. For over 20 years, we’ve partnered with more than 100 clients — from Fortune 500 companies to mid-sized firms — helping them solve complex data analytics challenges. As a leading Power BI Consulting Services in Miami and Tableau Consulting Services in Miami, we turn raw data into strategic insights that drive better decisions.
Top comments (0)