第3章:Python数据处理基础——pandas核心数据结构与数据清洗实战

各位同学,欢迎来到量化策略自动化全链路实战的第三站。今天我们要聊的,是每个量化工程师都绕不开的吃饭家伙——pandas。说实话,我入行头两年,80%的调试时间都花在数据清洗上。你想想看,策略写得再漂亮,数据一塌糊涂,回测结果就是垃圾进垃圾出。所以这一章,咱们把pandas的底子打扎实。

3.1 核心数据结构:Series与DataFrame

pandas就两个核心数据结构:Series和DataFrame。Series说白了就是一维带标签的数组,DataFrame就是二维表格。我个人习惯把DataFrame想象成Excel里的sheet,Series就是其中一列。

3.1.1 Series:一维数据容器

创建Series很简单,传个列表或字典就行。我在项目中经常用它来存单只股票的收盘价序列。

import pandas as pd
import numpy as np

# 从列表创建
s = pd.Series([100, 102, 101, 105], index=['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04'])
print(s)

# 从字典创建
price_dict = {'2024-01-01': 100, '2024-01-02': 102, '2024-01-03': 101}
s2 = pd.Series(price_dict)

嗯,这里要注意:索引(index)是Series的灵魂。没有索引,数据就是一堆散沙。我见过新手直接把数据塞进去,结果索引自动生成0,1,2...后面做时间序列分析时全乱套了。

3.1.2 DataFrame:二维表格

DataFrame才是我们真正天天打交道的。创建方式很多,最常用的是从字典或CSV读入。

# 从字典创建DataFrame
data = {
    '股票代码': ['000001', '000002', '600519'],
    '收盘价': [10.5, 20.3, 1500.0],
    '成交量': [1000000, 500000, 200000]
}
df = pd.DataFrame(data)
print(df)

# 查看基本信息
print(df.info())      # 数据类型、非空计数
print(df.describe())  # 统计摘要

记得有一次,我接手一个老项目,DataFrame里混着字符串和数字,一算均值直接报错。从那以后,我每次拿到数据第一件事就是跑df.info(),看看各列类型对不对。

核心操作速查:

  • df.head(n):看前n行,默认5行
  • df.tail(n):看后n行
  • df.columns:获取列名列表
  • df.index:获取行索引
  • df['列名']:选取单列,返回Series
  • df[['列1', '列2']]:选取多列,返回DataFrame

3.2 数据读取与写入:CSV/Excel/数据库

量化交易的数据源五花八门。我最常用的是CSV,因为轻量、通用。但遇到需要给老板汇报时,Excel更友好。数据库嘛,生产环境必备。

3.2.1 CSV文件操作

CSV是量化界的通用语言。我建议你养成一个好习惯:读入时指定parse_datesindex_col,省得后面再转换。

# 读取CSV
df = pd.read_csv('stock_data.csv', 
                 parse_dates=['日期'], 
                 index_col='日期',
                 encoding='utf-8')

# 写入CSV
df.to_csv('cleaned_data.csv', encoding='utf-8-sig', index=True)

避坑指南:我曾经因为没指定encoding='utf-8-sig',导出的CSV在Excel里中文全乱码。后来我统一用utf-8-sig,再没出过问题。

3.2.2 Excel文件操作

Excel支持多sheet,适合整理不同维度的数据。比如一个文件里,sheet1放日线数据,sheet2放财务指标。

# 读取Excel
df = pd.read_excel('fund_data.xlsx', sheet_name='日线数据')

# 写入Excel,多sheet
with pd.ExcelWriter('output.xlsx') as writer:
    df_daily.to_excel(writer, sheet_name='日线数据', index=False)
    df_fund.to_excel(writer, sheet_name='财务指标', index=False)

3.2.3 数据库读写

生产环境我一般用SQLite做本地缓存,用MySQL做团队共享。pandas的read_sqlto_sql简直是神器。

from sqlalchemy import create_engine

# 连接SQLite数据库
engine = create_engine('sqlite:///quant.db')

# 从数据库读取
df = pd.read_sql("SELECT * FROM daily_data WHERE date >= '2024-01-01'", engine)

# 写入数据库
df.to_sql('daily_data', engine, if_exists='replace', index=False)

注意:写入数据库时,if_exists参数有三个选项:fail(表存在就报错)、replace(替换)、append(追加)。我建议第一次用replace,后续增量更新用append

3.3 数据清洗:缺失值、重复值、异常值

数据清洗占了我量化工作中至少40%的时间。你想想看,从交易所拿到的原始数据,经常有停牌日、除权除息、数据缺失等问题。不洗干净,策略回测就是自欺欺人。

3.3.1 缺失值处理

缺失值在pandas里显示为NaN。处理方式就三种:删除、填充、插值。我个人经验是:如果缺失比例小于5%,直接删除;如果大于5%,用前向填充或插值。

# 检查缺失值
print(df.isnull().sum())

# 删除缺失值
df_drop = df.dropna()  # 删除任何包含NaN的行
df_drop_col = df.dropna(axis=1)  # 删除任何包含NaN的列

# 填充缺失值
df_fill = df.fillna(method='ffill')  # 前向填充
df_fill_mean = df.fillna(df.mean())  # 用均值填充

# 插值(适合时间序列)
df_interp = df.interpolate(method='linear')

我的经验:处理股票数据时,停牌日的缺失值用前向填充最合理。因为停牌期间价格不变,用前一天的价格是符合逻辑的。但如果是财务数据,用均值填充更稳妥。

3.3.2 重复值处理

重复数据通常来自数据源重复推送或合并时的错误。我曾经因为没去重,回测时同一笔交易算了两次收益,结果策略收益率虚高20%。

# 检查重复行
print(df.duplicated().sum())

# 查看重复的具体行
print(df[df.duplicated(keep=False)])

# 删除重复行
df_unique = df.drop_duplicates()

# 按指定列去重
df_unique_subset = df.drop_duplicates(subset=['股票代码', '日期'])

嗯,这里keep参数很关键:keep='first'保留第一个,keep='last'保留最后一个,keep=False全部删除。我一般用keep='first',因为最早的数据通常最可靠。

3.3.3 异常值处理

异常值是最头疼的。比如某只股票突然出现-999的收盘价,或者成交量突然放大100倍。这些不处理,策略会做出疯狂决策。

常用的方法有两种:3σ原则和IQR(四分位距)法。

# 3σ原则:超出均值±3倍标准差视为异常
def detect_outliers_zscore(df, column, threshold=3):
    z_scores = np.abs((df[column] - df[column].mean()) / df[column].std())
    return df[z_scores > threshold]

# IQR方法:超出Q1-1.5*IQR或Q3+1.5*IQR视为异常
def detect_outliers_iqr(df, column):
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    return df[(df[column] < lower_bound) | (df[column] > upper_bound)]

# 处理异常值:替换为上下限
def cap_outliers(df, column):
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    df[column] = df[column].clip(lower, upper)
    return df

避坑指南:我曾经用3σ原则处理收益率数据,结果发现收益率分布是尖峰厚尾的,3σ把很多正常的极端行情都剔除了。后来我改用IQR方法,效果好了很多。记住:金融数据很少服从正态分布。

3.4 本章知识体系总览

下面这张图是我自己总结的pandas数据处理全流程。你照着这个顺序做,基本不会漏掉关键步骤。

pandas数据处理全链路 数据读取 CSV / Excel / 数据库 数据结构 Series / DataFrame 数据清洗 缺失值 / 重复值 / 异常值 数据输出 CSV / Excel / 数据库 详细处理步骤: 1. 读取数据 → 检查数据类型(df.info()) 2. 设置索引(时间序列用日期做索引) 3. 处理缺失值(删除/填充/插值) 4. 处理重复值(按业务逻辑去重) 5. 处理异常值(3σ / IQR / 截尾) 6. 数据类型转换(astype / to_datetime) 7. 数据排序(sort_index / sort_values) 8. 输出清洗后的数据 💡 核心原则 先清洗,再分析 保留原始数据备份 每次清洗记录操作日志

好了,这一章的内容就到这里。pandas的数据处理能力远不止这些,但上面这些已经能覆盖量化交易中90%的场景。你把这些练熟了,后面写策略回测、因子分析都会顺手很多。


公众号:蓝海数据掘金营,微信deep3321