3、Pandas基础(上):Series与DataFrame的创建、基本属性、索引与切片、数据筛选与过滤
做量化回测,说白了就是跟数据打交道。你每天面对的是K线、成交量、各种因子……这些数据怎么存、怎么取、怎么切?Pandas就是干这个的。
我个人习惯把Pandas比作「Excel的Pro版」。它比Excel快,比Excel灵活,而且能跟Python生态无缝衔接。今天咱们先啃下最核心的两个数据结构:Series和DataFrame。
核心知识点速览
- Series:一维带标签数组,类似带索引的列表
- DataFrame:二维表格,类似Excel工作表
- 索引与切片:怎么快速定位数据
- 数据筛选:条件过滤,选出你想要的
3.1 Series:一维数据的基础单元
Series是什么?你可以把它想象成一个带标签的列表。每个元素都有一个索引,就像Excel里每一行都有行号一样。
创建Series
最直接的方式是从列表创建:
import pandas as pd
# 从列表创建
s = pd.Series([1, 3, 5, 7, 9])
print(s)
# 输出:
# 0 1
# 1 3
# 2 5
# 3 7
# 4 9
# dtype: int64
看到没?左边是索引,右边是值。默认索引从0开始,但你可以自己指定:
# 指定索引
s = pd.Series([1, 3, 5, 7, 9], index=['a', 'b', 'c', 'd', 'e'])
print(s)
# a 1
# b 3
# c 5
# d 7
# e 9
# dtype: int64
我的习惯:在量化回测里,我经常用日期作为Series的索引。比如某只股票每天的收益率,用日期做索引,后面做时间序列分析会方便很多。
基本属性
Series有几个常用属性,我列一下:
| 属性 | 说明 | 示例 |
|---|---|---|
| values | 返回所有值(NumPy数组) | s.values |
| index | 返回索引对象 | s.index |
| dtype | 返回数据类型 | s.dtype |
| size | 返回元素个数 | s.size |
| shape | 返回形状元组 | s.shape |
print(s.values) # [1 3 5 7 9]
print(s.index) # Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
print(s.dtype) # int64
print(s.size) # 5
3.2 DataFrame:二维表格的核心
DataFrame才是重头戏。量化回测里,你的K线数据、因子数据,基本都是DataFrame。它就像一张Excel表格,有行有列,每列可以有不同的数据类型。
创建DataFrame
创建方式很多,我常用的是从字典创建:
# 从字典创建
data = {
'股票代码': ['000001', '000002', '000003'],
'收盘价': [10.5, 22.3, 15.8],
'成交量': [10000, 15000, 8000],
'日期': ['2024-01-01', '2024-01-01', '2024-01-01']
}
df = pd.DataFrame(data)
print(df)
# 股票代码 收盘价 成交量 日期
# 0 000001 10.5 10000 2024-01-01
# 1 000002 22.3 15000 2024-01-01
# 2 000003 15.8 8000 2024-01-01
也可以从列表嵌套创建,不过我个人觉得字典方式更直观,列名和数据的对应关系一目了然。
基本属性
DataFrame的属性比Series多一些,常用的有:
| 属性 | 说明 | 示例 |
|---|---|---|
| shape | 行数和列数 | df.shape → (3, 4) |
| columns | 列名 | df.columns |
| index | 行索引 | df.index |
| dtypes | 每列的数据类型 | df.dtypes |
| info() | 数据概览(含内存使用) | df.info() |
| describe() | 统计描述 | df.describe() |
print(df.shape) # (3, 4)
print(df.columns) # Index(['股票代码', '收盘价', '成交量', '日期'], dtype='object')
print(df.dtypes)
# 股票代码 object
# 收盘价 float64
# 成交量 int64
# 日期 object
# dtype: object
注意:dtypes里显示object的列,通常是字符串。在量化回测里,日期列经常被存成object,我建议你后面用pd.to_datetime()转成datetime类型,这样时间运算才方便。
3.3 索引与切片:快速定位数据
索引和切片是高频操作。你想想看,每天面对几千只股票的数据,怎么快速找到某一只?怎么截取某段时间?
行索引与切片
DataFrame的行索引,默认是0开始的整数。你可以用类似列表的方式切片:
# 取前两行
print(df[:2])
# 取第1行到第3行(不包含第3行)
print(df[1:3])
列索引
取列更简单,用列名就行:
# 取单列,返回Series
print(df['收盘价'])
# 取多列,返回DataFrame
print(df[['股票代码', '收盘价']])
loc和iloc:精准定位
这两个是Pandas的精髓。loc用标签索引,iloc用位置索引。
# loc:基于标签
print(df.loc[0]) # 取索引为0的行
print(df.loc[0:2]) # 取索引0到2的行(包含2)
print(df.loc[0, '收盘价']) # 取第0行,收盘价列
# iloc:基于位置
print(df.iloc[0]) # 取第0行
print(df.iloc[0:2]) # 取第0到1行(不包含2)
print(df.iloc[0, 1]) # 取第0行,第1列
避坑指南:我曾经在回测里用loc切片,结果发现它包含结束位置,而iloc不包含。这个区别坑了我一下午。记住:loc是闭区间,iloc是半开区间。
3.4 数据筛选与过滤:条件为王
筛选是量化回测里最常用的操作。比如「选出所有收盘价大于20的股票」、「找出成交量异常的日期」。
布尔索引
这是最核心的筛选方式。你写一个条件,返回True/False的Series,然后用它去过滤DataFrame。
# 选出收盘价大于15的股票
condition = df['收盘价'] > 15
print(condition)
# 0 False
# 1 True
# 2 True
# Name: 收盘价, dtype: bool
# 应用条件
filtered_df = df[condition]
print(filtered_df)
# 股票代码 收盘价 成交量 日期
# 1 000002 22.3 15000 2024-01-01
# 2 000003 15.8 8000 2024-01-01
多条件筛选
多个条件用&(与)、|(或)、~(非)连接。注意每个条件要加括号。
# 收盘价大于10 且 成交量大于10000
condition = (df['收盘价'] > 10) & (df['成交量'] > 10000)
print(df[condition])
# 股票代码 收盘价 成交量 日期
# 1 000002 22.3 15000 2024-01-01
isin方法
如果你想筛选某个列的值是否在列表中,用isin很方便:
# 选出股票代码为000001或000003的行
codes = ['000001', '000003']
print(df[df['股票代码'].isin(codes)])
# 股票代码 收盘价 成交量 日期
# 0 000001 10.5 10000 2024-01-01
# 2 000003 15.8 8000 2024-01-01
query方法
我个人觉得query写起来更清爽,尤其条件复杂的时候:
# 用query筛选
print(df.query('收盘价 > 10 and 成交量 > 10000'))
# 股票代码 收盘价 成交量 日期
# 1 000002 22.3 15000 2024-01-01
实战经验:在量化回测里,我经常用布尔索引做「因子筛选」。比如选出市盈率小于20且市净率小于1.5的股票,然后计算它们的平均收益率。这种操作一天要写几十次,熟练了能省很多时间。
嗯,到这里Pandas基础的上半部分就差不多了。Series和DataFrame的创建、属性、索引切片、筛选过滤,这些是每天都要用的基本功。你想想看,后面做回测的时候,数据清洗、因子计算、信号生成,哪一步离得开这些操作?
多练练,尤其是loc和iloc的区别,还有布尔索引的写法。刚开始可能会觉得括号多、容易乱,但用顺手了就会发现——嗯,真香。