查看全课大纲(第 11 / 52 节)
- 1.量化分析基本概念:从咖啡馆到华尔街
- 2.量化交易员学习路线:从零基础到策略开发
- 3.量化策略生命周期:数据分析的五大流程
- 4.量化数据类型:数值型与类别型数据
- 5.量化沙盒搭建(1):Python与Anaconda安装
- 6.量化沙盒搭建(2):Jupyter Notebook与库配置
- 7.本地多源数据:量化历史回测的基石
- 8.TXT数据流操作:处理金融交易日志
- 9.JSON数据流操作:解析股票行情API响应
- 10.CSV行情文件操作:读取与存储历史K线
- 11.Excel报表操作:清洗财报与财务指标
- 12.量化数据库:SQL常用查询语法
- 13.量化数据库:Python连接与写入
- 14.量化多表连接:关联股票信息与行情
- 15.实战:小象咖啡店营业数据与个股财务指标的多源数据分析
- 16.量化数据采集:爬虫工作原理与合规
- 17.量化爬虫核心:URL管理与去重策略
- 18.量化网页下载:防反爬与请求头伪装
- 19.网页解析(1):使用XPath定位财经新闻
- 20.网页解析(2):使用CSS选择器与ItemLoader
- 21.Scrapy框架:构建工业级量化爬虫
- 22.Scrapy实战(1):新建项目与数据模型定义
- 23.Scrapy实战(2):编写财经爬虫逻辑
- 24.Scrapy实战(3):启用数据管道清洗行情
- 25.Scrapy实战(4):防反爬中间件配置
- 26.实战:构建每日热门个股行情自动采集系统
- 27.NumPy与SciPy:加速量化矩阵计算
- 28.NumPy数组:构建股票价格矩阵
- 29.数组切片:提取特定股票与交易日数据
- 30.NumPy常用方法:计算均值与波动率
- 31.向量化计算:摆脱缓慢的For循环
- 32.通用函数ufunc:计算对数收益率与累计收益
- 33.实战:股票组合资产收益率与协方差矩阵计算
- 34.Pandas Series:管理单只股票收盘价
- 35.Pandas DataFrame:构建多只股票行情面板
- 36.Pandas Index:时间序列索引与对齐
- 37.DataFrame索引操作:loc与iloc定位行情
- 38.索引总结:量化多字段筛选与定位
- 39.运算与对齐:合并不同交易日的数据
- 40.Pandas map:对股票代码与状态做映射
- 41.Pandas apply:滚动计算技术指标
- 42.排序操作:按市盈率与收益率对股票排名
- 43.数据清洗:填充与剔除停牌股票缺失值
- 44.数据清洗:剔除重复报价与异常交易数据
- 45.常用统计:量化最大回撤与分位数风险评估
- 46.极值定位:寻找历史最高价与最低价的发生日
- 47.风险与累计收益:计算年化波动率与累计回报
- 48.实战:小象咖啡店营业数据与热门个股时间序列清洗分析
- 49.Series索引操作:按位置与标签访问行情数据
- 50.文件读写操作:CSV行情数据导入导出
- 51.数据清洗:替换异常报价数据
- 52.常用统计:sum/mean/median/count聚合指标
Excel报表操作:清洗财报与财务指标
约 10 分钟
小象实战讲义 · 量化交易Python入门:从咖啡馆到华尔街
在量化交易中,历史回测数据往往以多种异构本地文件(TXT、CSV、JSON、Excel)和关系数据库形式存在。本章你将编写数据接口,读取小象咖啡的门店营收数据并解析股票行情API响应,打通量化回测的数据通道!
💡 核心导读
本节将系统讲解如何使用Python处理Excel文件,涵盖主流库的对比选型、数据读写操作技巧以及典型问题解决方案。通过本节学习,您将掌握:1) 使用pandas进行Excel数据高效读写 2) 处理多Sheet和格式转换 3) 解决中文编码等常见问题。配套的代码示例可直接应用于实际工作中的数据采集场景。
工具选型与基础操作
主流库对比
| 工具库 | 读写速度 | 功能完整性 | 内存占用 | 适用场景 |
|---|---|---|---|---|
| pandas | ★★★★ | ★★★★ | ★★★ | 常规数据分析 |
| openpyxl | ★★ | ★★★★★ | ★★ | 复杂格式处理 |
| xlrd/xlwt | ★★★ | ★★ | ★★★★ | 旧版xls文件处理 |
基础读写示例
import pandas as pd
# 构造示例销售数据
data = {
'订单日期': ['2023年01月15日', '2023年01月16日'],
'产品': ['A', 'B'],
'数量': [10, 5],
'金额': ['¥1000.50', '¥750.00'],
'地区': ['北京', '上海']
}
df = pd.DataFrame(data)
print("原始数据结构:")
print(df.head(2).to_string())
print("\n数据类型:")
print(df.dtypes.to_string())原始数据结构:
订单日期 产品 数量 金额 地区
0 2023年01月15日 A 10 ¥1000.50 北京
1 2023年01月16日 B 5 ¥750.00 上海
数据类型:
订单日期 str
产品 str
数量 int64
金额 str
地区 str高级操作技巧
多Sheet处理
import pandas as pd
# 构造多季度数据,模拟多sheet文件
q1_data = {
'股票代码': ['000001', '000002'],
'季度': ['Q1', 'Q1'],
'营收': [100.5, 200.3],
'净利润': [20.1, 40.5]
}
q2_data = {
'股票代码': ['000001', '000002'],
'季度': ['Q2', 'Q2'],
'营收': [110.2, 210.8],
'净利润': [22.3, 42.7]
}
q3_data = {
'股票代码': ['000003'],
'季度': ['Q3'],
'营收': [50.0],
'净利润': [5.0]
}
all_sheets = {
'Q1': pd.DataFrame(q1_data),
'Q2': pd.DataFrame(q2_data),
'Q3': pd.DataFrame(q3_data)
}
merged_data = pd.concat([
all_sheets['Q1'],
all_sheets['Q2']
], ignore_index=True)
print("合并前Sheet数量:", len(all_sheets))
print("合并后数据量:", len(merged_data))
print("\n合并后数据预览:")
print(merged_data.to_string())合并前Sheet数量: 3
合并后数据量: 4
合并后数据预览:
股票代码 季度 营收 净利润
0 000001 Q1 100.5 20.1
1 000002 Q1 200.3 40.5
2 000001 Q2 110.2 22.3
3 000002 Q2 210.8 42.7数据类型转换
import pandas as pd
# 构造带货币符号和中文日期的数据
data = {
'订单日期': ['2023年01月15日', '2023年02月20日', '2023年03月10日'],
'产品': ['股票A', '股票B', '股票C'],
'金额': ['¥1000.50', '¥750.00', '¥1200.75']
}
df = pd.DataFrame(data)
print("转换前数据类型:")
print(df.dtypes.to_string())
# 清洗转换
df['金额'] = df['金额'].str.replace('¥', '', regex=False).astype(float)
df['订单日期'] = pd.to_datetime(df['订单日期'], format='%Y年%m月%d日')
print("\n转换后数据类型:")
print(df.dtypes.to_string())
print("\n转换后数据:")
print(df.to_string())转换前数据类型:
订单日期 str
产品 str
金额 str
转换后数据类型:
订单日期 datetime64[us]
产品 str
金额 float64
转换后数据:
订单日期 产品 金额
0 2023-01-15 股票A 1000.50
1 2023-02-20 股票B 750.00
2 2023-03-10 股票C 1200.75常见问题解决方案
中文编码处理
import pandas as pd
# 模拟读取Excel文件,演示sheet_name参数
# 构造示例财务报表数据
income_statement_data = {
'项目': ['营业收入', '营业成本', '毛利润', '营业费用', '净利润'],
'2023Q1': [1000.0, 600.0, 400.0, 200.0, 200.0],
'2023Q2': [1100.0, 650.0, 450.0, 220.0, 230.0]
}
df_income = pd.DataFrame(income_statement_data)
# 模拟 pd.read_excel('report.xlsx', sheet_name='IncomeStatement')
df = df_income
print("利润表数据形状:", df.shape)
print("\n利润表数据:")
print(df.to_string())利润表数据形状: (5, 3)
利润表数据:
项目 2023Q1 2023Q2
0 营业收入 1000.0 1100.0
1 营业成本 600.0 650.0
2 毛利润 400.0 450.0
3 营业费用 200.0 220.0
4 净利润 200.0 230.0大文件分块处理
import pandas as pd
# 构造一个稍大的示例数据集,用于演示分块处理
large_data = {
'日期': pd.date_range('2023-01-01', periods=25000, freq='D'),
'股票代码': ['000001'] * 25000,
'收盘价': [10.0 + i * 0.001 for i in range(25000)],
'成交量': [1000000 + i * 100 for i in range(25000)]
}
df_large = pd.DataFrame(large_data)
# 使用pandas的chunksize参数进行分块迭代(这里用DataFrame的groupby模拟chunks)
chunk_size = 10000
total_rows = len(df_large)
print(f"总数据量: {total_rows} 行")
print(f"分块大小: {chunk_size}")
# 模拟分块处理逻辑
for start in range(0, total_rows, chunk_size):
end = start + chunk_size
chunk = df_large.iloc[start:end]
chunk_num = start // chunk_size + 1
print(f"正在处理第 {chunk_num} 个数据块,行范围: {start}-{end-1}")
# 在此处添加处理逻辑,例如计算每块的均值
mean_price = chunk['收盘价'].mean()
print(f" 本块收盘价均值: {mean_price:.4f}")总数据量: 25000 行
分块大小: 10000
正在处理第 1 个数据块,行范围: 0-9999
本块收盘价均值: 14.9995
正在处理第 2 个数据块,行范围: 10000-19999
本块收盘价均值: 24.9995
正在处理第 3 个数据块,行范围: 20000-29999
本块收盘价均值: 32.4995📝 动手练一练
第 1 题 · 概念应用
你正在处理一份从某券商导出的股票持仓 Excel 文件 portfolio.xlsx。该文件包含两个工作表:CurrentHoldings(当前持仓)和 TransactionHistory(交易历史)。你只需要分析当前持仓数据,并希望避免因读取整个文件而消耗过多内存。以下哪种做法最符合你的需求?
- A. 使用
pd.read_excel(‘portfolio.xlsx’),因为 pandas 默认会智能地只读取第一个工作表。 - B. 使用
pd.read_excel(‘portfolio.xlsx’, sheet_name=0),通过索引指定第一个工作表。 - C. 使用
pd.read_excel(‘portfolio.xlsx’, sheet_name=‘CurrentHoldings’),通过名称精确读取目标工作表。 - D. 使用
pd.read_excel(‘portfolio.xlsx’, sheet_name=None),读取所有工作表后再提取CurrentHoldings。
👉 点击查看参考答案
答案:C
推理过程:
- 选项 A 错误。
pd.read_excel在不指定sheet_name参数时,默认读取第一个工作表,但这是一种隐式行为,并非“智能选择”。如果目标工作表不是第一个,此方法会出错。 - 选项 B 可行,但不稳健。它通过索引(0)读取第一个工作表。如果文件结构发生变化(例如,工作表顺序调整),代码可能读取到错误的数据。在金融数据处理中,明确指定工作表名称是更可靠的做法。
- 选项 C 是最佳实践。通过
sheet_name=‘CurrentHoldings’直接按名称读取目标工作表,代码意图清晰,对工作表顺序变化不敏感,符合精确处理的需求。 - 选项 D 错误。
sheet_name=None会读取所有工作表到一个字典中,这会消耗更多内存(尤其当TransactionHistory表很大时),与题目“避免消耗过多内存”的要求相悖。
因此,最符合需求的做法是 C。
第 2 题 · 动手题(代码填空)
你收到一份来自分析师的简易财报数据,其中“净利润”列带有货币单位“万元”。你需要将其转换为纯数值(浮点型),以便后续计算净资产收益率(ROE)。请补全下方代码中的关键步骤。
import pandas as pd
# 模拟从Excel读取的简易资产负债表数据
data = {
'公司': ['公司A', '公司B', '公司C'],
'净资产(万元)': ['5000', '3200', '1800'],
'净利润(万元)': ['¥800.5', '¥450.2', '¥210.8'] # 注意包含货币符号
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print("\n原始数据类型:")
print(df.dtypes)
# 任务:清洗"净利润(万元)"列,移除货币符号"¥"并转换为浮点数
# 请补全下方代码
df['净利润(万元)'] = df['净利润(万元)'].str.replace(______, '').astype(float)
print("\n清洗后数据:")
print(df)
print("\n清洗后数据类型:")
print(df.dtypes)👉 点击查看参考答案
空位应填:‘¥’
完整可运行代码:
import pandas as pd
# 模拟从Excel读取的简易资产负债表数据
data = {
'公司': ['公司A', '公司B', '公司C'],
'净资产(万元)': ['5000', '3200', '1800'],
'净利润(万元)': ['¥800.5', '¥450.2', '¥210.8'] # 注意包含货币符号
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print("\n原始数据类型:")
print(df.dtypes)
# 任务:清洗"净利润(万元)"列,移除货币符号"¥"并转换为浮点数
df['净利润(万元)'] = df['净利润(万元)'].str.replace('¥', '').astype(float)
print("\n清洗后数据:")
print(df)
print("\n清洗后数据类型:")
print(df.dtypes)运行结果:
原始数据:
公司 净资产(万元) 净利润(万元)
0 公司A 5000 ¥800.5
1 公司B 3200 ¥450.2
2 公司C 1800 ¥210.8
原始数据类型:
公司 str
净资产(万元) str
净利润(万元) str
dtype: object
清洗后数据:
公司 净资产(万元) 净利润(万元)
0 公司A 5000 800.5
1 公司B 3200 450.2
2 公司C 1800 210.8
清洗后数据类型:
公司 str
净资产(万元) str
净利润(万元) float64
dtype: object解释:
- 核心操作是使用
Series.str.replace()方法移除字符串中的特定字符。此处需要移除的是人民币符号‘¥’。 - pandas 2.0 起
Series.str.replace默认regex=False,即按字面量匹配替换:str.replace('¥', '')直接把货币符号替换为空串即可;只有需要按正则模式替换时才显式传regex=True(pandas 1.x 时代默认是正则,升级后注意这个变化)。 - 移除符号后,字符串
‘800.5’可以通过.astype(float)安全地转换为浮点数,为后续的财务指标计算(如 ROE = 净利润 / 净资产)做好准备。
本章小结
📋 行动清单
- 使用pandas读取本地Excel文件,尝试用
to_markdown()方法输出前5行数据 - 创建一个包含多个Sheet的Excel文件,编写代码合并所有Sheet数据
- 处理包含中文日期格式(如”2023年12月31日”)的列,转换为标准datetime类型
「小象量化教研组」建议在实际操作时注意:1) 始终指定engine参数避免版本问题 2) 处理大型文件时使用chunksize参数 3) 重要操作前先创建数据备份。
—— 小象教研组