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 索引与切片实战流程
下面这张流程图展示了坏账率哨兵从原始数据到筛选异常区间的典型操作链路:
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 的所有记录。