第3章:数据采集与清洗
数据采集与清洗,说白了就是给模型准备「干净的口粮」。
我见过太多人,模型算法选得花里胡哨,结果数据一塌糊涂,最后跑出来的结果连自己都不敢信。嗯,咱们今天就把这块硬骨头啃下来。
3.1 从Excel/CSV导入数据
我个人习惯用pandas来读数据。它就像一把瑞士军刀,啥格式都能切。
import pandas as pd
# 读取CSV
df_csv = pd.read_csv('资金流水.csv', encoding='utf-8')
# 读取Excel
df_excel = pd.read_excel('资金流水.xlsx', sheet_name='Sheet1')
# 看一眼数据长啥样
print(df_csv.head())
print(df_csv.info())
这里有个坑:编码问题。我曾经在项目中遇到一个CSV文件,怎么读都是乱码。折腾了半天,发现是Excel保存时用了GBK编码。解决办法很简单:
df = pd.read_csv('文件.csv', encoding='gbk')
小技巧: 不确定编码时,可以用
chardet 库自动检测。但别太依赖它,有时候会翻车。
3.2 处理缺失值
资金流数据里,缺失值很常见。比如某天没交易记录,或者某笔金额没录入。
我一般先看看缺失情况:
# 查看每列缺失值数量
print(df.isnull().sum())
# 缺失比例
print(df.isnull().mean())
处理方式有三种:
- 直接删除:如果缺失比例很小(比如<5%),直接删掉那几行。简单粗暴。
- 填充:用均值、中位数、或者前向/后向填充。资金流数据我常用前向填充,因为时间序列有连续性。
- 插值:用线性插值或时间插值。适合有规律的数据。
# 前向填充
df.fillna(method='ffill', inplace=True)
# 线性插值
df.interpolate(method='linear', inplace=True)
注意: 千万别一股脑全用均值填充。资金流数据往往有趋势和季节性,均值填充会破坏这种结构。
3.3 处理异常值
异常值,说白了就是那些「离谱」的数据。比如某天突然多了一笔100亿的流水,明显是录入错误。
我常用的方法:
- 3σ原则:数据服从正态分布时,超过均值±3倍标准差的值视为异常。
- 箱线图法:用四分位数判断。超过Q1-1.5IQR或Q3+1.5IQR的算异常。
- 业务规则:比如单笔金额不能超过总资产的某个比例。
# 箱线图法示例
Q1 = df['金额'].quantile(0.25)
Q3 = df['金额'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
# 找出异常值
outliers = df[(df['金额'] < lower) | (df['金额'] > upper)]
print(f'发现 {len(outliers)} 个异常值')
我曾经在项目中遇到一个客户,他们的资金流数据里有个「幽灵账户」,每天固定出现一笔0.01元的交易。用箱线图法死活检测不出来,后来还是靠业务规则揪出来的。
核心原则: 异常值不一定要删除。有时候异常值本身就有业务含义,比如「大额资金调动」可能预示着重大事件。
3.4 数据标准化
标准化,就是把不同量纲的数据拉到同一个尺度上。比如金额是万元级别,交易笔数是百级别,不标准化的话,模型会「偏科」。
常用两种方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score标准化 | (x - μ) / σ | 数据近似正态分布 |
| Min-Max标准化 | (x - min) / (max - min) | 数据有明确边界 |
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score
scaler = StandardScaler()
df['金额_标准化'] = scaler.fit_transform(df[['金额']])
# Min-Max
scaler = MinMaxScaler()
df['金额_归一化'] = scaler.fit_transform(df[['金额']])
我个人偏好Z-score,因为它对异常值不那么敏感。但如果你要输入神经网络,Min-Max可能更合适。
3.5 时间序列对齐
资金流预测,时间对齐是灵魂。不同数据源的时间戳可能不一致,比如银行流水是每天下午5点更新,而内部系统是实时记录。
对齐步骤:
- 统一时间格式:全部转成datetime类型。
- 设置时间索引:把时间列设为索引。
- 重采样:按固定频率(如每天、每小时)重新采样。
- 填充缺失时间点:用前向填充或插值。
# 统一时间格式
df['日期'] = pd.to_datetime(df['日期'])
# 设置索引
df.set_index('日期', inplace=True)
# 按天重采样,取每天最后一笔
df_daily = df.resample('D').last()
# 填充缺失日期
df_daily = df_daily.asfreq('D', method='ffill')
避坑指南: 我曾经在项目中遇到一个坑——不同数据源的时间精度不一样。一个精确到秒,一个精确到天。对齐时一定要先统一精度,否则会出现「对不齐」的尴尬。
3.6 本章知识体系
下面这张图,把咱们刚才讲的内容串起来了:
数据清洗这件事,说白了就是「磨刀不误砍柴工」。你花80%的时间在数据准备上,剩下20%的时间建模,结果往往比反过来要好得多。
我个人习惯把清洗流程写成函数,每次有新数据直接调用。这样既省时间,又不容易出错。