4、Pandas基础:Series与DataFrame创建、数据查看、基本统计

做量化分析,说白了就是跟表格数据打交道。Pandas 就是 Python 里处理表格的瑞士军刀。我个人习惯,拿到任何数据源,第一件事就是把它塞进 Pandas 的 DataFrame 里。为什么?因为后续的清洗、切片、统计、可视化,全都依赖它。

这一节,咱们就聊聊 Pandas 最核心的两个数据结构:SeriesDataFrame。怎么创建它们,怎么快速瞄一眼数据长啥样,以及怎么算一些基础统计量。

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')
我曾经踩过的坑: 读取 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())

这行代码会按行业分组,计算每个行业的平均收益率。我在做多因子模型时,经常用这个来检查不同行业间的因子暴露差异。

个人经验: 分组统计后,如果某个组的数据量特别少(比如只有 1 只股票),它的均值可能没有统计意义。我一般会加个 size() 看看每组有多少样本。

4.5 知识体系总览

下面这张图,把这一节的核心内容串起来了。你一看就明白:Series 和 DataFrame 是什么关系,创建方式有哪些,查看数据用什么方法,统计计算怎么玩。

Pandas 基础:知识体系 Pandas 核心数据结构 Series(一维) DataFrame(二维) 创建:列表、字典、指定索引 操作:索引、切片、运算 创建:字典、列表、CSV读取 查看:head()、info()、describe() 统计:mean()、std()、corr()、groupby() 核心流程:创建 → 查看 → 统计 → 分析 拿到数据先 head() 看长相,info() 看结构,describe() 看分布

4.6 避坑指南

最后,分享几个我实战中踩过的坑:

  • 索引对齐问题: 两个 Series 做运算时,Pandas 会按索引对齐。如果索引不完全一致,结果会出现 NaN。我曾经用两个不同时间频率的数据做运算,结果出来一堆空值,排查了半天才发现是索引没对齐。
  • inplace 参数: 很多方法有 inplace=True 参数,表示直接修改原对象。我个人建议少用,因为容易忘记自己改过什么。我习惯用 df = df.dropna() 这种赋值方式,更清晰。
  • 数据类型陷阱: 有时候从 CSV 读进来的数字列,实际上是字符串类型(object)。这时候用 mean() 会报错。记得用 df['列名'] = pd.to_numeric(df['列名'], errors='coerce') 强制转换。

好了,这一节的内容就到这里。Pandas 的基础操作,说白了就是三板斧:创建、查看、统计。把这几个方法练熟了,后面做因子挖掘就顺手多了。


公众号:蓝海资料掘金营,微信deep3321