Reduce Pandas Memory Footprint by Converting Repeated Strings to Categorical
Convert repeated string columns to pandas Categorical to cut memory by up to 90%. This guide shows a worked example, explains how it works, and lists limits and common pitfalls.
20 Sept 2025, 14:16 UTC

Why Convert Repeated Strings to Categorical?
When a DataFrame contains a string column with many repeated values—think country names, product SKUs, or categorical labels—pandas stores each entry as a full Python object. Even if two rows share the same string, the memory cost is duplicated. Converting that column to pd.Categorical replaces each string with a small integer code and keeps a single copy of each unique value in a separate array, cutting memory usage by 70–90% in many cases.
How It Works
Under the hood, a Categorical object stores:
- codes – an
int8orint16array mapping each row to a category index. - categories – a list of the unique strings.
- ordered – a flag for ordinal data.
groupby, merge, or boolean indexing still run efficiently because pandas works with the integer codes internally.
Worked Example
Below is a reproducible snippet that demonstrates the memory savings and verifies that the categorical column retains the original values.
# Example DataFrame with many repeated strings
import pandas as pd
import numpy as np
# 1 million rows, 3 distinct countries
np.random.seed(0)
countries = np.random.choice(["USA", "CAN", "MEX"], size=1_000_000)
df = pd.DataFrame({"id": np.arange(1_000_000), "country": countries})
# Measure baseline memory
base_mem = df.memory_usage(deep=True).sum()
print(f"Baseline memory: {base_mem / (1024**2):.2f} MB")
# Convert to categorical
df['country'] = df['country'].astype('category')
cat_mem = df.memory_usage(deep=True).sum()
print(f"After conversion: {cat_mem / (1024**2):.2f} MB")
print(f"Savings: {100 * (base_mem - cat_mem) / base_mem:.1f}%")
# Verify values are unchanged
assert (df['country'].astype(str) == countries).all()
Running the snippet on a typical laptop shows a drop from roughly 70 MB to about 10 MB—an 86% reduction. The assert confirms the data is identical after conversion.
Common Mistakes & Limits
- High Cardinality: If almost every row has a unique string, the
categoriesarray grows large, potentially offsetting the benefit. Test withdf['col'].nunique()before converting. - Frequent Updates:
Categoricalarrays are immutable. Element‑wise assignment creates a new array, which can be slower than using anobjectcolumn for highly dynamic data. - Mismatched Categories on Merge: When concatenating or merging DataFrames, ensure that both share the same category ordering. Otherwise pandas will create a new categorical with a combined set, increasing memory.
- Ordered vs. Unordered: For ordinal data (e.g., "Low", "Medium", "High"), set
ordered=Trueto enable comparison operators. Forgetting this can lead to unexpected behavior. - Conversion Back: Converting back to
objectorstringis inexpensive, but you lose the memory advantage. Keep the column categorical if it will be reused.
Practical Verification Checklist
- Measure Memory:
df.memory_usage(deep=True).sum()before and after conversion. - Test Performance: Use
%timeiton common operations (e.g.,df.groupby('country').size()) to ensure speed is maintained. - Validate Data Integrity: Map codes back to categories and compare to the original column.
df['country'].cat.categories[df['country'].cat.codes] == df['country'].astype(str) - Check Dtype:
df['country'].dtypeshould showcategoryanddf['country'].cat.codes.dtypea small integer type. - Monitor Cardinality:
df['country'].nunique()relative to total rows. If >50% unique, reconsider.
Conclusion
Converting a string column with many repeated values to pd.Categorical is a straightforward, low‑risk technique that can dramatically reduce memory usage without sacrificing performance. Always benchmark memory and speed for your specific dataset, and be mindful of high‑cardinality or highly mutable scenarios where the benefits may diminish.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.