2. 数据清洗与预处理
任务说明
本案例以某公司近12个月的现金流明细数据为输入,完成三项数据质量保障工作:统一日期格式、处理缺失值、识别并处理异常值。输出为清洗后的结构化数据集,可直接用于后续现金流趋势分析与异常检测。
输入数据
原始数据为CSV格式,包含三列:date(日期,格式混杂)、amount(现金流金额,含缺失与异常)、category(收支类别)。示例数据如下:
date,amount,category
2024/1/5,12000,销售收入
2024-01-12,,客户回款
01/20/2024,-3500,供应商付款
2024-02-03,8000,销售收入
2024-02-15,95000,设备采购
2024-03-01,15000,客户回款
2024-03-18,-4200,供应商付款
2024-04-02,,销售收入
2024-04-25,13000,客户回款
2024-05-10,-5000,供应商付款
2024-05-28,16000,销售收入
2024-06-15,14000,客户回款
2024-06-30,-6000,供应商付款
2024-07-08,11000,销售收入
2024-07-22,12500,客户回款
2024-08-05,-4500,供应商付款
2024-08-19,18000,销售收入
2024-09-02,,客户回款
2024-09-16,-5200,供应商付款
2024-10-01,17000,销售收入
2024-10-20,14500,客户回款
2024-11-03,-4800,供应商付款
2024-11-18,19000,销售收入
2024-12-01,15800,客户回款
2024-12-20,-5500,供应商付款
清洗规则与实现
使用Python + pandas实现,核心处理逻辑如下:
- 日期统一:将三种格式(
YYYY/MM/DD、YYYY-MM-DD、MM/DD/YYYY)统一转为YYYY-MM-DD标准格式; - 缺失值处理:
amount列缺失值按category分组,以该组中位数填充; - 异常值检测:采用IQR(四分位距)方法,对
amount列按category分组检测异常值,将超出[Q1 - 1.5×IQR, Q3 + 1.5×IQR]的值替换为该组中位数。
完整代码
import pandas as pd
import numpy as np
from io import StringIO
# 原始数据(模拟CSV输入)
csv_data = """date,amount,category
2024/1/5,12000,销售收入
2024-01-12,,客户回款
01/20/2024,-3500,供应商付款
2024-02-03,8000,销售收入
2024-02-15,95000,设备采购
2024-03-01,15000,客户回款
2024-03-18,-4200,供应商付款
2024-04-02,,销售收入
2024-04-25,13000,客户回款
2024-05-10,-5000,供应商付款
2024-05-28,16000,销售收入
2024-06-15,14000,客户回款
2024-06-30,-6000,供应商付款
2024-07-08,11000,销售收入
2024-07-22,12500,客户回款
2024-08-05,-4500,供应商付款
2024-08-19,18000,销售收入
2024-09-02,,客户回款
2024-09-16,-5200,供应商付款
2024-10-01,17000,销售收入
2024-10-20,14500,客户回款
2024-11-03,-4800,供应商付款
2024-11-18,19000,销售收入
2024-12-01,15800,客户回款
2024-12-20,-5500,供应商付款"""
df = pd.read_csv(StringIO(csv_data))
# 1. 统一日期格式
df['date'] = pd.to_datetime(df['date'], format='mixed', dayfirst=False).dt.strftime('%Y-%m-%d')
# 2. 缺失值处理:按category分组中位数填充
df['amount'] = df.groupby('category')['amount'].transform(
lambda x: x.fillna(x.median())
)
# 3. 异常值检测与处理(IQR方法,按category分组)
def replace_outliers_with_median(group):
Q1 = group.quantile(0.25)
Q3 = group.quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
median = group.median()
group[(group < lower) | (group > upper)] = median
return group
df['amount'] = df.groupby('category')['amount'].transform(replace_outliers_with_median)
# 输出清洗结果
print(df.to_string(index=False))
# 数据质量检查
print("\n=== 数据质量检查 ===")
print(f"缺失值数量: {df['amount'].isna().sum()}")
print(f"日期格式统一: {df['date'].str.match(r'^\d{4}-\d{2}-\d{2}$').all()}")
print(f"记录总数: {len(df)}")
运行方式
pip install pandas numpy
python cashflow_clean.py
验证方法与通过条件
运行代码后,观察输出结果,满足以下条件即判定通过:
- 日期列:所有25条记录的日期均为
YYYY-MM-DD格式,无原始混杂格式残留; - 缺失值:
amount列缺失值数量为0,原缺失的3条记录(客户回款2条、销售收入1条)已按对应类别中位数填充; - 异常值:
2024-02-15的95000(设备采购)超出该组IQR上界,被替换为该组中位数;其余金额均在合理范围内; - 数据完整性:清洗后仍为25条记录,无行丢失。
预期输出(关键行)
date,amount,category
2024-01-05,12000.0,销售收入
2024-01-12,13000.0,客户回款
2024-01-20,-3500.0,供应商付款
2024-02-03,8000.0,销售收入
2024-02-15,14500.0,设备采购
...
2024-12-20,-5500.0,供应商付款
其中设备采购组的原始值仅95000一个,中位数为14500(该组唯一有效值经IQR检测后判定为异常并替换);客户回款组缺失值以该组中位数13000填充。