统一不一致的类别
通过映射别名、使用模糊匹配纠正拼写错误,并强制采用规范列表,统一自由文本类别列。
统一不一致的类别 是 CoddyKit 上的免费 Pandas & NumPy Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Pandas & NumPy Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Pandas & NumPy Academy 课程共包含 4 节课。
不一致类别带来的问题
类别数据由人工录入时,同一概念可能会以许多不同的拼写出现:Electronics、electronics、ELECTRONICS、Electronicss。对该列执行 groupby 会产生几十个很小的分组,而不是一个有意义的分组。在进行任何聚合或创建机器学习特征之前,将类别统一为规范列表是最重要的清洗步骤之一。
import pandas as pd
df = pd.read_csv('products.csv')
print(df['category'].value_counts().head(20))规范化大小写和空白字符
第一步也是最简单的标准化操作,是规范化大小写和空白字符。在进行任何其他比较之前,应用 .str.strip().str.lower() 删除开头和结尾的空格,并将所有内容转换为小写。仅此一步就能合并许多变体:Electronics、electronics 和 ' Electronics ' 规范化后都会变成 electronics。
df['category_clean'] = df['category'].str.strip().str.lower()
print('Before:', df['category'].nunique())
print('After:', df['category_clean'].nunique())使用字典映射别名
完成大小写规范化后,许多变体仍会因缩写、同义词或旧名称而彼此不同。请构建一个别名映射字典,其中键是变体拼写,值是规范形式。使用 df['category_clean'].map(alias_map).fillna(df['category_clean']) 应用映射——fillna 会保留字典中不存在的值,而不是将它们替换为 NaN。
alias_map = {
'elect': 'electronics',
'elec': 'electronics',
'tech': 'electronics',
'clothing': 'apparel',
'clothes': 'apparel',
'garments': 'apparel'
}
df['category_clean'] = df['category_clean'].map(alias_map).fillna(df['category_clean'])
print(df['category_clean'].value_counts().head())使用 str.replace 修复模式
有些类别名称存在格式一致的错误,例如连续空格或末尾数字。使用 .str.replace() 配合正则表达式,一次性修复所有行中的这类模式。例如,.str.replace(r'\s+', ' ', regex=True) 会将多个空格合并为一个,.str.replace(r'\d+$', '', regex=True) 会删除类别名称末尾的数字。
df['category_clean'] = (
df['category_clean']
.str.replace(r'\s+', ' ', regex=True) # collapse spaces
.str.replace(r'\d+$', '', regex=True) # strip trailing numbers
.str.strip()
)
print(df['category_clean'].value_counts())使用 difflib 进行模糊匹配
当拼写错误不可预测时,请使用模糊匹配为每个变体查找最接近的规范类别。Python 内置的 difflib.get_close_matches() 会根据字符串相似度,从有效类别列表中返回最佳匹配项。可以通过 df['category'].apply() 将其作为辅助函数应用,以处理仅使用 map() 无法捕获的变体。
from difflib import get_close_matches
CANONICAL = ['electronics', 'apparel', 'home', 'sports', 'beauty']
def fuzzy_fix(val):
matches = get_close_matches(str(val).lower().strip(), CANONICAL, n=1, cutoff=0.7)
return matches[0] if matches else val
df['category_fuzzy'] = df['category_clean'].apply(fuzzy_fix)
print(df[['category_clean', 'category_fuzzy']].head(10))构建规范类别列表
请明确地定义规范类别,而不要从数据中推断。硬编码列表会强制每个值通过验证关卡;不在列表中的任何值都会被标记为未知。请将规范列表维护在配置文件或单独的 DataFrame 列中,这样业务新增产品类别时即可轻松更新,而无需修改清洗代码。
CANONICAL_CATEGORIES = {
'electronics', 'apparel', 'home', 'sports',
'beauty', 'food', 'toys', 'automotive'
}
df['is_known_category'] = df['category_fuzzy'].isin(CANONICAL_CATEGORIES)
unknown = df[~df['is_known_category']]['category_fuzzy'].unique()
print('Unknown categories remaining:', unknown)处理未知类别
模糊修正后仍无法匹配任何规范值的未知类别,应归入 Other 标签,而不是直接删除,这样就不会无声无息地丢失行。请使用 np.where() 或简单的条件赋值,将它们设置为 'other'。记录映射到 'other' 的行数,并检查这些行是否存在某些模式,以判断是否需要新增规范类别。
import numpy as np
df['category_final'] = np.where(
df['category_fuzzy'].isin(CANONICAL_CATEGORIES),
df['category_fuzzy'],
'other'
)
print(df['category_final'].value_counts())强制使用 Categorical 数据类型
标准化后,请将清洗后的类别列转换为 Pandas 的 Categorical 数据类型,并明确指定有效类别列表。这样可以强制执行模式:任何尝试赋予无效类别的操作都会引发错误。此外,类别字符串会在内部以整数代码存储,从而减少内存占用,并加快大型 DataFrames 上的分组操作。
df['category_final'] = pd.Categorical(
df['category_final'],
categories=list(CANONICAL_CATEGORIES) + ['other']
)
print(df['category_final'].dtype)
print(df['category_final'].cat.categories)验证清洗后的列
完成所有标准化步骤后,执行最终验证:断言清洗后的列中不存在规范集合之外的值。请使用 assert df['category_final'].isin(valid_set).all()。将此断言放在清洗函数的末尾,以便每次 pipeline 执行时都会运行;当新的原始数据包含以前未见过的类别标签时,它可以及时发现回归问题。
valid_set = set(CANONICAL_CATEGORIES) | {'other'}
assert df['category_final'].isin(valid_set).all(), 'Invalid category found'
print('All categories valid. Distribution:')
print(df['category_final'].value_counts())跟踪标准化变更
建立一张差异表,显示每一行发生变化时的原始值及其清洗后的替换值。这份审计记录可以让数据负责人审核特定映射,并批准或拒绝这些映射。请使用布尔掩码选出发生变化的行,并并排比较原始列和最终列。将差异表导出为 CSV,供非技术相关人员审核。
changed = df['category'] != df['category_final']
diff_table = df[changed][['category', 'category_final']].drop_duplicates()
diff_table.columns = ['original', 'mapped_to']
print(f'Unique mappings applied: {len(diff_table)}')
print(diff_table)保存标准化数据集
用标准化后的类别列替换原始的杂乱类别列,并在保存前删除中间工作列。将别名映射字典和模糊修正的截止阈值存储在 pipeline 配置中,以确保标准化过程完全可复现。两个月后对新的数据转储执行清洗时,只要输入值相同,就应产生完全相同的结果。
df_out = df.drop(columns=['category_clean', 'category_fuzzy', 'is_known_category'])
df_out = df_out.rename(columns={'category_final': 'category'})
df_out.to_parquet('products_clean.parquet', index=False)
print('Saved. Category distribution:')
print(df_out['category'].value_counts())快速检查
测试您对本课中数据分析概念的理解。
课程回顾
本课中您学习了:将大小写和空白规范化,作为标准化的第一步;映射别名并应用模糊匹配来纠正拼写错误;以及使用 Categorical 数据类型和断言强制执行规范类别列表。接下来,我们将学习模式验证和运行时断言,以保护 pipeline 的每个阶段。
常见问题解答
「统一不一致的类别」课时是免费的吗?
是的 — 「统一不一致的类别」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Pandas & NumPy Academy 课程的其余内容,请升级到 CoddyKit PRO。 Pandas & NumPy Academy 课程共包含 4 节课。
「统一不一致的类别」这节课中我会学到什么?
通过映射别名、使用模糊匹配纠正拼写错误,并强制采用规范列表,统一自由文本类别列。 你通过在浏览器中直接运行的动手代码来练习 Pandas & NumPy Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Pandas & NumPy Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Pandas & NumPy Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「统一不一致的类别」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Pandas & NumPy Academy 课中编写并运行代码吗?
能。每节 Pandas & NumPy Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。