4、Pandas基础:Series与DataFrame创建、数据查看、基本统计
做量化分析,说白了就是跟表格数据打交道。Pandas 就是 Python 里处理表格的瑞士军刀。我个人习惯,拿到任何数据源,第一件事就是把它塞进 Pandas 的 DataFrame 里。为什么?因为后续的清洗、切片、统计、可视化,全都依赖它。
这一节,咱们就聊聊 Pandas 最核心的两个数据结构:Series 和 DataFrame。怎么创建它们,怎么快速瞄一眼数据长啥样,以及怎么算一些基础统计量。
4.1 Series:一维数据容器
Series 可以理解成带标签的一维数组。它比 Python 列表强在哪?强在索引可以不是整数,可以是字符串、时间戳,甚至自定义标签。
4.1.1 创建 Series
最直接的方式,从列表创建:
import pandas as pd
# 从列表创建 Series
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
我在项目中遇到过一种情况:从数据库查出来的数据,日期字段经常是字符串格式。这时候我会直接用日期字符串做索引,方便后续按时间切片。
s = pd.Series([100, 102, 101, 105],
index=['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04'])
print(s)
pd.to_datetime() 转换一下。这样就能用 s['2024-01'] 这种语法按月切片了。
4.1.2 从字典创建 Series
字典的键自动变成索引,值变成数据:
data = {'苹果': 10, '香蕉': 20, '橘子': 15}
s = pd.Series(data)
print(s)
输出:
苹果 10
香蕉 20
橘子 15
dtype: int64
嗯,这里要注意:字典是无序的,但 Series 会按字典的插入顺序排列。如果你需要特定顺序,可以手动指定 index 参数。
4.2 DataFrame:二维表格
DataFrame 才是我们真正天天打交道的对象。它就像一张 Excel 表格,有行有列,每列可以有不同的数据类型。
4.2.1 创建 DataFrame
方式一:从字典创建
这是最常用的方式。字典的键是列名,值是列数据:
data = {
'股票代码': ['000001', '000002', '000003'],
'收盘价': [10.5, 22.3, 15.8],
'成交量': [1000000, 1500000, 800000]
}
df = pd.DataFrame(data)
print(df)
输出:
股票代码 收盘价 成交量
0 000001 10.5 1000000
1 000002 22.3 1500000
2 000003 15.8 800000
方式二:从列表的列表创建
适合从 API 或 CSV 读取的原始数据:
data = [
['000001', 10.5, 1000000],
['000002', 22.3, 1500000],
['000003', 15.8, 800000]
]
df = pd.DataFrame(data, columns=['股票代码', '收盘价', '成交量'])
print(df)
方式三:从 CSV 文件读取
实战中最常见:
df = pd.read_csv('stock_data.csv')
encoding='utf-8' 或 encoding='gbk'。否则一堆乱码,排查半天才发现是编码问题。
4.3 数据查看:先瞄一眼
拿到 DataFrame 后,别急着分析。先看看数据长什么样。我一般会做这几步:
4.3.1 查看前几行
# 查看前5行
print(df.head())
# 查看前10行
print(df.head(10))
# 查看后5行
print(df.tail())
你想想看,如果数据有 100 万行,直接打印整个 DataFrame,终端直接卡死。所以 head() 和 tail() 是救命用的。
4.3.2 查看基本信息
# 查看行列数、列名、数据类型、内存占用
print(df.info())
输出示例:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 股票代码 3 non-null object
1 收盘价 3 non-null float64
2 成交量 3 non-null int64
dtypes: float64(1), int64(1), object(1)
memory usage: 200.0+ bytes
这里有个关键信息:Non-Null Count。如果某个列的非空数量小于总行数,说明有缺失值。我在项目中遇到过,某只股票停牌几天,那几天的收盘价就是 NaN。如果不处理,后面算收益率会直接报错。
4.3.3 查看描述性统计
print(df.describe())
输出:
收盘价 成交量
count 3.000000 3.000000e+00
mean 16.200000 1.100000e+06
std 5.938685 3.605551e+05
min 10.500000 8.000000e+05
25% 13.150000 9.000000e+05
50% 15.800000 1.000000e+06
75% 19.050000 1.250000e+06
max 22.300000 1.500000e+06
它会自动对数值型列计算:计数、均值、标准差、最小值、四分位数、最大值。一眼就能看出数据分布是否异常。
head() 看长相,再 info() 看结构,最后 describe() 看分布。这三板斧下来,数据的基本情况就心里有数了。
4.4 基本统计:算一算
Pandas 提供了丰富的统计方法。我挑几个最常用的说说。
4.4.1 单列统计
# 均值
print(df['收盘价'].mean())
# 中位数
print(df['收盘价'].median())
# 标准差
print(df['收盘价'].std())
# 最大值、最小值
print(df['收盘价'].max(), df['收盘价'].min())
# 求和
print(df['成交量'].sum())
4.4.2 多列统计
# 对所有数值列计算均值
print(df.mean())
# 计算相关系数矩阵
print(df.corr())
# 计算协方差矩阵
print(df.cov())
相关系数矩阵在因子分析里特别有用。比如你想知道「市盈率」和「未来收益率」有没有线性关系,直接看相关系数。绝对值越接近 1,关系越强。
4.4.3 分组统计
量化分析里经常要按行业、按月份分组统计:
# 假设 df 有 '行业' 和 '收益率' 两列
print(df.groupby('行业')['收益率'].mean())
这行代码会按行业分组,计算每个行业的平均收益率。我在做多因子模型时,经常用这个来检查不同行业间的因子暴露差异。
size() 看看每组有多少样本。
4.5 知识体系总览
下面这张图,把这一节的核心内容串起来了。你一看就明白:Series 和 DataFrame 是什么关系,创建方式有哪些,查看数据用什么方法,统计计算怎么玩。
4.6 避坑指南
最后,分享几个我实战中踩过的坑:
- 索引对齐问题: 两个 Series 做运算时,Pandas 会按索引对齐。如果索引不完全一致,结果会出现 NaN。我曾经用两个不同时间频率的数据做运算,结果出来一堆空值,排查了半天才发现是索引没对齐。
- inplace 参数: 很多方法有
inplace=True参数,表示直接修改原对象。我个人建议少用,因为容易忘记自己改过什么。我习惯用df = df.dropna()这种赋值方式,更清晰。 - 数据类型陷阱: 有时候从 CSV 读进来的数字列,实际上是字符串类型(object)。这时候用
mean()会报错。记得用df['列名'] = pd.to_numeric(df['列名'], errors='coerce')强制转换。
好了,这一节的内容就到这里。Pandas 的基础操作,说白了就是三板斧:创建、查看、统计。把这几个方法练熟了,后面做因子挖掘就顺手多了。
公众号:蓝海资料掘金营,微信deep3321