pandas / Intermediate: grouping, joins and temporal quality / Lesson 11
Strings, categories and text contracts
Normalize only under a declared identity rule and distinguish text representation from category meaning and order.
On this page
Lesson goals
- Explain string dtype, missing text and literal versus regex matching.
- Review category membership, order and code stability.
Normalization can merge distinct business identities
Trimming spaces or case-folding depot labels can repair a documented source convention, but the same operation can collapse distinct case-sensitive identifiers. State whether the field is a display label, free text or a stable key before normalizing. Preserve original values when changes need audit or review. Unicode normalization and case folding answer different questions from removing punctuation or transliterating Arabic text. Do not invent one universal text-cleaning chain. A valid normalized value should still be checked against the field’s allowed domain, and a collision should be investigated rather than silently keeping whichever source row happens to appear first.
Text dtype and missing policy remain explicit
Since pandas 3.0, inferred text commonly uses the dedicated str dtype, while explicit string defaults to pd.NA-style nullable behavior. The selected3.1 baseline retains this distinction; object dtype can still contain mixed arbitrary Python objects. Choose the required representation and optional storage backend deliberately rather than inferring type from printed text. Many .str operations preserve missingness, while Boolean results and their default missing treatment depend on dtype and method. If a missing description should be excluded from a search, make that policy visible through the documented na argument or an explicit missingness step rather than relying on a changing default.
Match a literal punctuation character deliberately
The illustrative Series contains A.1, A1 and missing with explicit string dtype. str.contains('.', regex=False, na=False) matches only the first value because dot is literal and missing is deliberately excluded. With regex=True, dot has a different pattern meaning and can match characters in both nonmissing strings. The code’s expected mask is derived rather than executed. Escaping and regex flags should follow the intended search language, not whichever expression happens to return the desired count on one fixture. Validate matched identities and include punctuation, non-ASCII text and missing values in the search contract.
import pandas as pd
labels = pd.Series(['A.1', 'A1', None], dtype='string')
matched = labels.str.contains('.', regex=False, na=False)
assert matched.tolist() == [True, False, False]
print(matched.tolist())
[True, False, False]
Expected mask selects only A.1; regex is disabled and missing is excluded. pandas was not run.
Categories encode a domain, not permanent numeric IDs
A categorical dtype can describe a limited domain such as new, packed and sent. Ordered categories use declared category order rather than lexical order, which matters for sorting and comparisons. Internal codes are representation details and can change when categories are reordered or combined; never export them as permanent status identity without an explicit mapping contract. Converting values outside a declared category set can create missing entries, so validate membership before conversion. Grouping with categorical keys also needs an explicit observed policy. Memory savings depend on cardinality and backend costs, not a promise that category always uses less memory.
Check one declared normalization rule locally
The standard-library example trims and case-folds two synthetic display labels under a rule that explicitly treats their casing and surrounding spaces as insignificant. Both become depot a, revealing a collision that is acceptable only under that declared label contract. It does not emulate pandas string arrays, regex backends, nullable masks or categorical conversion. A real key normalization review should compare original and normalized uniqueness and retain a collision report. This lesson executes only the plain-string rule; pandas text and category examples remain non-runnable with no Arrow backend, regex engine extension or external data installed or used.
labels = [' Depot A ', 'DEPOT A']
normalized = [label.strip().casefold() for label in labels]
assert normalized == ['depot a', 'depot a']
assert len(set(normalized)) == 1
print(normalized)
['depot a', 'depot a']
The collision is visible and acceptable only under the stated display-label rule; pandas behavior is not modeled.
Common errors and fixes
A literal dot search matches nearly every nonempty value.
- Why it happens
- The pattern was interpreted as regex rather than literal text.
- How to fix it
- Choose regex=False for the intended literal match or escape the actual pattern.
- Prevent it next time
- Test punctuation and negative examples explicitly.
Normalization collapses two distinct item keys.
- Why it happens
- A display-label cleaning rule was applied to case-sensitive identity.
- How to fix it
- Restore original key evidence and define an authorized identity normalization rule.
- Prevent it next time
- Compare uniqueness and collisions before and after normalization.
Category codes are treated as stable external status identifiers.
- Why it happens
- Internal encoding was confused with a business mapping.
- How to fix it
- Export labels or a separately versioned explicit mapping.
- Prevent it next time
- Test category reordering and unknown membership.
Check your understanding
1. Does converting a status column to category prove every source status was valid?
Show worked solution
No. Values outside an explicit category set can become missing during conversion. Validate membership and count newly missing values before accepting the result. A compact representation is not a quality certificate, and internal codes must not replace the source’s stable domain identifiers without a separate mapping contract.
2. Why is the normalization collision acceptable for the fixture but not automatically for item IDs?
Show worked solution
The fixture explicitly declares two display-label forms equivalent. Item identity may be case-sensitive or distinguish surrounding characters under another source contract. Determine that meaning first, preserve originals and investigate collisions rather than applying a universal text-cleaning function to every column.
Key takeaways
- Text matching and normalization must follow field meaning.
- Category membership, order and internal codes are separate concerns.