3、Pandas基础(上):Series与DataFrame的创建、基本属性、索引与切片、数据筛选与过滤

做量化回测,说白了就是跟数据打交道。你每天面对的是K线、成交量、各种因子……这些数据怎么存、怎么取、怎么切?Pandas就是干这个的。

我个人习惯把Pandas比作「Excel的Pro版」。它比Excel快,比Excel灵活,而且能跟Python生态无缝衔接。今天咱们先啃下最核心的两个数据结构:Series和DataFrame。

核心知识点速览

  • Series:一维带标签数组,类似带索引的列表
  • DataFrame:二维表格,类似Excel工作表
  • 索引与切片:怎么快速定位数据
  • 数据筛选:条件过滤,选出你想要的
Pandas基础(上) Series 创建:pd.Series() 属性:values, index 索引:loc, iloc DataFrame 创建:pd.DataFrame() 属性:shape, dtypes 筛选:布尔索引 切片 & 筛选 行切片:df[0:5] 条件筛选:df[df.a > 0]

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的区别,还有布尔索引的写法。刚开始可能会觉得括号多、容易乱,但用顺手了就会发现——嗯,真香。


专注资料整理