4. Pandas 数据清洗:缺失值处理、重复值处理、数据类型转换、异常值过滤

数据清洗是数据分析流程中最耗时但最关键的一环。原始数据通常包含缺失值、重复记录、错误的数据类型以及异常数值,这些问题会直接影响 KPI 指标计算的准确性和监控大屏的可靠性。本章将系统讲解 Pandas 中四大核心清洗操作,确保数据进入分析管道前达到“干净、一致、可用”的状态。

核心原则:清洗操作应保留原始数据的备份(df.copy()),避免不可逆修改。每次清洗后建议输出数据形状(df.shape)以验证操作效果。

4.1 缺失值处理

缺失值在 Pandas 中通常表示为 NaN(Not a Number)或 None。处理策略包括:删除缺失行/列、填充固定值或统计量、以及向前/向后填充。

方法 函数 适用场景
检测缺失 df.isnull() / df.isna() 定位缺失位置,常配合 sum() 统计总数
删除缺失行 df.dropna() 缺失比例极低(<5%)且随机分布
删除缺失列 df.dropna(axis=1) 某列缺失超过 80% 时直接丢弃
填充固定值 df.fillna(value) 已知业务默认值(如 0、未知)
填充均值/中位数 df.fillna(df.mean()) 数值型连续变量,分布较对称
前向填充 df.fillna(method='ffill') 时间序列数据,用上一时刻值填充
后向填充 df.fillna(method='bfill') 时间序列数据,用下一时刻值填充
import pandas as pd
import numpy as np

# 示例数据:包含缺失值的销售记录
df = pd.DataFrame({
    '日期': ['2025-01-01', '2025-01-02', '2025-01-03', '2025-01-04'],
    '销售额': [1200, np.nan, 1500, np.nan],
    '订单量': [30, 25, np.nan, 28],
    '客户评分': [4.5, 4.0, 3.8, np.nan]
})

# 1. 查看缺失概况
print("缺失数量:\n", df.isnull().sum())

# 2. 填充销售额缺失值为该列均值
df['销售额'] = df['销售额'].fillna(df['销售额'].mean())

# 3. 订单量缺失值用前向填充(假设时间序列)
df['订单量'] = df['订单量'].fillna(method='ffill')

# 4. 客户评分缺失值用固定值 4.0 填充
df['客户评分'] = df['客户评分'].fillna(4.0)

print("清洗后数据:\n", df)
实战建议:在 KPI 监控大屏场景中,对于“当日销售额”等关键指标,若缺失比例低于 10%,优先使用前向填充或均值填充;若缺失比例过高,应标记该指标为“数据异常”并在大屏上给出告警提示,而非强行填充。
缺失值处理决策流程 检测缺失值 缺失比例? 删除行/填充 均值/中位数填充 删除列/告警 完成缺失值处理 低:<5% 中:5%~50% 高:>50%

4.2 重复值处理

重复值会导致统计指标(如总和、均值)被放大,尤其在订单记录、用户行为日志中常见。Pandas 提供基于全部列或指定子集的去重功能。

# 示例数据:包含重复行
df_dup = pd.DataFrame({
    '用户ID': [101, 102, 101, 103, 102],
    '购买金额': [200, 350, 200, 400, 350],
    '时间戳': ['2025-01-01', '2025-01-01', '2025-01-01', '2025-01-02', '2025-01-01']
})

# 1. 检测重复行
print("完全重复行数:", df_dup.duplicated().sum())

# 2. 查看重复行
print("重复行详情:\n", df_dup[df_dup.duplicated(keep=False)])

# 3. 删除完全重复行(保留第一次出现)
df_clean = df_dup.drop_duplicates()
print("去重后数据:\n", df_clean)

# 4. 基于指定列去重(例如:用户ID + 时间戳)
df_clean_subset = df_dup.drop_duplicates(subset=['用户ID', '时间戳'], keep='last')
print("基于用户ID和时间戳去重(保留最后一条):\n", df_clean_subset)
注意:在 KPI 监控场景中,重复值可能来自数据采集系统的重试机制或日志重复上报。建议先通过 df.duplicated(keep=False) 查看所有重复行,确认是“业务允许重复”还是“系统异常重复”,再决定去重策略。对于时间序列 KPI,通常保留最新记录(keep='last')。

4.3 数据类型转换

数据类型不匹配是数据清洗中的“隐形杀手”。常见问题包括:数值列被读为字符串、日期列未解析为 datetime 类型、布尔值存储为 0/1 整数。正确的数据类型能提升计算效率并避免隐式错误。

目标类型 转换方法 典型场景
数值型 pd.to_numeric() 将含逗号或货币符号的字符串转为浮点数
日期时间 pd.to_datetime() 将 '2025-01-01' 字符串转为 datetime64
类别型 df['col'].astype('category') 将城市、性别等有限取值列转为 category 节省内存
布尔型 df['col'].astype(bool) 将 0/1 或 '是'/'否' 转为布尔值
# 示例:常见数据类型问题与修复
df_types = pd.DataFrame({
    '日期': ['2025/01/01', '2025/01/02', '2025/01/03'],
    '销售额': ['1,200', '1,500', '1,800'],
    '是否达标': ['是', '否', '是'],
    '城市代码': [1, 2, 1]
})

# 查看原始类型
print("原始类型:\n", df_types.dtypes)

# 1. 日期转换
df_types['日期'] = pd.to_datetime(df_types['日期'], format='%Y/%m/%d')

# 2. 销售额:去除逗号并转为浮点数
df_types['销售额'] = pd.to_numeric(df_types['销售额'].str.replace(',', ''))

# 3. 是否达标:映射为布尔值
df_types['是否达标'] = df_types['是否达标'].map({'是': True, '否': False})

# 4. 城市代码:转为类别型
df_types['城市代码'] = df_types['城市代码'].astype('category')

print("转换后类型:\n", df_types.dtypes)
print("数据预览:\n", df_types)
性能提示:在监控大屏的实时数据管道中,建议在数据入库阶段就完成类型转换,避免每次查询时重复转换。对于百万级数据,将字符串列转为 category 类型可减少 50% 以上的内存占用。
数据类型转换流程 原始数据 object / string pd.to_numeric 数值型 pd.to_datetime 日期时间 astype('category') 类别型 清洗后数据 类型正确 提升计算效率 避免隐式错误

4.4 异常值过滤

异常值是指明显偏离数据分布的值,可能由录入错误、传感器故障或特殊事件引起。常用检测方法包括:Z-Score 法、IQR(四分位距)法、业务规则过滤。

方法 原理 阈值 适用场景
Z-Score 计算每个值与均值的标准差倍数 |Z| > 3 近似正态分布的数据
IQR 法 基于四分位距:Q1 - 1.5*IQR 到 Q3 + 1.5*IQR 超出上下限 偏态分布或含离群点的数据
业务规则 根据领域知识设定合理范围 自定义 有明确业务约束(如年龄 0-120)
# 示例:使用 IQR 法过滤异常值
df_outlier = pd.DataFrame({
    '店铺': ['A', 'B', 'C', 'D', 'E'],
    '日销售额': [5000, 5200, 4800, 30000, 5100]  # D 店明显异常
})

# 计算四分位数
Q1 = df_outlier['日销售额'].quantile(0.25)
Q3 = df_outlier['日销售额'].quantile(0.75)
IQR = Q3 - Q1

lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

print(f"正常范围:{lower_bound:.0f} ~ {upper_bound:.0f}")

# 过滤异常值
df_normal = df_outlier[
    (df_outlier['日销售额'] >= lower_bound) & 
    (df_outlier['日销售额'] <= upper_bound)
]

print("过滤后数据:\n", df_normal)

# 使用 Z-Score 方法(scipy 可选)
from scipy import stats
z_scores = stats.zscore(df_outlier['日销售额'])
df_z_clean = df_outlier[abs(z_scores) < 3]
print("Z-Score 过滤后:\n", df_z_clean)
重要提醒:异常值不一定是“错误”。在 KPI 监控中,异常值可能代表“双十一大促销售额暴增”或“服务器宕机导致订单归零”。建议先标记异常值,由业务人员确认后再决定是剔除、修正还是保留并单独分析。直接删除异常值可能导致重要业务信号丢失。
异常值过滤方法对比 异常值过滤方法对比 Z-Score 法 公式:Z = (x - μ) / σ 阈值:|Z| > 3 适用:正态分布 优点:标准化可比较 缺点:受异常值影响 IQR 四分位距法 IQR = Q3 - Q1 下限:Q1 - 1.5*IQR 上限:Q3 + 1.5*IQR 适用:偏态分布 优点:稳健不受极端值影响 业务规则法 自定义范围 例:销售额 > 0 例:年龄 0~120 适用:有明确约束 优点:领域知识驱动 建议:先标记异常值,业务确认后再处理,避免丢失重要信号

4.5 综合清洗流程

在实际项目中,上述四个步骤通常按固定顺序执行。以下是一个完整的清洗管道示例,可直接用于 KPI 监控数据的预处理阶段。

def clean_kpi_data(df):
    """
    完整的 KPI 数据清洗函数
    步骤顺序:类型转换 → 缺失值处理 → 重复值处理 → 异常值过滤
    """
    df_clean = df.copy()
    
    # Step 1: 数据类型转换
    for col in df_clean.select_dtypes(include=['object']).columns:
        # 尝试转为数值
        df_clean[col] = pd.to_numeric(df_clean[col], errors='ignore')
    
    # Step 2: 缺失值处理(数值列用中位数填充,类别列用众数填充)
    for col in df_clean.select_dtypes(include=['float64', 'int64']).columns:
        df_clean[col] = df_clean[col].fillna(df_clean[col].median())
    for col in df_clean.select_dtypes(include=['object', 'category']).columns:
        df_clean[col] = df_clean[col].fillna(df_clean[col].mode()[0] if not df_clean[col].mode().empty else '未知')
    
    # Step 3: 重复值处理(基于全部列去重)
    df_clean = df_clean.drop_duplicates()
    
    # Step 4: 异常值过滤(IQR 法,仅对数值列)
    for col in df_clean.select_dtypes(include=['float64', 'int64']).columns:
        Q1 = df_clean[col].quantile(0.25)
        Q3 = df_clean[col].quantile(0.75)
        IQR = Q3 - Q1
        lower = Q1 - 1.5 * IQR
        upper = Q3 + 1.5 * IQR
        # 将异常值替换为边界值(也可选择标记或删除)
        df_clean[col] = df_clean[col].clip(lower, upper)
    
    return df_clean

# 使用示例
raw_data = pd.read_csv('kpi_daily.csv')
cleaned_data = clean_kpi_data(raw_data)
print(f"清洗前:{raw_data.shape},清洗后:{cleaned_data.shape}")
清洗管道设计原则:先做类型转换确保后续操作的数据类型正确;再做缺失值填充避免去重时因 NaN 导致误判;然后去重减少数据量;最后做异常值过滤,因为异常值检测(如 IQR)在数据完整且无重复时更准确。此顺序适用于 90% 以上的 KPI 监控场景。
原始数据 raw_data Step 1 类型转换 Step 2 缺失值处理 Step 3 重复值处理 Step 4 异常值过滤 清洗后数据 cleaned_data 清洗顺序原则 ① 先类型转换 确保后续操作类型正确 ② 再缺失值填充 避免 NaN 干扰去重 ← 上一章 📖 返回目录 下一章 →