第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亿的流水,明显是录入错误。

我常用的方法:

  1. 3σ原则:数据服从正态分布时,超过均值±3倍标准差的值视为异常。
  2. 箱线图法:用四分位数判断。超过Q1-1.5IQR或Q3+1.5IQR的算异常。
  3. 业务规则:比如单笔金额不能超过总资产的某个比例。
# 箱线图法示例
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点更新,而内部系统是实时记录。

对齐步骤:

  1. 统一时间格式:全部转成datetime类型。
  2. 设置时间索引:把时间列设为索引。
  3. 重采样:按固定频率(如每天、每小时)重新采样。
  4. 填充缺失时间点:用前向填充或插值。
# 统一时间格式
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 本章知识体系

下面这张图,把咱们刚才讲的内容串起来了:

数据采集与清洗知识体系 数据采集 缺失值处理 异常值处理 数据标准化 时间序列对齐 干净数据集 CSV / Excel 读取 编码处理 删除 / 填充 / 插值 前向填充常用 3σ / 箱线图 / 业务规则 异常值≠错误值 Z-score / Min-Max 消除量纲影响 统一时间格式 重采样 / 填充 可直接用于建模 质量有保障

数据清洗这件事,说白了就是「磨刀不误砍柴工」。你花80%的时间在数据准备上,剩下20%的时间建模,结果往往比反过来要好得多。

我个人习惯把清洗流程写成函数,每次有新数据直接调用。这样既省时间,又不容易出错。

专注资料整理