4. Pandas基础(上):Series与DataFrame的创建、基本属性、索引与切片

各位同学,欢迎来到《基本面量化选股》的数据清洗实战课。今天咱们聊聊Pandas,这个在量化投资领域几乎人手必备的库。

说实话,我刚开始做量化那会儿,面对一堆乱七八糟的财报数据,头都是大的。后来发现Pandas就像一把瑞士军刀,切数据、洗数据、算指标,一把搞定。今天这节,咱们先把Pandas最核心的两个数据结构——Series和DataFrame——给拿捏住。

核心知识点速览

  • Series:一维带标签的数组,类似Excel的一列
  • DataFrame:二维表格结构,类似Excel的一个Sheet
  • 基本属性:shape、dtypes、index、columns
  • 索引与切片:iloc、loc、布尔索引
Pandas基础 Series 创建:pd.Series() 属性:values, index DataFrame 创建:pd.DataFrame() 属性:shape, dtypes 索引与切片 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