本节摘要:数据"看起来有值"还不够,类型与量纲必须对。本节解决三类问题:类型错乱(数字存成字符串、日期不统一)、类别数据的编码(标签编码与独热编码)、数值特征的标准化(Min-Max 与 Z-score),并给出各自适用场景。
阅读完本节,你应当能够:
"金额"列明明是数字,dtype 却是 object——因为它里面混了一两个带逗号的字符串"1,299",Pandas 只好整列降级成字符串。此时求和直接报错或结果全错。类型问题不解决,后面每一步都是雷。另一个常见困惑:分类变量"门店"直接喂给算法会被当成数字 1、2、3 的大小关系,必须编码成算法能理解的形式。
Pandas 的每列都声明了 dtype。object 列(字符串)不能做数值运算;数值列混入字符串会自动降级。astype 是强转接口,pd.to_numeric 更宽容(能处理"1,299"这类带格式的文本)。
💡 关键直觉:无序类别(门店、类目、颜色)用独热编码;有序类别(差评/中评/好评、小/中/大)才用标签编码。给"门店"编成 0/1/2 让算法以为广州大于北京,是常见错误。
(x - min) / (max - min)。受异常值影响大(x - mean) / std。抗异常值更好import pandas as pd # 字符串转数值(自动处理 "1,299" 这种格式) df["金额"] = pd.to_numeric(df["金额"], errors="coerce") # 强转类型 df["门店编号"] = df["门店编号"].astype(int) # 字符串转日期 df["日期"] = pd.to_datetime(df["日期"])
⚠️ 常见坑:
to_numeric遇到无法转换的值,加errors="coerce"会变成 NaN,而不是报错中断——这正好暴露出"藏得最深的问题值"。转换后务必检查新产生的 NaN,它们就是需要回头修的脏数据。
df["日期"] = pd.to_datetime(df["日期"], format="%Y-%m-%d", errors="coerce")
format 明确告诉 Pandas 日期长什么样,errors="coerce" 把解析失败的变成 NaN 以便定位。格式混用的列("2026-01-01" 和 "20260102")在 to_datetime 面前也能统一。
# 标签编码:手动映射或 factorize df["门店编码"] = df["门店"].astype("category").cat.codes # 独热编码 df_onehot = pd.get_dummies(df["门店"], prefix="门店") df = pd.concat([df, df_onehot], axis=1)
get_dummies 一行生成所有独热列,配合 concat 拼回原表。类别多时(几十个)考虑只用出现频次高的前 N 类做独热,其余归为"其他",避免列爆炸。
# Min-Max 归一化 df["金额归一"] = (df["金额"] - df["金额"].min()) / (df["金额"].max() - df["金额"].min()) # Z-score 标准化 df["金额标准"] = (df["金额"] - df["金额"].mean()) / df["金额"].std()
| 数据形态 | 处理 | 典型场景 |
|---|---|---|
| 文本数字("1,299") | to_numeric + coerce | 金额、数量 |
| 日期格式不一 | to_datetime + format | 时间字段 |
| 无序类别 | 独热编码 | 门店、类目 |
| 有序类别 | 标签编码 | 评分、等级 |
| 量纲差异大 | 标准化 | 机器学习特征 |
| 有异常值的分布 | Z-score | 抗离群 |
df = pd.DataFrame({ "订单号": ["A001", "A002"], "金额": ["1,299", "89.5"], "日期": ["2026-01-01", "20260102"], "门店": ["北京", "上海"], }) df["金额"] = pd.to_numeric(df["金额"].str.replace(",", ""), errors="coerce") df["日期"] = pd.to_datetime(df["日期"], errors="coerce") df = pd.concat([df, pd.get_dummies(df["门店"], prefix="门店")], axis=1) print(df.dtypes) print(df)
金额、日期、门店三类问题一次解决,列类型全部正确。
| 现象 | 原因 | 解法 |
|---|---|---|
| 数字列是 object | 混入文本/千分位 | to_numeric + errors="coerce" |
| 日期是 object | 格式不统一 | to_datetime + format |
| 布尔列是 object | 存了"是/否"文本 | map({"是": True, "否": False}) |
| 数值列出现 NaN | 原值是空字符串 | replace("", np.nan) 再处理 |
| astype 报错 | 有无法转换的值 | 先 to_numeric 定位问题值 |
标准化不是所有场景都要做:
💡 关键直觉:先想清楚下游是谁。模型要"距离"就标准化,模型只"比较大小"就不用。标准化用错了场景,等于给数据强行套了不合适的尺子。
独热编码后表会变宽,注意数据形态的变化:
df = pd.DataFrame({"门店": ["北京", "上海", "广州", "北京"]}) encoded = pd.get_dummies(df["门店"], prefix="门店") print(encoded) # 门店_北京 门店_上海 门店_广州 # 0 1 0 0 # 1 0 1 0 # 2 0 0 1 # 3 1 0 0
每行只有一个 1、其余为 0,这就是"独热"的含义。类别有几十个时,独热列数会爆炸——先用 value_counts 看分布,只对高频类建列,低频归入"其他"。
类型与量纲都对了,还剩最后一块硬骨头——异常值。下一节教你用 IQR 和 Z-score 把它们揪出来,并把整套清洗流程固化成管道。