4. Pandas基础(上):Series与DataFrame的创建、基本属性、索引与切片
各位同学,欢迎来到《基本面量化选股》的数据清洗实战课。今天咱们聊聊Pandas,这个在量化投资领域几乎人手必备的库。
说实话,我刚开始做量化那会儿,面对一堆乱七八糟的财报数据,头都是大的。后来发现Pandas就像一把瑞士军刀,切数据、洗数据、算指标,一把搞定。今天这节,咱们先把Pandas最核心的两个数据结构——Series和DataFrame——给拿捏住。
核心知识点速览
- Series:一维带标签的数组,类似Excel的一列
- DataFrame:二维表格结构,类似Excel的一个Sheet
- 基本属性:shape、dtypes、index、columns
- 索引与切片:iloc、loc、布尔索引
4.1 什么是Series?
Series,说白了就是一列带名字的数据。你想想看,在Excel里我们经常选中某一列,然后做计算。Series就是干这个的——它把数据和索引(标签)打包在一起。
我习惯把Series想象成「有标签的列表」。普通的Python列表只有位置索引(0,1,2...),而Series除了位置索引,还可以有自定义的标签索引。这在处理股票代码、日期这些非数字索引时,简直不要太方便。
创建Series
创建Series最简单的方式,就是从一个列表或字典来构造。
import pandas as pd
# 从列表创建
s1 = pd.Series([100, 200, 150, 180])
print(s1)
# 输出:
# 0 100
# 1 200
# 2 150
# 3 180
# dtype: int64
# 从字典创建(键自动变成索引)
s2 = pd.Series({'茅台': 1800, '五粮液': 150, '泸州老窖': 200})
print(s2)
# 输出:
# 茅台 1800
# 五粮液 150
# 泸州老窖 200
# dtype: int64
我的小经验:从字典创建Series时,字典的键会自动成为索引。我在处理股票基本面数据时,经常用股票代码作为键,这样后续按代码查找数据就非常快。
Series的基本属性
创建好Series后,有几个属性我几乎天天用:
s = pd.Series([100, 200, 150, 180], index=['a', 'b', 'c', 'd'])
print(s.values) # 获取数据:array([100, 200, 150, 180])
print(s.index) # 获取索引:Index(['a', 'b', 'c', 'd'], dtype='object')
print(s.dtype) # 数据类型:int64
print(s.size) # 元素个数:4
print(s.shape) # 形状:(4,)
嗯,这里要注意一下shape属性。对于Series,它返回的是一个元组,比如(4,)。为什么是元组?因为Pandas统一了接口,DataFrame的shape也是元组,比如(100, 5)表示100行5列。这样设计,你想想看,是不是很一致?
4.2 DataFrame:二维表格的王者
如果说Series是一列数据,那DataFrame就是多列数据组成的表格。它既有行索引,又有列索引。我经常跟团队里的新人说:「DataFrame就是Excel的Sheet,只不过它更智能、更强大。」
创建DataFrame
创建DataFrame的方式有很多,我挑几个最常用的:
import pandas as pd
# 方式1:从字典创建(最常用)
data = {
'股票代码': ['000001', '000002', '600519'],
'股票名称': ['平安银行', '万科A', '贵州茅台'],
'收盘价': [12.5, 15.8, 1800.0],
'市盈率': [8.5, 6.2, 45.3]
}
df1 = pd.DataFrame(data)
print(df1)
# 方式2:从列表的列表创建
data2 = [
['000001', '平安银行', 12.5, 8.5],
['000002', '万科A', 15.8, 6.2],
['600519', '贵州茅台', 1800.0, 45.3]
]
df2 = pd.DataFrame(data2, columns=['股票代码', '股票名称', '收盘价', '市盈率'])
print(df2)
重点提醒:从字典创建时,字典的键会成为列名。从列表创建时,需要手动指定columns参数。我个人更推荐字典方式,因为代码可读性更好,别人一看就知道每列是什么。
DataFrame的基本属性
拿到一个DataFrame后,我通常会先看看它的「户口本」:
print(df1.shape) # (3, 4) —— 3行4列
print(df1.columns) # Index(['股票代码', '股票名称', '收盘价', '市盈率'], dtype='object')
print(df1.index) # RangeIndex(start=0, stop=3, step=1)
print(df1.dtypes) # 每列的数据类型
print(df1.info()) # 一次性看全貌(我最爱的函数之一)
print(df1.describe()) # 数值列的统计摘要
说到info(),我曾经在一次数据清洗项目中,发现一个数据集的某列全是NaN,就是靠info()一眼看出来的。当时那个数据集有200多列,手动检查得累死,但info()一秒就暴露了问题。
4.3 索引与切片:精准定位数据
索引和切片,说白了就是「怎么从表格里把想要的数据拿出来」。Pandas提供了两套索引系统:基于位置的iloc和基于标签的loc。这两兄弟我刚开始也经常搞混,后来总结了一个口诀:「iloc看位置,loc看名字」。
iloc:位置索引
iloc用整数位置来定位,跟Python列表的索引一模一样。从0开始,左闭右开。
# 继续用上面的df1
# 取第一行
print(df1.iloc[0])
# 输出:
# 股票代码 000001
# 股票名称 平安银行
# 收盘价 12.5
# 市盈率 8.5
# Name: 0, dtype: object
# 取前两行
print(df1.iloc[:2])
# 取第一行第一列
print(df1.iloc[0, 0]) # 输出:000001
# 取所有行,第一列到第三列
print(df1.iloc[:, 0:3])
loc:标签索引
loc用行标签和列名来定位。注意,loc是闭区间,两边都包含。
# 取行索引为0的行
print(df1.loc[0])
# 取多行
print(df1.loc[[0, 2]])
# 取特定列
print(df1.loc[:, ['股票代码', '收盘价']])
# 取特定行和列
print(df1.loc[0, '收盘价']) # 输出:12.5
避坑指南:我曾经犯过一个错误——用loc去取不存在的行标签,结果直接报错。比如df1.loc[5],因为行索引最大是2,所以会抛出KeyError。所以用loc前,最好先确认标签是否存在。
布尔索引:条件筛选的利器
布尔索引,说白了就是「用条件来筛选数据」。这是量化选股中最常用的技巧之一。
# 筛选市盈率小于10的股票
condition = df1['市盈率'] < 10
print(df1[condition])
# 输出:
# 股票代码 股票名称 收盘价 市盈率
# 0 000001 平安银行 12.5 8.5
# 1 000002 万科A 15.8 6.2
# 多条件筛选(注意用 & 而不是 and)
condition2 = (df1['市盈率'] < 10) & (df1['收盘价'] > 13)
print(df1[condition2])
# 输出:
# 股票代码 股票名称 收盘价 市盈率
# 1 000002 万科A 15.8 6.2
我的习惯:写多条件筛选时,每个条件一定要用括号括起来。不然Python的运算符优先级会让你怀疑人生。我刚开始写df1['市盈率'] < 10 & df1['收盘价'] > 13,结果报错,查了半天才发现是少了括号。
4.4 实战小案例:快速浏览股票数据
好了,理论讲完了,咱们来点实际的。假设你刚拿到一份股票基本面数据,想快速了解它的结构:
import pandas as pd
# 模拟一份股票数据
stock_data = {
'代码': ['600519', '000858', '002304', '000568', '600809'],
'名称': ['贵州茅台', '五粮液', '洋河股份', '泸州老窖', '山西汾酒'],
'市盈率': [45.3, 28.6, 22.1, 35.8, 55.2],
'市净率': [12.1, 6.8, 5.2, 8.3, 15.6],
'ROE': [0.25, 0.18, 0.15, 0.21, 0.28]
}
df = pd.DataFrame(stock_data)
# 1. 看整体信息
print("=== 数据概览 ===")
print(df.info())
# 2. 看统计摘要
print("\n=== 统计摘要 ===")
print(df.describe())
# 3. 筛选低市盈率股票
print("\n=== 低市盈率股票(<30) ===")
low_pe = df[df['市盈率'] < 30]
print(low_pe)
# 4. 用loc取特定股票的信息
print("\n=== 贵州茅台的信息 ===")
print(df.loc[df['名称'] == '贵州茅台', ['代码', '市盈率', 'ROE']])
你看,短短几行代码,就把数据摸了个透。这就是Pandas的魅力——用最少的代码,做最多的事。
好了,今天的内容就到这儿。Series和DataFrame是Pandas的基石,后面的数据清洗、合并、分组都建立在它们之上。建议你打开Jupyter Notebook,把上面的代码敲一遍,感受一下。遇到问题别怕,多试几次就熟了。
公众号:蓝海资料掘金营,微信deep3321