热线电话:13121318867

登录
首页大数据时代CDA 数据分析师:数据清洗实战指南 —— 筑牢数据分析的 “质量防线”
CDA 数据分析师:数据清洗实战指南 —— 筑牢数据分析的 “质量防线”
2025-10-23
收藏

在数据分析全流程中,“数据清洗” 恰似烹饪前的食材处理:若食材(数据)腐烂变质、混杂异物(脏数据),即便拥有精湛的烹饪技巧(分析模型),也无法烹制出符合要求的佳肴(可靠结论)。据行业调研显示,CDA(Certified Data Analyst)数据分析师约 60% 的工作时间都投入数据清洗 —— 其核心目标并非简单 “删除脏数据”,而是通过系统化处理,将 “格式混乱、缺失异常的不可用数据” 转化为 “干净一致、贴合业务需求的高质量数据”,为后续建模分析与业务决策夯实基础。

本文将从 CDA 分析师的实战视角,系统拆解数据清洗的核心认知、四类常见数据问题的处理策略、全流程操作规范,让 “数据清洗” 从 “繁琐的体力劳动” 升级为 “有逻辑、有策略的技术工作”。

一、核心认知:数据清洗的本质与 CDA 分析师的核心价值

数据清洗(Data Cleaning)并非 “删除错误数据” 的单一操作,而是一套完整的系统性流程:先识别数据中的质量问题(如缺失值异常值重复值、格式混乱),再结合业务逻辑制定针对性处理策略,最终输出可直接用于分析的高质量数据集。其本质是 “修复数据的‘瑕疵’,还原数据背后的业务真相”。

(一)数据清洗的三大核心目标

数据清洗需围绕 “完整性、一致性、准确性” 三大目标展开,三者共同构成数据质量的基础:

  1. 完整性:针对 “字段缺失” 问题,通过合理方式补全或标注(如 “用户年龄缺失” 时,结合用户所在城市的年龄分布填充中位数,而非直接删除整条记录),确保分析所需的关键字段无遗漏;

  2. 一致性:解决 “格式混乱” 问题,统一数据格式(如将日期统一为 “YYYY-MM-DD”)与编码规则(如 “性别” 字段统一用 “男 / 女”,替代 “1/0”“Male/Female” 等混合表述),避免因格式差异导致的分析偏差

  3. 准确性:修正 “数据异常” 问题,识别并修正录入错误(如 “订单金额为 - 100 元” 大概率是符号误输)、剔除无效数据(如测试环境产生的 “测试订单”、用户重复提交的表单),确保数据能真实反映业务情况。

(二)CDA 分析师与普通数据清洗的核心差异

普通数据清洗常停留在 “技术操作层面”(如用 Excel 删除重复行、用 0 填充缺失值),而 CDA 分析师的清洗工作需深度结合 “业务逻辑与分析目标”,两者在处理逻辑、目标导向等维度差异显著:

对比维度 普通数据清洗 CDA 分析师数据清洗
处理逻辑 按固定规则机械操作(如所有缺失值统一用 0 填充,不区分字段含义) 结合业务场景灵活选择策略(如 “用户年龄缺失用中位数填充,用户职业缺失标‘未知’,避免影响分层分析”)
目标导向 仅追求 “数据表面干净”,不关联后续分析需求 服务具体分析目标(如 “为用户分层分析做准备,需优先确保‘消费金额、复购频次’等核心字段无异常”)
工具选择 依赖 Excel 等可视化工具,仅支持 10 万条以内的小数据量 灵活组合 SQL、Python 等工具(SQL 用于海量数据去重,Python 用于复杂缺失值预测),适配多源 / 海量数据场景
结果验证 不做验证或仅简单核对数据量,忽略业务逻辑 结合业务场景做多层验证(如 “清洗后订单金额总和与财务部门统计数据对比,差异率需≤1%”)

(三)CDA 分析师在数据清洗中的核心角色

CDA 分析师并非 “数据清洗的机械执行者”,而是 “数据质量的守护者”,其核心价值体现在三个关键环节:

  1. 业务翻译者:将模糊的业务需求转化为明确的清洗目标(如 “需分析用户复购原因”,转化为 “需确保‘用户消费记录、复购间隔、优惠券使用情况’等字段无缺失异常”);

  2. 策略制定者:针对同一数据问题,根据业务场景选择最优方案(如 “用户收入缺失” 时,若需分析高收入用户特征,用 “同行业同职位收入中位数” 填充,而非简单用全局均值,避免极端值干扰);

  3. 质量把控者:清洗后通过 “业务逻辑核验” 确保数据可用(如 “订单金额需≥0”“用户年龄应在 18-80 岁合理范围”),而非仅满足 “缺失率≤1%” 等技术指标。

二、CDA 分析师必备的 4 类数据问题处理方法:场景、工具与实操

数据清洗的核心是 “针对性解决问题”。CDA 分析师需熟练处理 “缺失值异常值重复值、格式不一致” 四类高频问题,且每类问题的处理都需结合业务场景选择适配方法,避免 “一刀切” 的机械操作。

(一)问题 1:缺失值处理 ——“补全优先,删除为辅”

缺失值是最常见的数据质量问题(如用户注册时未填写 “职业”、订单系统故障导致 “支付时间” 缺失)。CDA 分析师需根据 “字段重要性、缺失率、业务含义” 三要素选择处理策略,优先通过补全保留数据价值,仅在必要时考虑删除。

常见处理策略与 CDA 实操

处理策略 适用场景 工具与代码示例(Python/Pandas)
1. 删除法 ① 缺失率极高(通常 > 30%)且字段非核心(如 “用户兴趣标签”,不影响营收分析);② 含缺失值的行无业务意义(如 “订单 ID 缺失的记录”,无法关联用户与商品信息) ```python

删除 “订单 ID” 缺失的行(核心字段,缺失则记录无效)

df_clean = df.dropna(subset=["order_id"])

删除 “兴趣标签” 字段(缺失率 40%,非核心,保留反而增加分析复杂度)

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条
(2)第二优先级:处理 order_amount 异常与 order_time 格式问题(核心指标字段
# ① 修正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()])}")
(3)第三优先级:处理 user_city 与 product_size 缺失问题(辅助字段
# ① 填充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)

3. 清洗验证(双重核验)

(1)技术指标验证
# 核心字段缺失率(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%
(2)业务逻辑验证
# 与财务数据对比(清洗后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 条女装订单数据,开展以下分析并支撑业务决策:

  1. 子品类营收分析:计算 “连衣裙、卫衣、外套” 等子品类的 GMV 占比,发现 “连衣裙占比 35%” 为核心品类,建议 11 月重点推广;

  2. 用户地域分析:统计各城市营收贡献,发现 “北京、上海、广州” 贡献 60% 营收,计划针对这三个城市推出专属优惠券;

  3. 订单时间分析:按小时聚合订单量,发现 “20:00-22:00” 为下单高峰,建议 11 月大促在该时段加大直播推广力度。

五、CDA 分析师数据清洗的常见误区与规避策略

(一)误区 1:盲目删除含缺失值的行,导致样本量骤减

表现:用户表 “user_age” 缺失率 15%,未评估字段重要性,直接删除所有缺失行,导致样本量减少 15%,后续用户分层分析的样本代表性下降;

规避策略

  • 优先选择 “填充法”(如分组中位数、模型预测),仅在 “缺失率 > 30% 且字段非核心” 时考虑删除;

  • 字段重要(如 user_age 影响用户分层),即使缺失率较高(10%-30%),也需通过模型预测等方式补全,而非简单删除。

(二)误区 2:用统一方法处理所有异常值,忽视业务逻辑

表现:将 “订单金额> 10000 元” 的记录全部视为异常值截断,未结合 “product_category” 判断,误删真实的奢侈品女装订单,导致营收统计偏差

规避策略

  • 异常值处理前,先结合业务字段(如品类、地域、用户职业)判断性质,避免 “一刀切”;

  • 对不确定性质的异常值,先标记而非直接删除,联系业务部门确认后再处理。

(三)误区 3:忽视重复值类型,直接完全去重

表现:订单表中 “order_id 重复但状态不同”(如 “待支付” 与 “已支付”),未判断重复类型,直接去重保留第一条,导致 “已支付” 的有效订单被删除,营收统计漏算;

规避策略

  • 先判断重复类型(完全重复 / 部分重复):完全重复可直接去重,部分重复需按 “业务优先级” 保留有效记录(如保留最新状态、金额较大的记录);

  • 去重前,用 “关键字段 + 非关键字段” 的组合判断重复性质,而非仅看关键字段

(四)误区 4:清洗后不做验证,直接进入分析环节

表现:清洗后未核验 GMV 与财务数据的一致性,直接用数据开展营收分析,导致 “营收增长 20%” 的结论实为数据重复计算(未去重),误导业务决策;

规避策略

  • 清洗后必须完成 “技术指标 + 业务逻辑 + 抽样对比” 三重验证,确保数据质量达标;

  • 核心指标(如 GMV、用户数)需与业务部门的统计数据对比,差异率需控制在 1% 以内。

六、结语

数据清洗的本质是 “CDA 分析师用业务逻辑修复数据的‘瑕疵’,为后续分析保驾护航”—— 它不是 “繁琐的体力活”,而是 “考验业务敏感度与技术能力的核心环节”。对 CDA 分析师而言,优秀的清洗能力不仅能提升分析效率,更能确保结论的可靠性,避免 “差之毫厘,谬以千里” 的决策偏差

在数据驱动的时代,“数据质量” 已成为企业的核心竞争力之一。CDA 分析师作为数据清洗的 “质量守护者”,需持续深化 “业务理解 + 工具应用” 的双重能力:既要熟练掌握 SQL、Python 等工具的实操技巧,更要深入理解业务逻辑 —— 每一个清洗策略的选择,都应贴合具体业务场景,最终让 “干净的数据” 转化为 “可靠的业务洞察”,支撑企业精准决策。

推荐学习书籍 《CDA一级教材》适合CDA一级考生备考,也适合业务及数据分析岗位的从业者提升自我。完整电子版已上线CDA网校,累计已有10万+在读~ !

免费加入阅读:https://edu.cda.cn/goods/show/3151?targetId=5147&preview=0

数据分析师资讯
更多

OK
客服在线
立即咨询
客服在线
立即咨询