3. Pandas 基础:Series 与 DataFrame 创建、数据读取、基本索引与切片

在量化回测中,坏账率哨兵需要处理大量时间序列与截面数据。Pandas 是 Python 生态中最核心的数据分析库,其两大核心数据结构——Series(一维序列)与 DataFrame(二维表格)——是回测脚本的基石。本节将带你从零掌握它们的创建、文件读写与索引切片操作。

学习目标: 能够独立创建 Series 与 DataFrame;熟练使用 read_csv / read_excel 读取外部数据;掌握 loc、iloc、布尔索引与切片技巧。

3.1 Series:一维带标签数组

Series 类似 Python 列表或 NumPy 数组,但每个元素都有一个显式的索引(index)。在坏账率监控中,常用 Series 存储单日逾期率、单笔贷款金额等一维序列。

3.1.1 创建 Series

import pandas as pd

# 从列表创建,默认整数索引
s1 = pd.Series([0.05, 0.08, 0.12, 0.07])
print(s1)

# 从字典创建,键自动成为索引
s2 = pd.Series({'2024-01-01': 0.05, '2024-01-02': 0.08, '2024-01-03': 0.12})
print(s2)

# 指定索引与名称
s3 = pd.Series([0.05, 0.08, 0.12], 
               index=['2024-01-01', '2024-01-02', '2024-01-03'],
               name='坏账率')
print(s3)

3.1.2 Series 基本属性

属性说明示例
.values返回 NumPy 数组s3.values → array([0.05, 0.08, 0.12])
.index返回索引对象s3.index → Index(['2024-01-01', ...])
.name序列名称s3.name → '坏账率'
.dtype数据类型s3.dtype → float64
.shape形状元组s3.shape → (3,)

3.2 DataFrame:二维表格结构

DataFrame 是回测中最常用的数据结构,每一列可以看作一个 Series。坏账率哨兵通常将日期作为行索引,逾期率、回收率、坏账金额等指标作为列。

3.2.1 创建 DataFrame

# 从字典创建(键为列名,值为列表)
df1 = pd.DataFrame({
    '日期': ['2024-01-01', '2024-01-02', '2024-01-03'],
    '逾期率': [0.05, 0.08, 0.12],
    '回收率': [0.60, 0.55, 0.48]
})
print(df1)

# 从二维列表创建,指定列名
df2 = pd.DataFrame(
    [[0.05, 0.60], [0.08, 0.55], [0.12, 0.48]],
    columns=['逾期率', '回收率'],
    index=['2024-01-01', '2024-01-02', '2024-01-03']
)
print(df2)

# 从 CSV 文件创建(最常用,见 3.3 节)

3.2.2 DataFrame 核心属性

属性说明示例
.columns列标签df1.columns → Index(['日期', '逾期率', '回收率'])
.index行标签df1.index → RangeIndex(start=0, stop=3, step=1)
.values二维 NumPy 数组df1.values → array([...])
.dtypes每列数据类型df1.dtypes → 逾期率 float64, 回收率 float64
.shape行数×列数df1.shape → (3, 3)
.info()内存使用与类型概览df1.info()
.describe()数值列统计摘要df1.describe()
实战提示: 在回测脚本中,建议始终将日期列设为行索引(df.set_index('日期', inplace=True)),这样后续时间切片与重采样会非常高效。

3.3 数据读取:CSV 与 Excel

量化回测的原始数据通常来自外部文件。Pandas 提供了极简的 IO 接口,一行代码即可加载数据。

3.3.1 读取 CSV 文件

# 标准读取
df = pd.read_csv('坏账率_历史数据.csv')

# 指定分隔符(如制表符)
df = pd.read_csv('data.tsv', sep='\t')

# 指定编码(处理中文)
df = pd.read_csv('坏账率_历史数据.csv', encoding='utf-8')

# 将第一列作为行索引
df = pd.read_csv('坏账率_历史数据.csv', index_col=0)

# 只读取前 1000 行
df = pd.read_csv('坏账率_历史数据.csv', nrows=1000)

# 指定日期列并解析为 datetime 类型
df = pd.read_csv('坏账率_历史数据.csv', parse_dates=['日期'])

3.3.2 读取 Excel 文件

# 读取第一个工作表
df = pd.read_excel('坏账率_历史数据.xlsx')

# 指定工作表名称
df = pd.read_excel('坏账率_历史数据.xlsx', sheet_name='Sheet1')

# 指定工作表索引(0 为第一个)
df = pd.read_excel('坏账率_历史数据.xlsx', sheet_name=0)

# 跳过前 2 行
df = pd.read_excel('坏账率_历史数据.xlsx', skiprows=2)
常见坑点: 读取 CSV 时若遇到中文乱码,尝试 encoding='gbk' 或 encoding='utf-8-sig';Excel 文件需安装 openpyxl 或 xlrd 引擎。

3.4 基本索引与切片

Pandas 提供两种索引方式:基于标签的索引(loc) 和 基于整数位置的索引(iloc)。掌握它们是高效数据筛选的关键。

3.4.1 列选择

# 选择单列 → 返回 Series
df['逾期率']

# 选择多列 → 返回 DataFrame
df[['逾期率', '回收率']]

# 使用属性语法(仅限无空格列名)
df.逾期率

3.4.2 行选择与切片

# 使用 iloc 按整数位置切片(左闭右开)
df.iloc[0:3]       # 前 3 行
df.iloc[[0, 2, 4]] # 第 0、2、4 行

# 使用 loc 按标签切片(闭区间)
df.loc['2024-01-01':'2024-01-10']  # 包含两端

# 布尔索引:筛选逾期率大于 0.10 的行
df[df['逾期率'] > 0.10]

3.4.3 行列同时索引

# loc:标签行列
df.loc['2024-01-01':'2024-01-05', ['逾期率', '回收率']]

# iloc:整数行列
df.iloc[0:5, 0:2]

# 混合:取前 10 行的 '逾期率' 列
df.loc[df.index[:10], '逾期率']

3.5 索引与切片实战流程

下面这张流程图展示了坏账率哨兵从原始数据到筛选异常区间的典型操作链路:

坏账率哨兵:数据索引与切片流程 原始 CSV/Excel pd.read_csv() set_index('日期') 列筛选 [['逾期率']] loc['2024-01':'2024-06'] df[df['逾期率']>0.10] 输出:坏账率异常区间 DataFrame

3.6 索引与切片对比速查表

操作语法返回类型说明
选择单列df['逾期率']Series列名必须存在
选择多列df[['逾期率','回收率']]DataFrame双括号
按标签选行df.loc['2024-01-01']Series包含结束标签
按位置选行df.iloc[0]Series左闭右开
标签切片df.loc['2024-01-01':'2024-01-10']DataFrame闭区间
位置切片df.iloc[0:10]DataFrame左闭右开
布尔索引df[df['逾期率'] > 0.10]DataFrame条件筛选
行列同时索引df.loc[行标签, 列标签]DataFrame/Series逗号分隔
核心原则: 行索引用 loc(标签)或 iloc(位置);列索引用方括号 [];条件筛选用布尔索引。三者组合可以应对回测中 95% 以上的数据提取需求。

3.7 实战案例:加载坏账率数据并提取异常区间

import pandas as pd

# 1. 读取 CSV 数据
df = pd.read_csv('坏账率_历史数据.csv', parse_dates=['日期'])
df.set_index('日期', inplace=True)

# 2. 查看数据概览
print(df.head())
print(df.info())

# 3. 提取 2024 年第一季度的数据
q1_data = df.loc['2024-01-01':'2024-03-31']

# 4. 筛选逾期率超过 0.10 的异常日期
anomaly = q1_data[q1_data['逾期率'] > 0.10]

# 5. 输出异常区间统计
print(f"异常天数:{len(anomaly)}")
print(anomaly[['逾期率', '回收率']])
课后练习: 下载一份真实的 P2P 坏账率样本数据(CSV 格式),尝试用 read_csv 加载,将日期列设为索引,然后使用 loc 提取最近 30 天的数据,并用布尔索引找出逾期率高于 0.15 的所有记录。