4. Pandas基础:Series与DataFrame、数据读取与写入、索引与选择、缺失值处理

好,咱们进入量化金融最核心的数据处理工具——Pandas。说实话,我见过不少新手,一上来就急着跑模型,结果数据清洗就卡了三天。Pandas就是那个让你少加班的利器。

我个人习惯把Pandas比作「Excel的超级升级版」。你想想看,Excel处理10万行数据就卡得不行,Pandas处理百万级数据跟玩似的。而且,它能跟Python的数学库无缝衔接,这才是关键。

4.1 Series与DataFrame:两种核心数据结构

Pandas里就两种核心结构:Series和DataFrame。说白了,Series就是一列数据,DataFrame就是多列数据组成的表格。

4.1.1 Series:一维数据容器

Series就像带标签的数组。每个元素都有索引,默认是0、1、2...但你可以自定义。

import pandas as pd
import numpy as np

# 从列表创建Series
s = pd.Series([100, 200, 300, 400, 500])
print(s)
# 输出:
# 0    100
# 1    200
# 2    300
# 3    400
# 4    500
# dtype: int64

# 自定义索引
s_with_index = pd.Series([100, 200, 300], index=['a', 'b', 'c'])
print(s_with_index)
# a    100
# b    200
# c    300
# dtype: int64

我在项目中遇到过一个问题:用字典创建Series时,键会自动变成索引。这个特性在构建股票代码映射表时特别有用。

# 从字典创建
stock_prices = {'AAPL': 175.30, 'GOOGL': 140.50, 'MSFT': 330.20}
s = pd.Series(stock_prices)
print(s)
# AAPL     175.30
# GOOGL    140.50
# MSFT     330.20
# dtype: float64

4.1.2 DataFrame:二维表格结构

DataFrame才是咱们真正的主力。它由多个Series组成,每个Series是一列。你可以把它想象成一个带行索引和列索引的Excel表格。

# 从字典创建DataFrame
data = {
    '股票代码': ['AAPL', 'GOOGL', 'MSFT', 'AMZN'],
    '收盘价': [175.30, 140.50, 330.20, 145.80],
    '成交量': [50000000, 25000000, 35000000, 40000000],
    '日期': ['2024-01-15', '2024-01-15', '2024-01-15', '2024-01-15']
}
df = pd.DataFrame(data)
print(df)
#   股票代码   收盘价       成交量          日期
# 0   AAPL  175.30  50000000  2024-01-15
# 1  GOOGL  140.50  25000000  2024-01-15
# 2   MSFT  330.20  35000000  2024-01-15
# 3   AMZN  145.80  40000000  2024-01-15
我的小技巧:创建DataFrame时,如果数据量很大(比如上万行),用字典方式创建比用列表嵌套列表快得多。这是我在处理日频股票数据时发现的。

4.2 数据读取与写入:跟文件打交道

做量化金融,数据来源无非是CSV、Excel、数据库。Pandas提供了统一的接口,让你不用关心底层文件格式。

4.2.1 读取CSV文件

CSV是最常见的格式。我建议你养成一个好习惯:读取时指定数据类型,能省去后面很多麻烦。

# 基础读取
df = pd.read_csv('stock_data.csv')

# 指定数据类型(推荐)
df = pd.read_csv(
    'stock_data.csv',
    dtype={'股票代码': str, '收盘价': float, '成交量': int},
    parse_dates=['日期'],  # 自动解析日期
    index_col='日期'       # 用日期列做索引
)

# 只读取前1000行(预览数据时用)
df_sample = pd.read_csv('stock_data.csv', nrows=1000)
我曾经踩过的坑:股票代码如果不指定为字符串类型,Pandas会把它当成数字,导致"000001"变成"1"。这个bug让我排查了整整两个小时。

4.2.2 读取Excel文件

Excel文件需要额外安装openpyxl或xlrd库。我个人偏爱openpyxl,因为它支持.xlsx格式。

# 读取Excel
df = pd.read_excel('stock_data.xlsx', sheet_name='Sheet1')

# 读取多个sheet
sheets = pd.read_excel('stock_data.xlsx', sheet_name=None)
# 返回一个字典,键是sheet名,值是DataFrame

4.2.3 写入文件

处理完数据后,保存结果也很简单。

# 保存为CSV
df.to_csv('processed_stock_data.csv', index=False)

# 保存为Excel
df.to_excel('processed_stock_data.xlsx', sheet_name='清洗后数据', index=False)
注意:保存时设置index=False,否则Pandas会把行索引也写进文件,导致多出一列没用的数据。

4.3 索引与选择:精准定位数据

数据读进来了,怎么取数据?Pandas提供了两种索引方式:基于标签的loc和基于位置的iloc。嗯,这里要注意区分。

4.3.1 使用loc按标签选择

# 假设df的索引是日期
df = pd.read_csv('stock_data.csv', index_col='日期', parse_dates=['日期'])

# 选择某一行
row = df.loc['2024-01-15']

# 选择多行
rows = df.loc['2024-01-15':'2024-01-20']

# 选择某几列
cols = df.loc[:, ['收盘价', '成交量']]

# 同时选择行和列
subset = df.loc['2024-01-15':'2024-01-20', ['收盘价', '成交量']]

4.3.2 使用iloc按位置选择

# 选择第一行
first_row = df.iloc[0]

# 选择前5行
first_5 = df.iloc[:5]

# 选择第一列
first_col = df.iloc[:, 0]

# 选择第1到第3行,第0到第2列
subset = df.iloc[0:3, 0:2]
核心区别:loc是闭区间(包含两端),iloc是半开区间(不包含右端)。这个细节我刚开始也搞混过。

4.3.3 布尔索引:条件筛选

这是我最常用的方式。你想筛选出所有收盘价大于200的股票,一行代码搞定。

# 筛选收盘价大于200的股票
high_price = df[df['收盘价'] > 200]

# 多条件筛选
filtered = df[(df['收盘价'] > 200) & (df['成交量'] > 30000000)]

# 使用query方法(更简洁)
filtered = df.query('收盘价 > 200 and 成交量 > 30000000')
我的习惯:条件复杂时用query,简单条件用布尔索引。query的语法更接近SQL,可读性更好。

4.4 缺失值处理:数据清洗的核心

真实数据永远不完美。缺失值就是最常见的「脏数据」。我处理过的股票数据里,节假日、停牌日都会产生缺失值。

4.4.1 检测缺失值

# 检查哪些值是缺失的
print(df.isnull())

# 统计每列的缺失值数量
print(df.isnull().sum())

# 查看缺失值占比
print(df.isnull().mean() * 100)

4.4.2 删除缺失值

# 删除包含任何缺失值的行
df_clean = df.dropna()

# 删除某列有缺失值的行
df_clean = df.dropna(subset=['收盘价'])

# 删除全部为缺失值的行
df_clean = df.dropna(how='all')

# 删除缺失值超过50%的列
df_clean = df.dropna(thresh=len(df) * 0.5, axis=1)
我曾经犯过的错:直接dropna删除所有缺失行,结果把停牌日的数据全删了,导致时间序列不连续。后来我改用填充方式处理。

4.4.3 填充缺失值

填充方式取决于业务场景。股票数据我一般用前向填充或插值。

# 用固定值填充
df['收盘价'] = df['收盘价'].fillna(0)

# 用均值填充
df['收盘价'] = df['收盘价'].fillna(df['收盘价'].mean())

# 前向填充(用上一个有效值填充)
df['收盘价'] = df['收盘价'].fillna(method='ffill')

# 后向填充
df['收盘价'] = df['收盘价'].fillna(method='bfill')

# 插值法(线性插值)
df['收盘价'] = df['收盘价'].interpolate(method='linear')
实战建议:股票数据用前向填充最合理。因为停牌日的价格应该沿用上一个交易日的数据。插值法适合连续变化的数据,比如温度、水位。

4.4.4 高级技巧:条件填充

# 只填充连续缺失不超过3天的数据
def fill_limited_missing(series, max_gap=3):
    # 标记连续缺失的组
    groups = series.isnull().astype(int).groupby(series.notnull().cumsum()).cumsum()
    # 只填充连续缺失不超过max_gap的
    series_filled = series.where(groups > max_gap, series.fillna(method='ffill'))
    return series_filled

df['收盘价'] = fill_limited_missing(df['收盘价'], max_gap=3)

这个函数是我在实盘项目中写的。连续停牌超过3天的股票,我会标记为异常,而不是盲目填充。你想想看,如果一只股票连续停牌一周,用前向填充显然不合理。

知识体系总览

下面这张图总结了本章的核心内容,方便你回顾。

Pandas基础核心知识体系 Pandas基础 Series与DataFrame 一维 vs 二维 从列表/字典创建 索引与数据类型 数据读取与写入 CSV / Excel / 数据库 指定数据类型 to_csv / to_excel 索引与选择 loc(标签) vs iloc(位置) 布尔索引 query方法 缺失值处理 检测:isnull / sum 删除:dropna 填充:ffill / interpolate 核心原则:先检测 → 再决策(删除/填充)→ 最后验证

好了,这一章的内容就到这里。Pandas基础是量化金融的「基本功」,你花时间把它练扎实了,后面跑模型的时候会特别顺畅。记住我上面提到的那些坑,能帮你少走不少弯路。

专注资料整理