10 Pandas Series和DataFrame创建
10.1 引言Pandas在金融数据分析中的核心地位
Pandas是Python数据分析的基石库,建立在NumPy之上,提供了专为数据处理设计的高级数据结构和分析工具。在金融领域,Pandas几乎是事实上的标准,从数据清洗到复杂的时间序列分析,从简单的统计计算到复杂的金融建模,Pandas都能胜任。
10.2 本章学习目标
通过本章学习,你将能够:
- 说出 Series 与 DataFrame 的结构组成(值、索引、列标签),并解释 Pandas 相比 Excel 在可重复性与处理规模上的优势
- 使用
pd.Series(data, index=...)与pd.DataFrame(data, index=...)从列表、字典等创建对象,并为金融数据设置有含义的行索引(如交易日) - 使用
[]、.loc、.iloc访问列、行与单个值,并用布尔索引与query完成条件筛选 - 使用
mean/std/describe等统计函数按行或按列(axis)计算指标,并解释索引对齐运算中 NaN 的来源 - 识别并初步处理缺失值(
isnull/dropna/fillna),为第 章节 16 章的数据清洗打基础
10.3 Pandas简介与安装
理论背景:Pandas的设计哲学
Pandas的设计深受R语言data.frame的影响,并结合了NumPy的高性能计算能力和Python的灵活性:
- DataFrame: 二维表格数据结构(类似Excel表格)
- Series: 一维带标签数组(类似DataFrame的单列)
- Index: 行标签,支持时间序列索引
- 向量化操作: 类似NumPy的高性能运算
为什么Pandas优于Excel?
- 处理大数据: Excel限制约100万行,Pandas几乎无限制
- 可重复性: 代码可以重现,Excel操作难以追踪
- 自动化: 可以构建自动化分析流程
- 复杂计算: 支持复杂的分组、聚合、变换操作
- 时间序列: 专门优化的时间序列处理能力
带着这一设计脉络,我们先完成Pandas的导入与安装验证:
# =============================================================================
# 题目:导入Pandas库并验证安装
# =============================================================================
# 本代码块演示Pandas库的标准导入方式,并验证其是否正常工作。
# 在金融数据分析中,Pandas是核心工具库,用于处理结构化数据。
# =============================================================================
# ==================== 导入Pandas库 ====================
import pandas as pd # 导入Pandas库,使用pd作为别名(行业惯例)
import numpy as np # 导入NumPy库,Pandas依赖NumPy进行数值计算
# ==================== 查看Pandas版本 ====================
print(f"Pandas版本: {pd.__version__}") # 打印Pandas版本号,用于确认环境
# ==================== 测试Pandas是否正常工作 ====================
test_data = pd.Series([1, 2, 3, 4, 5]) # 创建测试Series对象
print(f"\n测试数据:\n{test_data}") # 打印测试数据,验证Pandas功能正常
# 输出解释:显示包含整数1-5的Series,左侧为索引(0-4),右侧为值安装命令:
# 使用conda
conda install pandas
# 使用pip
pip install pandas10.4 Series一维带标签数组
10.4.1 创建Series
Series是Pandas的一维数据结构,类似NumPy数组但带标签索引。
语法: pd.Series(data, index=index, dtype=dtype, name=name)
任务要求:用列表 list_may(四只股票某日的涨跌幅)与列表 name(对应的股票名称)创建带标签索引的 Series s,并打印输出。请将代码原样输入教学平台(注释除外),判定以平台为准。
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一:创建股票涨跌幅Series
import pandas as pd
list_may=[-0.0035,-0.0338,0.0058,-0.0298] # 定义列表list_may
name=['中国卫星','中国软件','中国银行','上汽集团'] # 定义列表name
s=pd.Series(list_may,index=name) # 创建Series序列s
print(s) # 输出任务一:创建股票涨跌幅Series预期输出(本机 Python 实际运行结果,具体以平台运行结果为准):
中国卫星 -0.0035
中国软件 -0.0338
中国银行 0.0058
上汽集团 -0.0298
dtype: float64
代码深度解析:
Series的组成:
- Values: 底层的数据(NumPy数组)
- Index: 行标签(可以是字符串、数字、日期等)
- Name: Series的名称(可选)
查看Series属性:
print(f"值:\n{s.values}") print(f"索引:\n{s.index}") print(f"名称: {s.name}") print(f"数据类型: {s.dtype}")索引的意义:
- 快速查找: 类似字典的键值查找
- 数据对齐: 自动按索引对齐运算
- 时间序列: 支持日期时间索引
10.4.2 Series的基本操作
# =============================================================================
# 题目:Series的基本操作、数据访问和统计分析
# =============================================================================
# 本代码块演示Series的常用操作,包括索引访问、条件筛选、统计摘要和排序。
# 金融应用场景:分析股票涨跌幅,筛选负收益股票,计算统计指标。
# =============================================================================
# ==================== 创建示例Series ====================
s = pd.Series( # 创建Series对象
[-0.0035, -0.0338, 0.0058, -0.0298], # 数据:四只股票涨跌幅
index=['中国卫星', '中国软件', '中国银行', '上汽集团'], # 索引:股票名称
name='涨跌幅' # Series名称
)
print("完整Series:") # 打印标题
print(s) # 显示完整的Series对象
# ==================== 1. 通过索引访问单个元素 ====================
print(f"\n中国软件涨跌幅: {s['中国软件']:.4f}") # 使用索引标签访问单个元素
# 输出解释:.4f格式化为保留4位小数,显示-0.0338
# ==================== 2. 通过位置访问(类似NumPy) ====================
print(f"第一个元素: {s[0]:.4f}") # 使用整数位置访问第一个元素
print(f"前两个元素:\n{s[:2]}") # 使用切片访问前两个元素(位置0和1)
# 输出解释:显示中国卫星和中国软件的涨跌幅
# ==================== 3. 布尔索引(条件筛选) ====================
print(f"\n负收益股票:\n{s[s < 0]}") # 使用布尔条件筛选负收益股票
# 输出解释:显示涨跌幅小于0的三只股票(中国卫星、中国软件、上汽集团)
# ==================== 4. 统计摘要 ====================
print(f"\n统计摘要:")
print(s.describe()) # 显示描述性统计信息
# 输出解释:包含count(数量)、mean(均值)、std(标准差)、min(最小值)、
# 25%(下四分位数)、50%(中位数)、75%(上四分位数)、max(最大值)
# ==================== 5. 向量化运算 ====================
s_doubled = s * 2 # Series乘以标量,所有元素都乘以2(向量化操作)
print(f"\n涨跌幅翻倍:")
print(s_doubled)
# 输出解释:每个涨跌幅值都翻倍,如-0.0035变为-0.0070
# ==================== 6. 排序 ====================
s_sorted = s.sort_values() # 按值升序排序(默认)
print(f"\n按值排序(升序):")
print(s_sorted)
# 输出解释:从最负到最正排序,中国软件(-0.0338)排在最前
s_sorted_desc = s.sort_values(ascending=False) # 按值降序排序
print(f"\n按值排序(降序):")
print(s_sorted_desc)
# 输出解释:从最正到最负排序,中国银行(0.0058)排在最前补充说明:索引 vs 位置
| 访问方式 | 语法 | 说明 | 示例 |
|---|---|---|---|
| 标签索引 | s['label'] |
使用索引名 | s['中国卫星'] |
| 位置索引 | s[0] |
使用整数位置 | s[0] |
| 切片(标签) | s['a':'c'] |
包含两端 | s['A':'C'] |
| 切片(位置) | s[0:2] |
不包含末端 | s[0:2] |
易混淆点: 标签切片包含末端,位置切片不包含!
10.5 DataFrame二维表格数据
10.5.1 创建DataFrame
DataFrame是Pandas的二维数据结构,类似Excel表格或SQL表。
语法: pd.DataFrame(data, index=index, columns=columns)
任务要求:用字典 data(四只股票各 5 个交易日的涨跌幅)与列表 index(五个交易日)创建 DataFrame df,并打印输出。请将代码原样输入教学平台(注释除外),判定以平台为准。
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务二:创建股票涨跌幅DataFrame
import pandas as pd
data={ # 定义data字典,开始构建键值映射
'中国卫星':[-0.0351,0.0172,-0.0035,-0.0246,0.0394], # "中国卫星"的数据序列
'中国软件':[-0.0139,0.0243,-0.0338,0.0146,0.0001], # "中国软件"的数据序列
'中国银行':[-0.0139,0.0243,-0.0338,0.0146,0.0001], # "中国银行"的数据序列
'上汽集团':[0.0212,0.0021,-0.0298,-0.0027,-0.0143] # "上汽集团"的数据序列
} # 数据结构定义结束
index=['20200525','20200526','20200527','20200528','20200529'] # 定义列表index
df=pd.DataFrame(data,index) # 创建数据框df
print(df) # 输出数据框数据预期输出(本机 Python 实际运行结果,具体以平台运行结果为准):
中国卫星 中国软件 中国银行 上汽集团
20200525 -0.0351 -0.0139 -0.0139 0.0212
20200526 0.0172 0.0243 0.0243 0.0021
20200527 -0.0035 -0.0338 -0.0338 -0.0298
20200528 -0.0246 0.0146 0.0146 -0.0027
20200529 0.0394 0.0001 0.0001 -0.0143
数据观察:两列完全相同? 仔细比对上表会发现,中国软件 与 中国银行 两列的数值逐行完全一样。两只不同行业的股票连续五天涨跌幅恰好全部相同的概率几乎为零,这更像是原始数据录入时的复制粘贴痕迹(数据质量缺陷),而非真实行情。课堂不妨将其作为一个”找数据异常”的小练习:让学生用 df['中国软件'].equals(df['中国银行']) 自行验证——真实数据中这类”看起来太整齐”的列,往往是后续数据清洗环节(参见第16章缺失值与异常处理)最先要排查的对象。
代码深度解析:
DataFrame的结构:
DataFrame = { Index: 行标签(日期、ID等) Columns: 列名(变量名) Values: 二维NumPy数组 }从字典创建: 键自动成为列名
设置行索引:
- 字符串: 日期、股票代码等
- DatetimeIndex: 时间序列(推荐)
- RangeIndex: 默认整数索引
10.5.2 其他创建DataFrame的方法
# =============================================================================
# 题目:演示创建DataFrame的其他常用方法
# =============================================================================
# 本代码块演示从列表、元组、字典列表和NumPy数组创建DataFrame。
# 金融应用场景:根据数据来源不同,灵活选择创建方法。
# =============================================================================
# ==================== 方法1:从列表的列表创建 ====================
data_list = [ # 列表的列表,每个子列表代表一行数据
['中信证券', 24.78, 0.05], # 第1行:名称、价格、涨跌幅
['国泰君安', 20.15, -0.02], # 第2行:名称、价格、涨跌幅
['海通证券', 14.03, 0.03] # 第3行:名称、价格、涨跌幅
]
df1 = pd.DataFrame(data_list, columns=['名称', '价格', '涨跌幅']) # 指定列名
print("方法1 - 从列表的列表:")
print(df1)
# 输出解释:每行代表一只证券,列分别为名称、价格、涨跌幅
# ==================== 方法2:从元组列表创建 ====================
data_tuples = [ # 元组列表,每个元组代表一行数据
('600519.SH', '贵州茅台', 1850.00), # 第1行:代码、名称、价格
('000858.SZ', '五粮液', 220.50), # 第2行:代码、名称、价格
('600036.SH', '招商银行', 45.20) # 第3行:代码、名称、价格
]
df2 = pd.DataFrame(data_tuples, columns=['代码', '名称', '价格']) # 指定列名
print("\n方法2 - 从元组列表:")
print(df2)
# 输出解释:每行代表一只股票,包含代码、名称和价格信息
# ==================== 方法3:从字典列表创建 ====================
data_dict_list = [ # 字典列表,每个字典代表一行数据
{'code': '600519.SH', 'name': '贵州茅台', 'price': 1850.00}, # 第1行数据
{'code': '000858.SZ', 'name': '五粮液', 'price': 220.50}, # 第2行数据
{'code': '600036.SH', 'name': '招商银行', 'price': 45.20} # 第3行数据
]
df3 = pd.DataFrame(data_dict_list) # 字典的键自动成为列名
print("\n方法3 - 从字典列表:")
print(df3)
# 输出解释:字典的键(code, name, price)自动成为列名
# ==================== 方法4:从NumPy数组创建 ====================
np.random.seed(42) # 设置随机种子,确保随机数表可重现
arr = np.random.randn(5, 3) # 生成5行3列的标准正态分布随机数
df4 = pd.DataFrame( # 从NumPy数组创建DataFrame
arr, # 数据源
index=['row1', 'row2', 'row3', 'row4', 'row5'], # 自定义行索引
columns=['A', 'B', 'C'] # 自定义列名
)
print("\n方法4 - 从NumPy数组:")
print(df4)
# 输出解释:5行3列的随机数表,行为row1-row5,列为A-C10.6 DataFrame的基本操作
10.6.1 访问数据
# =============================================================================
# 题目:演示访问DataFrame中数据的多种方法
# =============================================================================
# 本代码块演示如何访问DataFrame的列、行和单个值。
# 金融应用场景:提取特定股票数据、查询特定日期的数据、获取单个价格值。
# =============================================================================
# ==================== 重新创建示例DataFrame ====================
data = { # 准备字典数据
'中国卫星': [-0.0351, 0.0172, -0.0035, -0.0246, 0.0394],
'中国软件': [-0.0139, 0.0243, -0.0338, 0.0146, 0.0001],
'中国银行': [-0.0139, 0.0243, -0.0338, 0.0146, 0.0001],
'上汽集团': [0.0212, 0.0021, -0.0298, -0.0027, -0.0143]
}
index = ['20200525', '20200526', '20200527', '20200528', '20200529'] # 日期索引
df = pd.DataFrame(data, index=index) # 创建DataFrame
# ==================== 1. 访问列(推荐方式) ====================
print("方法1 - 使用点号(列名无空格):")
print(df.中国卫星.head(3)) # 使用点号访问列,head(3)显示前3行
# 输出解释:显示中国卫星列的前3个值(-0.0351, 0.0172, -0.0035)
print("\n方法2 - 使用方括号(通用):")
print(df['中国软件'].head(3)) # 使用方括号访问列(通用方法)
# 输出解释:显示中国软件列的前3个值
print("\n方法3 - 同时访问多列:")
print(df[['中国卫星', '中国软件']].head(3)) # 使用列表访问多列
# 输出解释:显示前3行,包含中国卫星和中国软件两列
# ==================== 2. 访问行 ====================
print("\n使用loc访问行(按标签):")
print(df.loc['20200526']) # 使用loc按标签访问单行
# 输出解释:显示20200526这一行的所有列数据
print(df.loc[['20200526', '20200527']]) # 使用loc按标签访问多行
# 输出解释:显示20200526和20200527两行的所有列数据
print("\n使用iloc访问行(按位置):")
print(df.iloc[0]) # 使用iloc按位置访问第1行(位置0)
# 输出解释:显示第1行(20200525)的所有列数据
print(df.iloc[0:2]) # 使用iloc按位置访问前2行
# 输出解释:显示前2行(位置0和1)
# ==================== 3. 访问单个值 ====================
print("\n访问单个值:")
print(f"df.loc['20200526', '中国卫星'] = {df.loc['20200526', '中国卫星']:.4f}") # 使用loc访问
# 输出解释:显示20200526这一天中国卫星的涨跌幅(0.0172)
print(f"df.iloc[0, 0] = {df.iloc[0, 0]:.4f}") # 使用iloc访问
# 输出解释:显示第1行第1列的值(-0.0351)10.6.2 DataFrame的基本信息
# =============================================================================
# 题目:查看DataFrame的结构信息和统计摘要
# =============================================================================
# 本代码块演示如何快速了解DataFrame的基本情况。
# 金融应用场景:数据探索性分析(EDA),检查数据质量,了解数据分布。
# =============================================================================
print("DataFrame形状(行, 列):")
print(df.shape) # 返回元组(行数, 列数)
# 输出解释:(5, 4)表示5行4列
print("\n前3行数据:")
print(df.head(3)) # 显示前3行数据
# 输出解释:显示20200525-20200527共3行的所有列数据
print("\n后3行数据:")
print(df.tail(3)) # 显示后3行数据
# 输出解释:显示20200527-20200529共3行的所有列数据
print("\n数据类型:")
print(df.dtypes) # 显示每列的数据类型
# 输出解释:所有列都是float64类型(浮点数)
print("\n基本统计信息:")
print(df.describe()) # 显示描述性统计(均值、标准差、最小值、分位数、最大值)
# 输出解释:对每列计算count、mean、std、min、25%、50%、75%、max
print("\nDataFrame信息:")
print(df.info()) # 显示DataFrame的详细信息(包括内存使用)
# 输出解释:显示列名、非空值数量、数据类型、内存占用等
print("\n转置DataFrame:")
print(df.T) # 转置DataFrame(行列互换)
# 输出解释:原来的行变成列,原来的列变成行10.6.3 数据筛选与查询
# =============================================================================
# 题目:使用条件表达式筛选DataFrame数据
# =============================================================================
# 本代码块演示如何根据条件筛选DataFrame的行。
# 金融应用场景:找出涨幅为正的交易日,找出满足多个条件的记录。
# =============================================================================
# ==================== 1. 条件筛选 ====================
print("中国卫星涨幅超过0的交易日:")
positive = df[df.中国卫星 > 0] # 使用布尔条件筛选行
print(positive)
# 输出解释:显示中国卫星涨跌幅大于0的两个交易日(20200526, 20200529)
# ==================== 2. 多条件筛选 ====================
print("\n中国卫星>0 且 中国软件>0的交易日:")
multi_condition = df[(df.中国卫星 > 0) & (df.中国软件 > 0)] # 使用&连接条件(且)
print(multi_condition)
# 输出解释:显示两只股票同时上涨的交易日
# ==================== 3. 使用query方法(更直观) ====================
print("\n使用query筛选:")
query_result = df.query('中国卫星 > 0 and 中国软件 > 0') # 使用query方法
print(query_result)
# 输出解释:结果与上一步相同,但语法更直观
# ==================== 4. 选择特定列 ====================
print("\n选择特定列:")
selected = df[['中国卫星', '上汽集团']] # 选择两列
print(selected.head()) # 显示前几行
# 输出解释:只显示中国卫星和上汽集团两列的数据10.7 Series与DataFrame的运算
10.7.1 算术运算
Pandas支持向量化的算术运算,并自动对齐索引。
# =============================================================================
# 题目:演示Pandas的向量化算术运算和索引对齐机制
# =============================================================================
# 本代码块演示Series和DataFrame的算术运算,以及Pandas强大的索引对齐功能。
# 金融应用场景:计算股票收益率差,计算投资组合收益,计算价格变化。
# =============================================================================
# ==================== 创建两个Series ====================
s1 = pd.Series([10, 20, 30], index=['a', 'b', 'c']) # 第1个Series
s2 = pd.Series([5, 15, 25], index=['a', 'b', 'd']) # 第2个Series(注意索引不同)
print("Series 1:")
print(s1)
print("\nSeries 2:")
print(s2)
# 输出解释:s1索引为a,b,c;s2索引为a,b,d
# ==================== 基本运算(自动对齐索引) ====================
print("\n加法(自动对齐):")
print(s1 + s2)
# 输出解释:a:15(a对a), b:35(b对b), c:NaN(c无对应), d:NaN(d无对应)
# 索引对齐机制:只有相同索引的值才会相加,不同的产生NaN
print("\n减法:")
print(s1 - s2)
# 输出解释:a:5, b:5, c:NaN, d:NaN
print("\n乘法:")
print(s1 * s2)
# 输出解释:a:50, b:300, c:NaN, d:NaN
print("\n除法:")
print(s1 / s2)
# 输出解释:a:2.0, b:1.33, c:NaN, d:NaN
# ==================== DataFrame运算 ====================
df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) # 第1个DataFrame
df2 = pd.DataFrame({'A': [10, 20, 30], 'B': [40, 50, 60]}) # 第2个DataFrame
print("\nDataFrame加法:")
print(df1 + df2)
# 输出解释:A列:[11, 22, 33], B列:[44, 55, 66]
# ==================== 标量运算(广播) ====================
print("\nDataFrame乘以标量:")
print(df1 * 10) # DataFrame乘以标量,所有元素都乘以10
# 输出解释:A列:[10, 20, 30], B列:[40, 50, 60]10.7.2 统计函数
# =============================================================================
# 题目:使用Pandas统计函数进行金融数据分析
# =============================================================================
# 本代码块演示常用的统计函数,以及按行或按列计算。
# 金融应用场景:计算平均收益率、波动率、累积收益率等关键指标。
# =============================================================================
print("每只股票的统计信息:")
# ==================== 按列计算(每只股票所有交易日) ====================
print("\n均值:")
print(df.mean()) # 计算每列的平均值(默认axis=0)
# 输出解释:显示每只股票5个交易日的平均涨跌幅
print("\n标准差:")
print(df.std()) # 计算每列的标准差(衡量波动性)
# 输出解释:标准差越大,股票波动越大
print("\n最大值:")
print(df.max()) # 计算每列的最大值
# 输出解释:显示每只股票最大的单日涨幅
print("\n最小值:")
print(df.min()) # 计算每列的最小值
# 输出解释:显示每只股票最大的单日跌幅
print("\n中位数:")
print(df.median()) # 计算每列的中位数
# 输出解释:排序后位于中间的值
# ==================== 按行计算(每交易日所有股票) ====================
print("\n按行计算(横向):")
print(df.mean(axis=1)) # axis=1表示沿行方向计算(每行的均值)
# 输出解释:每个交易日4只股票的平均涨跌幅
# ==================== 按列计算(每只股票所有交易日) ====================
print("\n按列计算(纵向):")
print(df.mean(axis=0)) # axis=0表示沿列方向计算(每列的均值,默认)
# 输出解释:与df.mean()相同,每只股票的平均涨跌幅
# ==================== 累积收益率计算 ====================
print("\n累积收益率:")
cum_returns = (1 + df).cumprod() - 1 # 计算累积收益率
print(cum_returns)
# 输出解释:
# (1+df)将涨跌幅转换为收益率因子(如-0.01变为0.99)
# cumprod()计算累积乘积
# -1将因子转换回收益率
# 最终结果是累积收益率(假设初始投资为1)10.8 缺失值处理
# =============================================================================
# 题目:演示缺失值的检测、删除和填充方法
# =============================================================================
# 本代码块演示如何处理数据中的缺失值(NaN)。
# 金融应用场景:处理停牌日的缺失价格,填充股票未交易日的数据。
# =============================================================================
# ==================== 创建包含缺失值的DataFrame ====================
df_with_nan = pd.DataFrame({ # 创建包含NaN的DataFrame
'A': [1, 2, np.nan, 4], # 第3个值为NaN
'B': [5, np.nan, np.nan, 8], # 第2、3个值为NaN
'C': [9, 10, 11, 12] # 无缺失值
})
print("包含缺失值的DataFrame:")
print(df_with_nan)
# 输出解释:NaN表示Not a Number,即缺失值
# ==================== 1. 检测缺失值 ====================
print("\n缺失值位置:")
print(df_with_nan.isnull()) # 检测每个值是否为缺失值
# 输出解释:True表示缺失值,False表示有效值
# ==================== 2. 删除缺失值 ====================
print("\n删除包含缺失值的行:")
print(df_with_nan.dropna()) # 删除包含任何缺失值的行
# 输出解释:只保留第1行和第4行(完整无缺失)
print("\n删除包含缺失值的列:")
print(df_with_nan.dropna(axis=1)) # 删除包含任何缺失值的列
# 输出解释:只保留C列(完全无缺失)
# ==================== 3. 填充缺失值 ====================
print("\n用0填充:")
print(df_with_nan.fillna(0)) # 用0填充所有缺失值
# 输出解释:所有NaN都被替换为0
print("\n用前向填充填充:")
print(df_with_nan.ffill()) # 前向填充(用前一个值填充)
# 输出解释:A列第3行用2填充,B列第2、3行用5填充(第4行本来就是8)
print("\n用均值填充:")
print(df_with_nan.fillna(df_with_nan.mean())) # 用每列的均值填充
# 输出解释:A列用均值2.33填充,B列用均值6.5填充10.9 本章小结
要点:
- Series = 值 + 标签索引;DataFrame = 行列标签的二维表,从字典创建时键自动成为列名
[]取列,.loc按标签取数(切片含端点),.iloc按位置取数(切片不含端点)- 向量化运算自动按索引对齐,索引对不上的位置产生 NaN
shape/dtypes/describe/info是拿到新数据框后的第一批检查动作
易错点:
- 标签切片
s['a':'c']含端点,位置切片s[0:2]不含端点,两套规则不要混 - 两个索引不同的 Series 相加,缺失对齐处得 NaN 而不是 0
df.列名在列名含空格或与 DataFrame 方法重名时失效,通用写法是df['列名']
10.10 动手与思考
以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。
输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。
import pandas as pd s = pd.Series([2, 4, 6], index=['a', 'b', 'c']) print(s['b']) print(s.iloc[1]) t = pd.Series([10, 20, 30], index=['a', 'b', 'd']) print(s + t)参考答案(先写下你的预测再点开)
解题思路:逐行推演。
s['b']按标签索引取值,标签b对应 4;s.iloc[1]按整数位置取值,位置 1(第 2 个元素)同样是 4——本例标签与位置恰好指向同一个元素,但两者依据完全不同。关键是s + t:两个 Series 相加时 pandas 自动按索引对齐,a是 2+10=12,b是 4+20=24;c只在s中、d只在t中,对不上的位置无法相加,得NaN。结果索引是两个索引的并集,且因为出现了NaN,dtype 从 int 变为 float64。# 验证脚本:逐行输出四个表达式的求值结果 import pandas as pd # 导入Pandas库 s = pd.Series([2, 4, 6], index=['a', 'b', 'c']) # 创建带字母标签的Series print(s['b']) # 按标签取值 print(s.iloc[1]) # 按位置取值 t = pd.Series([10, 20, 30], index=['a', 'b', 'd']) # 第二个Series,索引部分不同 print(s + t) # 相加时按索引自动对齐,缺失对齐处为NaN预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
4 4 a 12.0 b 24.0 c NaN d NaN dtype: float64回扣本章:对应本章小结“要点”第 3 条与“易错点”第 2 条——向量化运算自动按索引对齐,索引对不上的位置产生
NaN而不是 0。概念辨析:
s['b']与s.iloc[1]都能取到 4,两种访问方式的依据有何不同?df['中国卫星']与df[['中国卫星']]返回的对象类型分别是什么?参考答案(点开前请先独立完成)
解题思路:逐点作答。第一,
s['b']依据的是标签——到索引里查名字叫b的那一行;s.iloc[1]依据的是位置——取排序后的第 2 个元素。本题中标签b恰好排在位置 1,所以两者都取到 4;但若把索引改成['c', 'b', 'a'],s['b']仍是 4,s.iloc[1]取到的却是a对应的 6,两种依据的差异立刻显现。第二,df['中国卫星']用单个字符串作键,取出的是一列,返回Series(带Name: 中国卫星);df[['中国卫星']]用列表作键,语义是“取列表中的这些列”,返回只含一列的DataFrame。后者是取多列写法df[['列1', '列2']]在单列上的特例,因此保留了二维表结构。# 验证脚本:对比两种取列方式的返回类型 import pandas as pd # 导入Pandas库 df_demo = pd.DataFrame({'中国卫星': [-0.0035, 0.0172], '上汽集团': [0.0212, 0.0021]}, index=['第1日', '第2日']) # 构造两列演示数据框 print(df_demo['中国卫星']) # 单个字符串作键:取出的是一列,返回Series print(df_demo[['中国卫星']]) # 列表作键:取出的是只含一列的DataFrame print(type(df_demo['中国卫星'])) # 验证返回类型 print(type(df_demo[['中国卫星']])) # 验证返回类型预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
第1日 -0.0035 第2日 0.0172 Name: 中国卫星, dtype: float64 中国卫星 第1日 -0.0035 第2日 0.0172 <class 'pandas.core.series.Series'> <class 'pandas.core.frame.DataFrame'>回扣本章:对应本章小结“要点”第 1、2 条与“易错点”第 3 条——
[]取列时单层括号得到 Series、双层括号得到单列 DataFrame,通用写法是df['列名']。变式任务(平台任务一同型改造):仿照平台任务一,为你自选的 4 只股票构造某日涨跌幅 Series(索引用股票简称);再仿照任务二把它扩展为 5 个交易日 × 4 只股票的 DataFrame,并用
describe()查看每列的统计摘要。参考答案(点开前请先独立完成)
解题思路:分两步仿写。第一步仿照 列表 10.2:把 4 个涨跌幅数值放进列表、4 个股票简称放进名称列表,
pd.Series(数据列表, index=名称列表)即得带标签索引的 Series。第二步仿照 列表 10.4:为每只股票准备 5 个交易日的涨跌幅列表,组成“列名→列表”的字典,再pd.DataFrame(字典, 交易日列表)得到 5 行 × 4 列的数据框;describe()随后给出每列的 count、mean、std、min 与四分位数。下方代码中的涨跌幅为自选的演示数值,重点是流程与结构;你应换成自己选定股票的真实涨跌幅。# 变式程序:自选4只股票的涨跌幅Series与5日DataFrame import pandas as pd # 导入Pandas库 list_change = [0.012, -0.008, 0.015, -0.021] # 自选4只股票某日涨跌幅(演示数值) name_stock = ['招商银行', '宁波银行', '三一重工', '中兴通讯'] # 股票简称作为标签索引 s_change = pd.Series(list_change, index=name_stock) # 创建带标签索引的Series print(s_change) # 输出某日涨跌幅Series data_change = { # 每只股票5个交易日的涨跌幅(演示数值) '招商银行': [0.012, -0.006, 0.003, 0.015, -0.002], '宁波银行': [-0.008, 0.011, -0.004, 0.009, 0.006], '三一重工': [0.015, 0.021, -0.012, 0.008, -0.005], '中兴通讯': [-0.021, 0.017, -0.009, 0.013, 0.020] } index_days = ['20240506', '20240507', '20240508', '20240509', '20240510'] # 5个交易日标签 df_change = pd.DataFrame(data_change, index_days) # 创建5行×4列的DataFrame print(df_change) # 输出数据框 print(df_change.describe()) # 每列的统计摘要预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
招商银行 0.012 宁波银行 -0.008 三一重工 0.015 中兴通讯 -0.021 dtype: float64 招商银行 宁波银行 三一重工 中兴通讯 20240506 0.012 -0.008 0.015 -0.021 20240507 -0.006 0.011 0.021 0.017 20240508 0.003 -0.004 -0.012 -0.009 20240509 0.015 0.009 0.008 0.013 20240510 -0.002 0.006 -0.005 0.020 招商银行 宁波银行 三一重工 中兴通讯 count 5.000000 5.000000 5.000000 5.000000 mean 0.004400 0.002800 0.005400 0.004000 std 0.008961 0.008349 0.013722 0.018028 min -0.006000 -0.008000 -0.012000 -0.021000 25% -0.002000 -0.004000 -0.005000 -0.009000 50% 0.003000 0.006000 0.008000 0.013000 75% 0.012000 0.009000 0.015000 0.017000 max 0.015000 0.011000 0.021000 0.020000注意:以上为本题变式的独立代码;列表 10.2 与 列表 10.4 对应的平台原始代码块仍须原样输入教学平台,不要用本变式替换。
回扣本章:对应本章小结“要点”第 1 条——列表加索引创建 Series、字典加行索引创建 DataFrame,
describe()是拿到新数据框后的第一批检查动作之一。动手验证:对平台任务二得到的数据框
df,运行df['中国软件'].equals(df['中国银行'])确认两列是否完全相同;再分别运行df.mean(axis=0)与df.mean(axis=1),说明axis取值对应的计算方向。参考答案(点开前请先独立完成)
解题思路:先运行 列表 10.4 得到
df(或按该代码块的数据在本地重建同一数据框),再执行三行验证。equals逐元素比较两列,返回True即印证正文“数据观察”一段的判断:中国软件与中国银行两列逐行完全相同,是数据录入缺陷而非真实行情。df.mean(axis=0)中的axis=0指沿行方向压缩(行被消掉),对每列求均值,即每只股票 5 个交易日的平均涨跌幅;df.mean(axis=1)沿列方向压缩(列被消掉),对每行求均值,即每个交易日 4 只股票的平均涨跌幅。记忆口径:axis是“被消掉的轴”,不是“保留的方向”。# 验证脚本:先按 @lst-ch10-platform-task-2 的数据在本地重建同一 df,再执行三项检查 import pandas as pd # 导入Pandas库 data = {'中国卫星': [-0.0351, 0.0172, -0.0035, -0.0246, 0.0394], # 四只股票的5日涨跌幅 '中国软件': [-0.0139, 0.0243, -0.0338, 0.0146, 0.0001], '中国银行': [-0.0139, 0.0243, -0.0338, 0.0146, 0.0001], # 与中国软件逐行相同 '上汽集团': [0.0212, 0.0021, -0.0298, -0.0027, -0.0143]} df = pd.DataFrame(data, index=['20200525', '20200526', '20200527', '20200528', '20200529']) # 重建平台任务二的df print(df['中国软件'].equals(df['中国银行'])) # 逐元素比较两列是否完全相同 print(df.mean(axis=0)) # axis=0:沿行方向压缩,得到每列(每只股票)的均值 print(df.mean(axis=1)) # axis=1:沿列方向压缩,得到每行(每个交易日)的均值预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
True 中国卫星 -0.00132 中国软件 -0.00174 中国银行 -0.00174 上汽集团 -0.00470 dtype: float64 20200525 -0.010425 20200526 0.016975 20200527 -0.025225 20200528 0.000475 20200529 0.006325 dtype: float64回扣本章:对应本章学习目标第 4 条与正文“数据观察”段——按行或按列(
axis)计算指标时先想清楚“要消掉哪个轴”;两列均值完全相同(-0.00174)正是它们逐行相等的直接后果。