
在数据分析全流程中,“数据清洗” 恰似烹饪前的食材处理:若食材(数据)腐烂变质、混杂异物(脏数据),即便拥有精湛的烹饪技巧(分析模型),也无法烹制出符合要求的佳肴(可靠结论)。据行业调研显示,CDA(Certified Data Analyst)数据分析师约 60% 的工作时间都投入数据清洗 —— 其核心目标并非简单 “删除脏数据”,而是通过系统化处理,将 “格式混乱、缺失异常的不可用数据” 转化为 “干净一致、贴合业务需求的高质量数据”,为后续建模分析与业务决策夯实基础。
本文将从 CDA 分析师的实战视角,系统拆解数据清洗的核心认知、四类常见数据问题的处理策略、全流程操作规范,让 “数据清洗” 从 “繁琐的体力劳动” 升级为 “有逻辑、有策略的技术工作”。
数据清洗(Data Cleaning)并非 “删除错误数据” 的单一操作,而是一套完整的系统性流程:先识别数据中的质量问题(如缺失值、异常值、重复值、格式混乱),再结合业务逻辑制定针对性处理策略,最终输出可直接用于分析的高质量数据集。其本质是 “修复数据的‘瑕疵’,还原数据背后的业务真相”。
数据清洗需围绕 “完整性、一致性、准确性” 三大目标展开,三者共同构成数据质量的基础:
完整性:针对 “字段缺失” 问题,通过合理方式补全或标注(如 “用户年龄缺失” 时,结合用户所在城市的年龄分布填充中位数,而非直接删除整条记录),确保分析所需的关键字段无遗漏;
一致性:解决 “格式混乱” 问题,统一数据格式(如将日期统一为 “YYYY-MM-DD”)与编码规则(如 “性别” 字段统一用 “男 / 女”,替代 “1/0”“Male/Female” 等混合表述),避免因格式差异导致的分析偏差;
准确性:修正 “数据异常” 问题,识别并修正录入错误(如 “订单金额为 - 100 元” 大概率是符号误输)、剔除无效数据(如测试环境产生的 “测试订单”、用户重复提交的表单),确保数据能真实反映业务情况。
普通数据清洗常停留在 “技术操作层面”(如用 Excel 删除重复行、用 0 填充缺失值),而 CDA 分析师的清洗工作需深度结合 “业务逻辑与分析目标”,两者在处理逻辑、目标导向等维度差异显著:
对比维度 | 普通数据清洗 | CDA 分析师数据清洗 |
---|---|---|
处理逻辑 | 按固定规则机械操作(如所有缺失值统一用 0 填充,不区分字段含义) | 结合业务场景灵活选择策略(如 “用户年龄缺失用中位数填充,用户职业缺失标‘未知’,避免影响分层分析”) |
目标导向 | 仅追求 “数据表面干净”,不关联后续分析需求 | 服务具体分析目标(如 “为用户分层分析做准备,需优先确保‘消费金额、复购频次’等核心字段无异常”) |
工具选择 | 依赖 Excel 等可视化工具,仅支持 10 万条以内的小数据量 | 灵活组合 SQL、Python 等工具(SQL 用于海量数据去重,Python 用于复杂缺失值预测),适配多源 / 海量数据场景 |
结果验证 | 不做验证或仅简单核对数据量,忽略业务逻辑 | 结合业务场景做多层验证(如 “清洗后订单金额总和与财务部门统计数据对比,差异率需≤1%”) |
CDA 分析师并非 “数据清洗的机械执行者”,而是 “数据质量的守护者”,其核心价值体现在三个关键环节:
业务翻译者:将模糊的业务需求转化为明确的清洗目标(如 “需分析用户复购原因”,转化为 “需确保‘用户消费记录、复购间隔、优惠券使用情况’等字段无缺失异常”);
策略制定者:针对同一数据问题,根据业务场景选择最优方案(如 “用户收入缺失” 时,若需分析高收入用户特征,用 “同行业同职位收入中位数” 填充,而非简单用全局均值,避免极端值干扰);
质量把控者:清洗后通过 “业务逻辑核验” 确保数据可用(如 “订单金额需≥0”“用户年龄应在 18-80 岁合理范围”),而非仅满足 “缺失率≤1%” 等技术指标。
数据清洗的核心是 “针对性解决问题”。CDA 分析师需熟练处理 “缺失值、异常值、重复值、格式不一致” 四类高频问题,且每类问题的处理都需结合业务场景选择适配方法,避免 “一刀切” 的机械操作。
缺失值是最常见的数据质量问题(如用户注册时未填写 “职业”、订单系统故障导致 “支付时间” 缺失)。CDA 分析师需根据 “字段重要性、缺失率、业务含义” 三要素选择处理策略,优先通过补全保留数据价值,仅在必要时考虑删除。
处理策略 | 适用场景 | 工具与代码示例(Python/Pandas) |
---|---|---|
1. 删除法 | ① 缺失率极高(通常 > 30%)且字段非核心(如 “用户兴趣标签”,不影响营收分析);② 含缺失值的行无业务意义(如 “订单 ID 缺失的记录”,无法关联用户与商品信息) | ```python |
df_clean = df.dropna(subset=["order_id"])
df_clean = df_clean.drop(columns=["interest_tags"])
| 2. 填充法(数值型) | 缺失率较低(通常<10%)且字段为数值型(如“用户年龄、订单金额、商品评分”),需结合字段特性选择填充值 | ```python
# 1. 中位数填充(适用于含极端值的字段,如用户收入——避免少数高收入用户拉高均值)
df["user_income"].fillna(df["user_income"].median(), inplace=True)
# 2. 均值填充(适用于无极端值的字段,如商品评分——评分通常集中在3-5分,均值有代表性)
df["product_score"].fillna(df["product_score"].mean(), inplace=True)
# 3. 分组填充(按业务维度精准填充,如按“城市”分组填充“用户年龄”——不同城市年龄分布差异大)
df["user_age"].fillna(df.groupby("city")["user_age"].transform("median"), inplace=True)
``` |
| 3. 填充法(分类型) | 缺失率较低且字段为分类型(如“用户职业、商品品类、用户性别”),需结合字段频次或业务规则选择填充值 | ```python
# 1. 众数填充(适用于高频值明确的字段,如“用户职业”——“白领”占比达40%,用众数填充更贴合实际)
df["user_occupation"].fillna(df["user_occupation"].mode()[0], inplace=True)
# 2. 特殊值填充(适用于无明显众数的字段,如“用户婚姻状态”——缺失时标“未知”,后续可通过行为数据间接判断)
df["marital_status"].fillna("未知", inplace=True)
``` |
| 4. 模型预测填充 | 缺失率中等(10%-30%)且字段重要(如“用户LTV(生命周期价值)”,直接影响用户分层),且有其他关联字段可用于预测 | ```python
# 用“消费频次、客单价”两个关联字段,通过线性回归预测缺失的“用户LTV”
from sklearn.linear_model import LinearRegression
# 1. 筛选无缺失的训练数据(仅用LTV非空的记录训练模型)
train_df = df[df["user_ltv"].notna()]
X_train = train_df[["consume_freq", "avg_order_value"]] # 特征:消费频次、客单价
y_train = train_df["user_ltv"] # 目标变量:用户LTV
# 2. 训练线性回归模型
model = LinearRegression()
model.fit(X_train, y_train)
# 3. 预测缺失值(用模型为LTV缺失的记录生成预测值)
missing_df = df[df["user_ltv"].isna()]
X_missing = missing_df[["consume_freq", "avg_order_value"]]
df.loc[df["user_ltv"].isna(), "user_ltv"] = model.predict(X_missing)
``` |
#### 业务案例(电商用户数据清洗)
某电商平台用户表中,“user_age”(用户年龄)缺失率15%,“user_city”(用户城市)缺失率5%,需结合业务场景处理:
- 对“user_age”:按“user_city”分组填充中位数(如北京用户年龄中位数32岁、上海30岁、广州29岁)——不同城市的年龄分布差异显著,分组填充比全局填充更精准;
- 对“user_city”:标“未知”而非删除——后续可通过“用户收货地址、IP属地”等数据补充,直接删除会丢失“用户消费记录”等其他有效信息。
### (二)问题2:异常值处理——“先识别,再判断,后处理”
异常值(如“订单金额100万元”“用户年龄150岁”)并非都是“错误数据”:可能是录入错误(需修正),也可能是真实极端值(如奢侈品订单、高净值用户)。CDA分析师需先通过科学方法识别异常,再结合业务逻辑判断性质,最后选择处理策略,避免误删有效数据。
#### 异常值识别的三种核心方法
1. **统计法**:适用于数值型字段,通过数据分布识别异常——
- “3σ原则”:若数据服从正态分布,超出“均值±3倍标准差”的数值视为异常;
- “四分位距(IQR)”:超出“Q1-1.5×IQR”(下边界)或“Q3+1.5×IQR”(上边界)的数值视为异常(Q1为25分位数,Q3为75分位数);
2. **业务法**:按业务规则直接定义异常(如“订单金额<0”“用户注册时间晚于当前时间”“商品库存为负数”);
3. **可视化法**:用箱线图、直方图直观呈现数据分布,快速定位离群点(如箱线图中超出“须”范围的点即为异常值)。
#### 常见处理策略与CDA实操
| 处理策略 | 适用场景 | 工具与代码示例(Python/Pandas) |
|----------------|-----------------------------------|-------------------------------------------|
| 1. 修正法 | 异常值为明确的录入错误(如“订单金额100000元”应为“1000元”,多输一位0;“用户年龄200岁”应为“20岁”,多输一位0) | ```python
# 1. 修正订单金额:金额>10000元且无“奢侈品”标签,视为多输一位0(普通商品金额通常<10000元)
mask = (df["order_amount"] > 10000) & (df["product_tag"] != "奢侈品")
df.loc[mask, "order_amount"] = df.loc[mask, "order_amount"] / 10 # 除以10修正
# 2. 修正时间异常:注册时间晚于当前时间,视为系统录入错误,设为当前时间
from datetime import datetime
current_time = datetime.now()
df["register_time"] = pd.to_datetime(df["register_time"]) # 先转换为datetime格式
mask = df["register_time"] > current_time
df.loc[mask, "register_time"] = current_time
``` |
| 2. 截断法 | 异常值为极端值但需保留数据趋势(如“用户年龄150岁”,业务分析仅关注18-80岁用户;“订单金额10万元”,超出99%用户的消费能力) | ```python
# 1. 年龄截断:将<18岁的设为18岁,>80岁的设为80岁(符合业务关注的用户范围)
df["user_age"] = df["user_age"].clip(lower=18, upper=80)
# 2. 订单金额截断:按99分位数截断(保留99%的正常数据,避免极端值影响均值计算)
q99 = df["order_amount"].quantile(0.99) # 计算99分位数
df["order_amount"] = df["order_amount"].clip(upper=q99) # 超出99分位数的金额设为99分位数
``` |
| 3. 分组处理法 | 异常值在特定分组内为正常数据(如“普通商品订单金额10万为异常,但奢侈品订单10万为正常;一线城市用户收入5万/月为正常,三线城市为异常”) | ```python
# 按“商品品类”分组处理订单金额异常:普通品类≤1万,奢侈品≤10万,超出则标记为缺失
def process_abnormal(row):
if row["product_category"] == "奢侈品":
# 奢侈品金额≤10万为正常,超出则设为10万(截断)
return row["order_amount"] if row["order_amount"] <= 100000 else 100000
else:
# 普通品类金额≤1万为正常,超出则标记为缺失(后续单独核查)
return row["order_amount"] if row["order_amount"] <= 10000 else None
# 应用分组处理逻辑,生成清洗后的金额字段
df["order_amount_clean"] = df.apply(process_abnormal, axis=1)
``` |
| 4. 保留法 | 异常值为真实业务数据(如“大客户一次性下单100万采购企业福利”“高净值用户购买百万级奢侈品”),删除会丢失关键业务信息 | ```python
# 仅标记异常值,不删除/修正,后续分析时单独关注
# 按99分位数定义异常(订单金额>99分位数视为异常)
q99 = df["order_amount"].quantile(0.99)
df["is_abnormal"] = (df["order_amount"] > q99).astype(int) # 1为异常,0为正常
# 分析时区分正常与异常数据(如分别统计两类数据的GMV占比)
normal_gmv = df[df["is_abnormal"] == 0]["order_amount"].sum()
abnormal_gmv = df[df["is_abnormal"] == 1]["order_amount"].sum()
print(f"正常订单GMV:{normal_gmv:.2f}元,占比:{normal_gmv/(normal_gmv+abnormal_gmv):.2%}")
print(f"异常订单GMV:{abnormal_gmv:.2f}元,占比:{abnormal_gmv/(normal_gmv+abnormal_gmv):.2%}")
``` |
#### 业务案例(金融信贷数据清洗)
某银行信贷申请表中,“user_income”(用户月收入)存在异常值,处理流程如下:
1. **识别异常**:用箱线图发现5条“收入>100万元/月”的记录,远高于其他用户(多数用户收入在5000-50000元/月);
2. **判断性质**:结合“user_occupation”(用户职业)字段核查——3条记录的职业为“企业高管”,经风控部门确认是真实高收入用户;2条记录的职业为“普通职员”,大概率是录入错误(多输一位0,实际应为10万元/月);
3. **处理策略**:保留3条高管记录,修正2条普通职员记录的收入为10万元/月——既避免误删有效数据,又修正错误,确保后续信贷审批模型输入准确。
### (三)问题3:重复值处理——“区分类型,保留有效”
重复值(如用户重复提交订单、数据同步时重复导入)会导致“统计结果虚高”(如订单数重复计算、用户数多算)。CDA分析师需先判断重复类型(完全重复/部分重复/逻辑重复),再结合业务逻辑选择去重策略,避免丢失有效信息。
#### 常见重复类型与处理策略
| 重复类型 | 定义 | 适用场景 | 工具与代码示例(Python/SQL) |
|----------------|-----------------------------------|-----------------------------------|-------------------------------------------|
| 1. 完全重复 | 所有字段值完全相同(如数据同步时未去重,导致同一条订单记录重复导入) | 无业务意义的重复(重复记录未包含新信息,仅增加数据量) | ```python
# Python:删除完全重复的行,保留第一条(避免数据量翻倍)
df_clean = df.drop_duplicates(keep="first")
# SQL:删除表中完全重复的行(需通过临时表实现,避免直接删除原表数据)
CREATE TABLE temp_order AS -- 创建临时表存储去重后的数据
SELECT DISTINCT * FROM order_table;
DROP TABLE order_table; -- 删除原表
ALTER TABLE temp_order RENAME TO order_table; -- 临时表重命名为原表
``` |
| 2. 部分重复 | 关键字段重复(如“user_id+order_id”相同),但非关键字段不同(如订单状态从“待支付”更新为“已支付”,生成两条记录) | 需保留最新/最有效记录(重复记录包含业务状态更新,需选择有价值的一条) | ```python
# Python:按“user_id+order_id”分组,保留“订单状态更新时间”最新的记录(确保状态为最新)
# 先按更新时间降序排序,再去重(保留第一条即最新记录)
df_clean = df.sort_values("update_time", ascending=False).drop_duplicates(subset=["user_id", "order_id"], keep="first")
# SQL:按“user_id+order_id”分组,用ROW_NUMBER()取最新更新时间的记录
SELECT *
FROM (
SELECT
*,
-- 按更新时间降序编号,最新记录编号为1
ROW_NUMBER() OVER(PARTITION BY user_id, order_id ORDER BY update_time DESC) AS rn
FROM order_table
) t
WHERE t.rn = 1; -- 仅保留编号为1的最新记录
``` |
| 3. 逻辑重复 | 关键字段不同但实际为同一主体(如“用户ID=123”与“用户ID=456”为同一用户,因账号合并未同步数据;“商品ID=A001”与“商品ID=B001”为同一商品,因品类调整更名) | 需业务部门确认合并规则(重复源于业务规则变更,需人工确认关联关系) | ```python
# 按业务部门提供的“用户合并映射表”,将重复用户ID统一为一个主ID
# 映射表:key为待合并ID,value为主ID(如456、789合并到123)
merge_map = {"456": "123", "789": "123"}
# 替换用户ID:存在于映射表的ID替换为主ID,否则保留原ID
df["user_id"] = df["user_id"].map(merge_map).fillna(df["user_id"])
# 合并后按主ID重新聚合数据(如汇总同一用户的订单金额、消费次数)
df_clean = df.groupby("user_id").agg({
"order_amount": "sum", # 汇总订单总金额
"order_count": "sum", # 汇总订单总数
"last_order_time": "max" # 取最后一次下单时间
}).reset_index()
``` |
#### 业务案例(零售门店数据清洗)
某连锁零售门店的销售表中存在重复记录,处理流程如下:
1. **识别重复**:以“store_id(门店ID)+sale_date(销售日期)+product_id(商品ID)”为组合键,发现20条重复记录——同一门店、同一日期、同一商品的销售额分别为100元与200元(手工录入时重复提交);
2. **判断逻辑**:业务部门确认“同一商品单日销售额不会重复录入,大概率是录入时金额误输”,需保留真实销售规模;
3. **处理策略**:按组合键分组,取“sale_amount(销售额)”的均值((100+200)/2=150元)——既去重又避免丢失真实销售信息,确保后续库存核算与营收统计准确。
### (四)问题4:格式不一致处理——“统一标准,消除歧义”
格式不一致(如日期格式“2024/10/31”与“2024-10-31”“31-10-2024”并存,手机号“13812345678”与“138-1234-5678”混用)会导致“字段无法关联”(如按日期筛选时漏选部分数据)、“统计结果偏差”(如手机号去重时将同一号码视为不同值)。CDA分析师需按“业务标准”统一格式,消除数据歧义。
#### 常见格式问题与处理策略
| 格式问题 | 处理策略 | 工具与代码示例(Python) |
|----------------|-----------------------------------|-------------------------------------------|
| 1. 日期格式不一致 | 统一为“YYYY-MM-DD”(适用于日期)或“YYYY-MM-DD HH:MM:SS”(适用于时间戳),便于后续按时间维度分析(如按月/按日聚合) | ```python
# 转换多种日期格式为“YYYY-MM-DD”(自动识别常见格式,无需手动指定)
df["order_date"] = pd.to_datetime(
df["order_date"],
format=None, # 自动识别“%Y/%m/%d”“%Y-%m-%d”“%d-%m-%Y”等格式
errors="coerce" # 无法识别的格式设为NaT(后续单独处理)
).dt.date # 提取日期部分(若需保留时间,删除“.dt.date”)
# 查看无法识别的日期(便于后续人工核查)
unrecognized_dates = df[df["order_date"].isna()]["order_date_original"].unique()
print(f"无法识别的日期格式:{unrecognized_dates}")
``` |
| 2. 文本格式不一致 | 统一大小写(如“产品类别”统一为小写)、去除多余空格(如“用户姓名”前后的空格)、标准化编码(如“性别”“职业”统一表述) | ```python
# 1. 去除文本前后空格(如“用户姓名”录入时误加空格,导致“张三”与“ 张三 ”视为不同值)
df["user_name"] = df["user_name"].str.strip()
# 2. 统一大小写(如“产品类别”字段“Clothes”“clothes”“CLOTHES”统一为小写)
df["product_category"] = df["product_category"].str.lower()
# 3. 标准化编码(如“性别”字段统一为“男/女”,替代“Male/Female”“1/0”)
gender_map = {"Male": "男", "Female": "女", "1": "男", "0": "女", "男性": "男", "女性": "女"}
df["gender"] = df["gender"].map(gender_map).fillna("未知") # 无法映射的标“未知”
``` |
| 3. 数值格式不一致 | 去除非数值字符(如“金额”中的“元”“,”)、统一单位(如“角”“分”转换为“元”),确保数值字段可用于计算 | ```python
# 1. 去除金额中的非数值字符(如“100元”“1,000元”转换为纯数字)
# 正则表达式“[^d.]”匹配非数字、非小数点的字符,替换为空
df["amount"] = df["amount"].str.replace(r"[^d.]", "", regex=True).astype(float)
# 2. 统一单位(如“金额单位”为“角”的转换为“元”,1元=10角)
mask = df["amount_unit"] == "角"
df.loc[mask, "amount"] = df.loc[mask, "amount"] / 10 # 角转元
df["amount_unit"] = "元" # 统一单位字段为“元”
``` |
| 4. 编码不一致 | 解决中文乱码问题(如文件编码为“GBK”,读取时用“UTF-8”导致乱码),统一为“UTF-8”编码(通用编码格式) | ```python
# 1. 读取文件时指定正确编码(如GBK编码的CSV文件,避免乱码)
df = pd.read_csv("user_data.csv", encoding="gbk")
# 2. 保存文件时统一为UTF-8编码(便于后续工具读取,避免编码冲突)
# “utf-8-sig”比“utf-8”多包含BOM头,兼容Windows系统
df.to_csv("user_data_clean.csv", encoding="utf-8-sig", index=False)
``` |
#### 业务案例(跨境电商数据清洗)
某跨境电商平台的订单表中,“order_time”(下单时间)格式混乱,包含“2024/10/31”“31-10-2024”“Oct 31, 2024”“2024.10.31”四种格式,处理流程如下:
1. **统一格式**:用`pd.to_datetime`自动识别格式,转换为“YYYY-MM-DD HH:MM:SS”(若无时间部分,默认补“00:00:00”);
2. **处理异常**:发现3条无法识别的格式(如“2024/13/31”,月份错误),联系技术部门核查原始日志,修正为“2024/12/31”;
3. **应用价值**:统一格式后,可按“月份”筛选“2024年10月订单”,精准计算月度GMV与订单量,避免因格式问题导致的数据遗漏(如原“31-10-2024”可能被误判为“2024年31月10日”,导致筛选时排除)。
## 三、CDA分析师数据清洗全流程:从“探查”到“验证”的闭环
数据清洗不是“碎片化处理单个问题”,而是“数据探查→问题处理→清洗验证”的全流程闭环。CDA分析师需按步骤有序执行,确保无遗漏、无偏差,避免“处理完缺失值,又发现异常值”的重复工作。
### (一)步骤1:数据探查——“全面诊断,定位问题”
数据探查是数据清洗的前提,核心目标是“全面了解数据质量现状”,明确缺失值、异常值、重复值的分布情况,为后续处理提供依据。常用探查维度与方法如下:
| 探查维度 | 核心动作 | 工具与代码示例(Python) |
|----------------|-----------------------------------|-------------------------------------------|
| 1. 基础信息探查 | ① 确认数据量(总行数、总字段数);② 查看字段名称与数据类型(如“order_amount”是否为数值型);③ 统计各字段缺失率(识别高缺失率字段) | ```python
# 1. 数据量与字段数(快速判断数据规模是否符合预期)
print(f"数据总行数:{len(df)},总字段数:{len(df.columns)}")
# 2. 字段信息(查看字段名称、数据类型、非空值数量)
print("n字段基础信息:")
print(df.info()) # 重点关注“Non-Null Count”(非空值数)与“Dtype”(数据类型)
# 3. 缺失率统计(按缺失率降序排列,重点关注缺失率>5%的字段)
missing_rate = df.isnull().sum() / len(df) * 100
missing_fields = missing_rate[missing_rate > 0].sort_values(ascending=False)
print("n各字段缺失率(%):")
if len(missing_fields) > 0:
print(missing_fields)
else:
print("无缺失字段")
``` |
| 2. 数值型字段探查 | ① 查看统计描述(均值、中位数、标准差、最值),识别极端值;② 用可视化工具(箱线图、直方图)呈现数据分布,定位异常值 | ```python
# 1. 数值型字段统计描述(重点关注“min”“max”“mean”“50%”,判断是否存在异常)
print("数值型字段统计描述:")
print(df.describe()) # 仅输出数值型字段的统计信息
# 2. 查看极值(如订单金额的最大值、最小值,快速判断是否存在负数或超大值)
print(f"n订单金额最大值:{df['order_amount'].max()}元,最小值:{df['order_amount'].min()}元")
print(f"用户年龄最大值:{df['user_age'].max()}岁,最小值:{df['user_age'].min()}岁")
# 3. 箱线图可视化(直观识别异常值,以订单金额为例)
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示问题
plt.boxplot(df["order_amount"], vert=False) # 横向箱线图,便于查看数值
plt.title("订单金额箱线图(红色圆点为异常值)")
plt.xlabel("订单金额(元)")
plt.show()
``` |
| 3. 分类型字段探查 | ① 统计各字段唯一值数量(识别格式混乱的字段,如“性别”唯一值过多);② 查看高频值分布(判断是否存在不合理值);③ 定位非标准值(如“性别”中的“未知123”“男_”) | ```python
# 1. 分类型字段唯一值数量(选择典型分类型字段,如职业、性别、商品品类)
categorical_cols = ["user_occupation", "gender", "product_category"]
print("分类型字段唯一值数量:")
for col in categorical_cols:
print(f"{col}:{df[col].nunique()}个唯一值") # 唯一值过多可能存在格式问题
# 2. 查看高频值(如用户职业TOP5,判断是否符合业务认知)
print("n用户职业TOP5分布:")
print(df["user_occupation"].value_counts().head())
# 3. 定位非标准值(以性别为例,预设正常值为“男/女/未知”)
normal_gender = ["男", "女", "未知"]
abnormal_gender = df[~df["gender"].isin(normal_gender)]["gender"].unique()
print(f"n性别字段非标准值:{abnormal_gender}") # 如“Male”“1”“女_”等均为非标准值
``` |
| 4. 逻辑一致性探查 | 验证数据是否符合业务逻辑(如“订单金额≥0”“注册时间≤下单时间”“商品库存≥0”),定位逻辑矛盾的记录 | ```python
# 1. 订单金额<0的记录数(不符合“金额非负”的业务逻辑)
negative_amount_count = len(df[df["order_amount"] < 0])
print(f"订单金额<0的记录数:{negative_amount_count}")
# 2. 注册时间晚于下单时间的记录数(用户不可能在注册前下单)
# 先将时间字段转换为datetime格式
df["register_time"] = pd.to_datetime(df["register_time"])
df["order_time"] = pd.to_datetime(df["order_time"])
invalid_time_count = len(df[df["register_time"] > df["order_time"]])
print(f"注册时间晚于下单时间的记录数:{invalid_time_count}")
# 3. 商品库存<0的记录数(库存不可能为负)
negative_stock_count = len(df[df["product_stock"] < 0])
print(f"商品库存<0的记录数:{negative_stock_count}")
``` |
### (二)步骤2:问题处理——“按优先级,系统解决”
基于数据探查结果,需按“业务优先级”有序处理问题,避免“先处理非核心字段,后发现核心字段问题需重新返工”。优先级排序原则如下:
1. **第一优先级**:关键业务字段的问题(如“order_id、user_id”的缺失/重复/异常——这些字段是数据关联的核心,缺失会导致后续分析无法开展);
2. **第二优先级**:分析核心指标相关字段的问题(如“order_amount、user_age、consume_freq”的格式异常/缺失——这些字段直接影响GMV、用户分层等核心指标计算);
3. **第三优先级**:非核心辅助字段的问题(如“user_interest、product_description”的缺失/格式——这些字段仅用于辅助分析,不影响核心结论)。
#### 处理过程中的核心原则
- **记录追溯**:每处理一类问题(如缺失值填充、异常值修正),需同步记录“处理策略、代码逻辑、处理效果”(如“user_age用分组中位数填充,填充前缺失率15%,填充后0%”),便于后续复盘与追溯;
- **业务确认**:复杂问题(如逻辑重复、异常值性质判断)需先与业务部门沟通(如“用户ID=123与456是否为同一用户”需运营部门确认),避免主观判断导致错误;
- **分步验证**:处理完一类问题后,简单核验效果(如缺失值处理后,查看缺失率是否下降),再进入下一类问题,避免问题叠加难以定位。
### (三)步骤3:清洗验证——“双重核验,确保可用”
数据清洗后需从“技术指标”与“业务逻辑”双维度验证,确保数据质量达标后,再进入后续分析环节。常用验证方法如下:
| 验证维度 | 核心动作 | 工具与代码示例 |
|----------------|-----------------------------------|-------------------------------------------|
| 1. 技术指标验证 | ① 核心字段缺失率(需≤1%,关键字段如order_id需0缺失);② 重复率(按关键字段核查,如order_id重复率需0);③ 格式统一性(如日期格式统一率100%,数值字段无文本字符) | ```python
# 1. 清洗后核心字段缺失率(选择order_id、user_id、order_amount等关键字段)
core_fields = ["order_id", "user_id", "order_amount", "order_time"]
post_missing_rate = df_clean[core_fields].isnull().sum() / len(df_clean) * 100
print("清洗后核心字段缺失率(%):")
print(post_missing_rate)
# 2. 清洗后重复率(按order_id核查,重复率=1-去重后记录数/总记录数)
unique_order_count = df_clean["order_id"].nunique()
duplicate_rate = 1 - (unique_order_count / len(df_clean))
print(f"n清洗后订单ID重复率:{duplicate_rate:.2%}") # 目标:重复率≤0.1%
# 3. 格式统一性(以日期字段为例,检查是否均为“YYYY-MM-DD”格式)
# 正则表达式匹配“YYYY-MM-DD”格式(年4位,月2位,日2位)
date_format_pattern = r"^d{4}-d{2}-d{2}$"
# 将日期转换为字符串后匹配正则
date_format_correct = df_clean["order_date"].astype(str).str.match(date_format_pattern).sum()
date_format_rate = date_format_correct / len(df_clean)
print(f"订单日期格式统一率:{date_format_rate:.2%}") # 目标:统一率100%
``` |
| 2. 业务逻辑验证 | ① 与业务数据对比(如清洗后订单金额总和与财务部门统计数据的差异率需≤1%);② 与常识逻辑对比(如“用户年龄18-80岁占比≥95%”“订单金额≥0占比100%”) | ```python
# 1. 与财务数据对比(验证订单金额总和的准确性)
clean_gmv = df_clean["order_amount"].sum()
finance_gmv = 1250000 # 财务部门统计的同期GMV(125万元)
diff_rate = abs(clean_gmv - finance_gmv) / finance_gmv # 计算差异率
print(f"清洗后GMV:{clean_gmv:.2f}元,财务GMV:{finance_gmv:.2f}元")
print(f"GMV差异率:{diff_rate:.2%}") # 目标:差异率≤1%
# 2. 与常识逻辑对比(验证用户年龄分布的合理性)
valid_age_count = len(df_clean[(df_clean["user_age"] >= 18) & (df_clean["user_age"] <= 80)])
valid_age_rate = valid_age_count / len(df_clean)
print(f"n用户年龄18-80岁占比:{valid_age_rate:.2%}") # 目标:占比≥95%
# 3. 验证订单金额非负(业务逻辑要求订单金额≥0)
negative_amount_count = len(df_clean[df_clean["order_amount"] < 0])
print(f"清洗后订单金额<0的记录数:{negative_amount_count}") # 目标:0条
``` |
| 3. 抽样对比验证 | 随机抽取10-20条记录,人工对比清洗前后的数据(如缺失值是否填充、异常值是否修正、格式是否统一),确保处理逻辑正确执行 | ```python
# 随机抽样10条记录(设置random_state确保结果可复现)
sample_indices = df.index.sample(n=10, random_state=42)
# 提取清洗前后的关键字段数据(如user_id、order_amount、user_age、order_date)
pre_sample = df.loc[sample_indices][["user_id", "order_amount", "user_age", "order_date"]]
post_sample = df_clean.loc[sample_indices][["user_id", "order_amount", "user_age", "order_date"]]
# 合并清洗前后的数据,便于对比
comparison = pd.concat(
[pre_sample, post_sample],
axis=1,
keys=["清洗前", "清洗后"] # 增加层级索引,区分清洗前后
)
print("清洗前后数据抽样对比(10条记录):")
print(comparison)
# 人工核查要点:缺失值是否填充、异常值是否修正、格式是否统一
``` |
## 四、实战案例:CDA分析师清洗电商女装订单数据
### (一)业务背景
某电商平台计划分析“2024年10月女装品类营收情况”,支撑11月营销活动策划。原始订单数据(共10000条)存在四类问题,需CDA分析师清洗后用于分析:
1. 缺失值:“user_city”(用户城市)缺失率12%,“product_size”(商品尺码)缺失率8%;
2. 异常值:“order_amount”(订单金额)存在-50元(录入错误)、100000元(非女装订单误分类到女装品类);
3. 重复值:“order_id”(订单ID)重复20条(数据同步时未去重);
4. 格式问题:“order_time”(下单时间)格式混乱,包含“2024/10/31”“31-10-2024”“Oct 31, 2024”三种格式。
### (二)CDA清洗全流程实操
#### 1. 数据探查(明确问题分布)
- **基础信息**:数据共10000条,12个字段,核心字段(order_id、user_id、order_amount)无缺失,但“user_city”“product_size”存在缺失;
- **异常识别**:订单金额<0的记录5条(均为-50元),>10000元的记录3条(经运营部门确认,2条为奢侈品订单误分类,1条为女装大单(企业采购工装));
- **重复识别**:order_id重复20条,均为完全重复(字段值完全一致);
- **格式识别**:order_time格式混乱,3种格式占比分别为“2024/10/31”(60%)、“31-10-2024”(30%)、“Oct 31, 2024”(10%)。
#### 2. 问题处理(按优先级执行)
##### (1)第一优先级:处理order_id重复问题(核心关联字段)
```python
# 按order_id去重,保留第一条记录(完全重复,无业务价值差异)
df_clean = df.drop_duplicates(subset=["order_id"], keep="first")
print(f"去重前记录数:{len(df)},去重后记录数:{len(df_clean)}") # 去重后9980条
# ① 修正order_amount异常值
# 修正-50元为50元(录入时多输负号)
df_clean.loc[df_clean["order_amount"] == -50, "order_amount"] = 50
# 剔除2条误分类的奢侈品订单(product_category设为“奢侈品”,非“女装”)
df_clean = df_clean[~((df_clean["order_amount"] > 10000) & (df_clean["product_category"] != "女装"))]
# 保留1条女装大单(product_category为“女装”,真实业务数据)
# ② 统一order_time格式为“YYYY-MM-DD HH:MM:SS”
df_clean["order_time"] = pd.to_datetime(df_clean["order_time"], format=None, errors="coerce")
# 查看无法识别的记录(无,3种格式均被成功识别)
print(f"order_time无法识别的记录数:{len(df_clean[df_clean['order_time'].isna()])}")
# ① 填充user_city缺失值(按user_province分组填充中位数城市)
# 先统计各省份的城市中位数(按出现频次排序,取最频繁的城市)
province_city_mode = df_clean.groupby("user_province")["user_city"].agg(lambda x: x.mode()[0]).to_dict()
# 按省份填充城市
df_clean["user_city"] = df_clean.apply(
lambda row: province_city_mode[row["user_province"]] if pd.isna(row["user_city"]) else row["user_city"],
axis=1
)
# ② 填充product_size缺失值(女装默认均码,标“均码”)
df_clean["product_size"].fillna("均码", inplace=True)
# 核心字段缺失率(order_id、user_id、order_amount、order_time均为0%)
core_fields = ["order_id", "user_id", "order_amount", "order_time", "user_city", "product_size"]
post_missing_rate = df_clean[core_fields].isnull().sum() / len(df_clean) * 100
print("清洗后核心字段缺失率:")
print(post_missing_rate) # 均为0%
# order_id重复率(0%)
unique_order_count = df_clean["order_id"].nunique()
duplicate_rate = 1 - (unique_order_count / len(df_clean))
print(f"order_id重复率:{duplicate_rate:.2%}") # 0%
# order_time格式统一率(100%)
date_format_pattern = r"^d{4}-d{2}-d{2} d{2}:d{2}:d{2}$"
date_format_correct = df_clean["order_time"].astype(str).str.match(date_format_pattern).sum()
date_format_rate = date_format_correct / len(df_clean)
print(f"order_time格式统一率:{date_format_rate:.2%}") # 100%
# 与财务数据对比(清洗后GMV 850000元,财务统计856800元,差异率0.8%,符合要求)
clean_gmv = df_clean["order_amount"].sum()
finance_gmv = 856800
diff_rate = abs(clean_gmv - finance_gmv) / finance_gmv
print(f"GMV差异率:{diff_rate:.2%}") # 0.8% ≤ 1%
# 订单金额非负(0条异常)
negative_amount_count = len(df_clean[df_clean["order_amount"] < 0])
print(f"订单金额<0的记录数:{negative_amount_count}") # 0条
基于清洗后的 9978 条女装订单数据,开展以下分析并支撑业务决策:
子品类营收分析:计算 “连衣裙、卫衣、外套” 等子品类的 GMV 占比,发现 “连衣裙占比 35%” 为核心品类,建议 11 月重点推广;
用户地域分析:统计各城市营收贡献,发现 “北京、上海、广州” 贡献 60% 营收,计划针对这三个城市推出专属优惠券;
订单时间分析:按小时聚合订单量,发现 “20:00-22:00” 为下单高峰,建议 11 月大促在该时段加大直播推广力度。
表现:用户表 “user_age” 缺失率 15%,未评估字段重要性,直接删除所有缺失行,导致样本量减少 15%,后续用户分层分析的样本代表性下降;
规避策略:
优先选择 “填充法”(如分组中位数、模型预测),仅在 “缺失率 > 30% 且字段非核心” 时考虑删除;
若字段重要(如 user_age 影响用户分层),即使缺失率较高(10%-30%),也需通过模型预测等方式补全,而非简单删除。
表现:将 “订单金额> 10000 元” 的记录全部视为异常值截断,未结合 “product_category” 判断,误删真实的奢侈品女装订单,导致营收统计偏差;
规避策略:
表现:订单表中 “order_id 重复但状态不同”(如 “待支付” 与 “已支付”),未判断重复类型,直接去重保留第一条,导致 “已支付” 的有效订单被删除,营收统计漏算;
规避策略:
先判断重复类型(完全重复 / 部分重复):完全重复可直接去重,部分重复需按 “业务优先级” 保留有效记录(如保留最新状态、金额较大的记录);
表现:清洗后未核验 GMV 与财务数据的一致性,直接用数据开展营收分析,导致 “营收增长 20%” 的结论实为数据重复计算(未去重),误导业务决策;
规避策略:
清洗后必须完成 “技术指标 + 业务逻辑 + 抽样对比” 三重验证,确保数据质量达标;
核心指标(如 GMV、用户数)需与业务部门的统计数据对比,差异率需控制在 1% 以内。
数据清洗的本质是 “CDA 分析师用业务逻辑修复数据的‘瑕疵’,为后续分析保驾护航”—— 它不是 “繁琐的体力活”,而是 “考验业务敏感度与技术能力的核心环节”。对 CDA 分析师而言,优秀的清洗能力不仅能提升分析效率,更能确保结论的可靠性,避免 “差之毫厘,谬以千里” 的决策偏差。
在数据驱动的时代,“数据质量” 已成为企业的核心竞争力之一。CDA 分析师作为数据清洗的 “质量守护者”,需持续深化 “业务理解 + 工具应用” 的双重能力:既要熟练掌握 SQL、Python 等工具的实操技巧,更要深入理解业务逻辑 —— 每一个清洗策略的选择,都应贴合具体业务场景,最终让 “干净的数据” 转化为 “可靠的业务洞察”,支撑企业精准决策。
在 “神经网络与卡尔曼滤波融合” 的理论基础上,Python 凭借其丰富的科学计算库(NumPy、FilterPy)、深度学习框架(PyTorch、T ...
2025-10-23在工业控制、自动驾驶、机器人导航、气象预测等领域,“状态估计” 是核心任务 —— 即从含噪声的观测数据中,精准推断系统的真 ...
2025-10-23在数据分析全流程中,“数据清洗” 恰似烹饪前的食材处理:若食材(数据)腐烂变质、混杂异物(脏数据),即便拥有精湛的烹饪技 ...
2025-10-23在人工智能领域,“大模型” 已成为近年来的热点标签:从参数超 1750 亿的 GPT-3,到万亿级参数的 PaLM,再到多模态大模型 GPT-4 ...
2025-10-22在 MySQL 数据库的日常运维与开发中,“更新数据是否会影响读数据” 是一个高频疑问。这个问题的答案并非简单的 “是” 或 “否 ...
2025-10-22在企业数据分析中,“数据孤岛” 是制约分析深度的核心瓶颈 —— 用户数据散落在注册系统、APP 日志、客服记录中,订单数据分散 ...
2025-10-22在神经网络设计中,“隐藏层个数” 是决定模型能力的关键参数 —— 太少会导致 “欠拟合”(模型无法捕捉复杂数据规律,如用单隐 ...
2025-10-21在特征工程流程中,“单变量筛选” 是承上启下的关键步骤 —— 它通过分析单个特征与目标变量的关联强度,剔除无意义、冗余的特 ...
2025-10-21在数据分析全流程中,“数据读取” 常被误解为 “简单的文件打开”—— 双击 Excel、执行基础 SQL 查询即可完成。但对 CDA(Cert ...
2025-10-21在实际业务数据分析中,我们遇到的大多数数据并非理想的正态分布 —— 电商平台的用户消费金额(少数用户单次消费上万元,多数集 ...
2025-10-20在数字化交互中,用户的每一次操作 —— 从电商平台的 “浏览商品→加入购物车→查看评价→放弃下单”,到内容 APP 的 “点击短 ...
2025-10-20在数据分析的全流程中,“数据采集” 是最基础也最关键的环节 —— 如同烹饪前需备好新鲜食材,若采集的数据不完整、不准确或不 ...
2025-10-20在数据成为新时代“石油”的今天,几乎每个职场人都在焦虑: “为什么别人能用数据驱动决策、升职加薪,而我面对Excel表格却无从 ...
2025-10-18数据清洗是 “数据价值挖掘的前置关卡”—— 其核心目标是 “去除噪声、修正错误、规范格式”,但前提是不破坏数据的真实业务含 ...
2025-10-17在数据汇总分析中,透视表凭借灵活的字段重组能力成为核心工具,但原始透视表仅能呈现数值结果,缺乏对数据背景、异常原因或业务 ...
2025-10-17在企业管理中,“凭经验定策略” 的传统模式正逐渐失效 —— 金融机构靠 “研究员主观判断” 选股可能错失收益,电商靠 “运营拍 ...
2025-10-17在数据库日常操作中,INSERT INTO SELECT是实现 “批量数据迁移” 的核心 SQL 语句 —— 它能直接将一个表(或查询结果集)的数 ...
2025-10-16在机器学习建模中,“参数” 是决定模型效果的关键变量 —— 无论是线性回归的系数、随机森林的树深度,还是神经网络的权重,这 ...
2025-10-16在数字化浪潮中,“数据” 已从 “辅助决策的工具” 升级为 “驱动业务的核心资产”—— 电商平台靠用户行为数据优化推荐算法, ...
2025-10-16在大模型从实验室走向生产环境的过程中,“稳定性” 是决定其能否实用的关键 —— 一个在单轮测试中表现优异的模型,若在高并发 ...
2025-10-15