本章会用到的数据
公司信息 :公司基本信息
财务数据 :财务报表
本地缓存 :首次运行时下载,并保存到 data/course/。
这些数据能做什么 :认识公司、财务指标、日期和表格结构,并练习筛选、汇总与可视化。
分析时注意 :财务信息从 info_date 起才视为可用,不能用尚未披露的数据解释更早的决策。
示例说明 :模拟数据只用于解释方法,不代表真实市场表现。
【课堂核心】2 学时学习安排
动机与先修
15
概念与公式
25
跟做示例
30
形成性检查
15
独立任务与反馈
25
本章小结
10
【可选拓展】 :数据库史、完整图库、境外/生成数据机制例与额外 API 示例,安排课后阅读,不占核心时间。
今日议题:我们如何从数据海洋中淘金?
我们的核心挑战,是如何不被海量数据淹没,并从中提取出能够驱动决策的 α (Alpha) 信号。
从数据噪声中识别金融信号 原始数据经过筛选与验证后形成可用于决策的信号。
原始数据 行情 · 财报 · 文本 清洗与验证 信息时点 · 样本外 α
基本数据说明:七项信息缺一不可
文件、key
确定数据文件与逻辑数据表,避免读取同名异表
字段、单位
确定变量含义与量纲,防止元/万元或价格/收益率混用
频率、样本期
确定观察粒度与覆盖范围,防止日频、季频或区间错配
可得日
记录信息真正可用于决策的日期,而不是它描述的财年
反例:财年早于决策日,不等于信息已经可得。
决策时点 :研究者在 2024-03-31 做预测。
实际披露 :2023 年报中的该字段到 2024-04-30 才披露。
错误性质 :可得日晚于决策日,构成前瞻偏差。
修正方法 :删除该字段,或把决策日推迟到披露之后。
本章目标:掌握数据分析的“三板斧”
今天课程结束时,你将掌握从原始数据中提取价值的核心三步。这三步构成了我们数据分析步骤的基石。
1. 理解数据存储
辨析不同存储介质与文件格式的优劣,为数据选择合适的“家”。
2. 掌握数据操纵
使用 Pandas 对数据进行读取、清洗、筛选、聚合与合并。
3. 实现数据可视化
用 Seaborn 将枯燥的数据转化为直观、有洞察力的图表。
本章三项学习目标 依次学习数据存储、数据操纵与数据可视化。
数据存储 格式 · 容量 · 读取 数据操纵 清洗 · 筛选 · 合并 数据表达 图表 · 口径 · 结论
金融数据管理的挑战与机遇
金融数据不仅量大,而且复杂,这既是挑战也是机遇。高效的数据管理能力是区分业余和专业的关键。
挑战
如何高效地存储、读取和处理 TB 甚至 PB 级别的数据?
如何保证数据质量,避免“垃圾进,垃圾出”?
如何在噪音中识别出微弱的信号?
机遇
投资决策 : 股市指标预测公司基本面。
风险管理 : 银行转账信息评估信用风险。
监管科技 : 识别异常交易模式。
本章学习路线图
我们将遵循一条从理论到工具,再到实践的清晰路径,确保你系统地掌握本章知识。
Chapter Roadmap
路线图依次展示理论、工具与实践三个学习阶段。
1. 理论基础
存储 & 格式
2. 核心工具
Pandas & Seaborn
3. 动手实践
数据探索 & 可视化
分析师的困境:速度 vs. 空间
在数据分析中,我们始终在两个核心存储设备之间进行权衡:内存 (RAM) 和 硬盘 (Hard Drive/SSD) 。理解它们的区别,是高效处理数据的第一步。
一个生动的类比:工作台 vs. 仓库
将内存和硬盘想象成你的工作台 和仓库 ,这能帮助你直观地理解它们各自的角色、优缺点和协同工作的模式。
内存与持久存储的取舍 内存速度快但容量有限,持久存储容量大但读取较慢,数据分析需要在两者之间分块交换。
内存 RAM 速度快 · 容量有限 硬盘 SSD/HDD 容量大 · 读取较慢 按需读取 · 分块处理 · 及时释放
内存与硬盘的协同步骤
数据分析的典型步骤完美体现了二者的协同:从硬盘(仓库)中调取数据到内存(工作台)进行处理,然后将结果存回硬盘。
Data Workflow between RAM and HDD
流程图展示数据从硬盘长期存储进入内存处理,再把结果写回硬盘的分析过程。
硬盘 (仓库)
长期存储海量数据
内存 (工作台)
高速计算与分析
2. 读入数据进行分析
4. 将结果写回保存
1. 原始数据
3. 程序执行
内存 vs. 硬盘:核心特性对比
角色
工作区 (Workspace)
存储区 (Storage)
速度
极快
相对较慢
容量
较小 (8GB - 64GB)
巨大 (256GB - 数TB)
持久性
易失性 (断电即清空)
非易失性 (长期保存)
成本
每GB成本高
每GB成本低
数据格式:为数据选择合适的“容器”
数据不仅要选择存储设备,还要选择合适的文件格式 。这决定了数据的结构、性能和通用性。我们将重点关注四种主流格式。
Four Key Data Formats
四个图标分别表示 CSV、XLSX、JSON 与 Parquet 数据格式。
CSV
通用文本
XLSX
电子表格
{ }
网络数据
[ C ]
大数据
文件格式 1: CSV (逗号分隔值)
结构 : 纯文本,以逗号分隔值的表格数据。
优点 : 格式简单,人类可读性高,通用性极强。
缺点 : 无数据类型信息,处理大文件时读写慢,占用空间较大。
金融用例 : 分享小规模的日度收盘价数据。
文件格式 2: XLSX (Excel 工作簿)
结构 : 微软Excel的专有格式,支持多工作表、公式、图表。
优点 : 商业分析中最常用,支持丰富的数据类型和格式化。
缺点 : 格式复杂导致读写较慢,非纯文本,依赖特定库。
金融用例 : 制作需要格式化和图表的财务报告。
文件格式 3: JSON (JavaScript 对象表示法)
结构 : 纯文本,基于键值对的层次化数据结构。
优点 : 结构灵活清晰,人类可读性高,是网络API数据交换的标准。
缺点 : 对于大型表格数据,冗余信息多,效率不高。
金融用例 : 从券商API获取账户信息、订单状态等嵌套数据。
文件格式 4: Parquet (列式存储)
结构 : 二进制,列式存储,专为大数据分析设计。
优点 : 读写速度极快 (特别是读取特定列时),压缩率高,自带数据类型。
缺点 : 二进制格式,人类不可直接阅读。
金融用例 : 存储海量历史高频交易数据、行情数据版本。
什么是“列式存储”?
行式存储(如CSV)像读书一样,一行一行存数据。列式存储(如Parquet)像查字典一样,一列一列存数据,这使其在分析查询时效率极高。
行式与列式存储查询比较 左侧行式存储为计算价格均值读取每行全部字段,右侧列式存储只扫描价格列,用高亮数据块对比分析查询的I/O范围。
行式存储 (Row-Oriented)
分析查询: 计算平均价格
SELECT AVG(Price)
硬盘读取操作 (Disk Read)
...
效率低下
必须读取包含无关列的整行数据
列式存储 (Columnar)
分析查询: 计算平均价格
SELECT AVG(Price)
硬盘读取操作 (Disk Read)
非常高效
只需精确读取“价格”列的数据
我们的核心工具(一): Pandas 简介
Pandas 用带行列标签的 DataFrame 串起一条最小可检查结果程:
选择
df.loc[rows, columns]
行筛选条件、字段清单与样本数
清洗
dropna、astype、主键去重
缺失率、类型、重复键与行数变化
聚合
groupby(...).agg(...)
分组键、统计量、分母与单位
合并时再用 merge(..., validate='one_to_one') 声明键关系;任何重复键、未匹配或意外增删行都必须先停止并修正。
Pandas 的核心:DataFrame 对象
一个DataFrame是一个二维的、大小可变的、异构的表格数据结构,带有标记的轴(行和列)。
Anatomy of a Pandas DataFrame
示意图把 DataFrame 分为索引、列名与数据区三个组成部分。
600104
150.5
1.2M
600276
2730.1
800K
603195
780.0
2.5M
Ticker
Price
Volume
0
1
2
Columns (列)
Index (索引)
Data (数据)
Pandas 的核心优势:直观且强大
标签对齐
操作自动按行和列的标签对齐,避免了手动管理的麻烦。
轻松处理缺失值
提供了简单易用的方法来处理 NaN (Not a Number)。
功能丰富
groupby, merge, pivot_table 等高级功能让复杂的数据操作变得简单。
生态系统
与 Matplotlib, Seaborn, Scikit-learn 等库无缝集成。
我们也必须了解Pandas的局限性
了解工具的边界同样重要。
1. 默认内存步骤的限制
默认行为 :eager DataFrame 步骤通常把当前分区物化到内存。
失败条件 :单次全量读取超过可用内存时会失败。
本章策略 :使用 HDF5 选择性读取、chunksize 分块或流式迭代。
规模继续上升 :转向第 18 章的分区并行框架。
Out of Memory Error 大数据集无法装入容量较小内存的示意图。
50GB Dataset vs. 16GB RAM RAM (16GB) Data (50GB) 内存溢出
2. 非原生SQL支持
Pandas 本身不执行SQL查询。要在DataFrame上使用SQL语法,需要借助 pandasql 等第三方库。
我们的核心工具(二): Matplotlib 简介
Matplotlib 是 Python 数据可视化的奠基者 。
定位 : 一个功能强大但相对底层的绘图库,提供了对图表每个元素的完全控制能力。
优点 : 灵活性极高 ,应用广泛,图表类型丰富。
类比 : Matplotlib 就像是画家的画笔和颜料 ,你可以画出任何你想要的东西,但这需要你手动调配。
我们的核心工具(三): Seaborn 简介
Seaborn 是基于 Matplotlib 的高级统计数据可视化库 。
定位 : 专注于绘制美观且信息丰富的统计图表。
优点 : 高级接口,美观默认值,与Pandas完美集成。
类比 : Seaborn 就像一个专业的图表设计师 ,你告诉他数据和主题,他就能迅速生成专业、美观的图表。
Matplotlib vs. Seaborn
Matplotlib vs. Seaborn Analogy
类比图将 Matplotlib 比作可完全控制的调色板,将 Seaborn 比作快速统一样式的设计模板。
Matplotlib
画家的颜料盘:完全控制
Seaborn
设计师的模板:快速专业
实战场景
我们将通过一个实际案例,一步步学习Pandas的核心操作。
场景 : 我们有一份虚拟的投资者信息数据,包含ID、收入、性别和股票投资组合金额。
目标 : 对这份数据进行读取、检查、筛选、扩展、聚合、合并,并最终保存。
步骤0: 准备工作环境和数据
在开始之前,我们需要导入Pandas库,并创建一个模拟的数据文件 income_stock.csv。
为了保证结果可复现,我们使用 np.random.seed(42) 固定随机数。
代码
# 为“步骤0: 准备工作环境和数据”导入 `pandas` 并绑定 `pd`,用于整理当前任务的表格、字段与时间索引。
import pandas as pd
# 为“步骤0: 准备工作环境和数据”导入 `numpy` 并绑定 `np`,用于执行当前任务的数组、数值或随机机制计算。
import numpy as np
# 为“步骤0: 准备工作环境和数据”导入 `os`,用于检查练习文件是否存在。
import os
# 为“步骤0: 准备工作环境和数据”,设置随机种子以保证结果可复现
np.random.seed(42 )
# 构造十名教学用户的固定字段字典,供 DataFrame 创建、文件读写与合并机制演示。
data = {
# 生成十个唯一用户编号,作为两张演示表的合并键。
'id' : [f'i_ { i+ 1 } ' for i in range (10 )],
# 生成十个整数收入值,用于筛选与分组统计演示。
'income' : np.random.randint(25000 , 500000 , 10 ),
# 固定十条性别标签,保证分组与条件筛选都有样本。
'gender' : ['F' , 'M' , 'M' , 'F' , 'M' , 'F' , 'M' , 'F' , 'F' , 'M' ],
# 生成十个持股金额,用于计算持股收入比。
'stock' : np.random.uniform(15000 , 250000 , 10 )
}
# 将用户字段整理为收入与持股演示表。
df_demo = pd.DataFrame(data)
# 构造同一用户主键对应的 GPA 字典,作为后续一对一合并的右表输入。
gpa_data = {
# 复用相同十个用户编号,使 GPA 可按编号并入主表。
'id' : [f'i_ { i+ 1 } ' for i in range (10 )],
# 生成两位小数 GPA,作为右表待合并字段。
'gpa' : np.round (np.random.uniform(2.0 , 4.0 , 10 ), 2 )
}
# 将用户编号与 GPA 整理为合并演示右表。
df_gpa_demo = pd.DataFrame(gpa_data)
# 仅在输出目录缺失时创建目录,避免改动已有练习文件。
if not os.path.exists('data' ):
# 当输出目录不存在时创建该目录。
os.makedirs('data' )
# 保存表格数据版本,供后续读取示例复用。
df_demo.to_csv('data/income_stock.csv' , index= False )
# 保存表格数据版本,供后续读取示例复用。
df_gpa_demo.to_csv('data/gpa.csv' , index= False )
# 报告两份演示 CSV 已写入公开数据目录。
print ('演示数据 "income_stock.csv" 和 "gpa.csv" 已创建。' )
演示数据 "income_stock.csv" 和 "gpa.csv" 已创建。
步骤1: 将数据从CSV文件读入内存
我们的第一个任务是将硬盘上的 income_stock.csv 文件加载到内存中的 Pandas DataFrame 中。
我们使用 pd.read_csv() 函数。
代码
# 读取固定公开数据版本并保留来源口径。
df = pd.read_csv('data/income_stock.csv' )
# 报告 CSV 已读入 `df`,提示学生继续检查字段与类型。
print ('数据成功读入名为 df 的 DataFrame 中。' )
数据成功读入名为 df 的 DataFrame 中。
步骤2: 对数据进行第一次“侦察”
数据读入后,绝不能假设它是完美的。首先要快速检查它的基本情况。
问题1 : 数据长什么样?(预览)
问题2 : 数据有多大?(维度)
问题3 : 每列存储的是什么类型的数据?(类型)
侦察工具1: .head() - 快速预览数据
.head() 方法可以显示 DataFrame 的前5行(默认),让我们对数据结构有一个直观的认识。
代码
# 为“侦察工具1: `.head()` - 快速预览数据”,显示前几条记录以核对字段与取值。
df.head()
0
i_1
146958
F
51658.712279
1
i_2
171867
M
28649.648860
2
i_3
156932
M
218551.394257
3
i_4
390838
F
156262.027760
4
i_5
284178
M
181397.055782
侦察工具2: .shape - 查看数据维度
.shape 属性返回一个元组,表示 (行数, 列数)。
代码
# 显示 `df` 的行数与列数,检查“侦察工具2: .shape - 查看数据维度”的数据规模。
df.shape
解读 : 这个输出 (10, 4) 告诉我们,这个数据集有 10 个观测样本(行),每个样本有 4 个特征(列)。
侦察工具3: .dtypes - 检查数据类型
.dtypes 属性告诉我们每一列存储的数据是什么格式。这是至关重要的一步,因为错误的类型会导致计算错误。
代码
# 列出 `df` 各字段的数据类型,发现读取或转换造成的类型偏差。
df.dtypes
id object
income int64
gender object
stock float64
dtype: object
解读 :
income 是 int64 (整数),正确。
stock 是 float64 (浮点数/小数),正确。
id 和 gender 是 object,这通常表示文本(字符串),也正确。
步骤3: 数据选择与切片 - 获取你关心的部分
我们很少需要一次性处理整个数据集。更常见的操作是选择特定的列或行。
选择列 : 分析特定变量。
选择行 : 分析特定观测样本。
选择单列:使用方括号 []
要获取一个完整的列,可以使用 df['列名'] 的语法。这将返回一个 Pandas Series 对象。
代码
# 从数据框提取持股金额列为 `stock_series`,演示单列索引返回 Series。
stock_series = df['stock' ]
# 为“选择单列:使用方括号 `[]`”,显示前几条记录以核对字段与取值。
stock_series.head()
0 51658.712279
1 28649.648860
2 218551.394257
3 156262.027760
4 181397.055782
Name: stock, dtype: float64
选择多列:传递一个列表
要选择多个列,向方括号中传递一个包含列名的列表。
代码
# 为“选择多列:传递一个列表”,显示前几条记录以核对字段与取值。
df[['id' , 'income' ]].head()
0
i_1
146958
1
i_2
171867
2
i_3
156932
3
i_4
390838
4
i_5
284178
选择单行:使用 .iloc[] 按位置索引
要根据行号(从0开始)选择一行,使用 .iloc[行号]。
代码
# 按位置取得第一条用户记录,而不是保留尚未调用的索引器。
user_0 = df.iloc[0 ]
# 输出第 1 行记录,核对 `.iloc[0]` 的位置索引结果。
print (user_0)
id i_1
income 146958
gender F
stock 51658.712279
Name: 0, dtype: object
步骤4: 数据筛选 - 根据条件过滤数据
筛选是数据分析中最核心的操作之一。我们提出问题,然后用条件来过滤出答案。
核心思想:布尔掩码 (Boolean Masking)
布尔掩码 Part 1: 创建条件
首先,我们创建一个布尔条件。Pandas会逐行判断这个条件,返回一个由 True / False 组成的 Series。
问题 : 哪些用户是男性?
代码
# 创建一个布尔掩码
mask_male = (df['gender' ] == 'M' )
# 输出性别条件生成的布尔序列,检查每行是否满足男性筛选条件。
print (mask_male)
0 False
1 True
2 True
3 False
4 True
5 False
6 True
7 False
8 False
9 True
Name: gender, dtype: bool
布尔掩码 Part 2: 应用掩码
然后,将这个布尔 Series 放入 df[] 中,Pandas 会返回所有对应值为 True 的行。
代码
# 从上一步获取掩码
mask_male = (df['gender' ] == 'M' )
# 将掩码应用于DataFrame
df_male = df[mask_male]
# 为“布尔掩码 Part 2: 应用掩码”,显示前几条记录以核对字段与取值。
df_male.head()
1
i_2
171867
M
28649.648860
2
i_3
156932
M
218551.394257
4
i_5
284178
M
181397.055782
6
i_7
135268
M
242928.815258
9
i_10
162337
M
57728.867294
多条件筛选:使用 & (与) 和 | (或)
当有多个条件时,需要用 & (and) 或 | (or) 连接。
重要 : 每个条件都必须用括号 () 括起来!
问题 : 筛选出性别为男 并且 收入 > 300,000 的用户。
代码
# 同时应用男性与高收入条件,得到满足两项标准的记录子集。
df_male_highinc = df[(df['gender' ] == 'M' ) & (df['income' ] > 300000 )]
# 输出双条件筛选后的记录,验证布尔条件按行同时成立。
print (df_male_highinc)
Empty DataFrame
Columns: [id, income, gender, stock]
Index: []
步骤5: 创建新变量 - 特征工程的基础
通常,原始数据本身不足以回答问题。我们需要基于现有变量创建新的、更有意义的变量(特征)。
问题 : 用户的投资额占其收入的比例是多少?
创建新列:直接赋值
我们可以像给字典赋值一样,直接创建一个新列。
代码
# 用持股金额除以收入,生成衡量资产相对收入规模的比例列。
df['stock_income_ratio' ] = df['stock' ] / df['income' ]
# 为“创建新列:直接赋值”,显示前几条记录以核对字段与取值。
df.head()
0
i_1
146958
F
51658.712279
0.351520
1
i_2
171867
M
28649.648860
0.166697
2
i_3
156932
M
218551.394257
1.392650
3
i_4
390838
F
156262.027760
0.399813
4
i_5
284178
M
181397.055782
0.638322
解读 : 一个名为 stock_income_ratio 的新列被创建出来,它的值是每一行 stock 除以 income 的结果。
步骤6: 数据汇总与聚合 - 从细节到宏观
我们经常需要从个体数据中计算出汇总统计量,以了解数据的宏观特征。
问题1 : 数据集的整体统计特征是怎样的?
问题2 : 不同性别的用户在收入和投资上有什么差异?
汇总工具1: .describe() - 获取描述性统计
.describe() 方法可以快速计算出所有数值型列的常用统计指标。
代码
# 汇总 `df` 各数值列的计数、位置与离散统计量。
df.describe()
count
10.000000
10.000000
10.000000
mean
190603.500000
123253.758925
0.695423
std
89590.564097
86907.971239
0.544712
min
79886.000000
19837.356160
0.136924
25%
145398.750000
53176.251033
0.352543
50%
159634.500000
110580.859385
0.519067
75%
217635.750000
203317.279387
0.881390
max
390838.000000
242928.815258
1.795907
汇总工具2: groupby() - 分组聚合的神器
groupby() 是 Pandas 中最强大的功能之一。它遵循 “拆分-应用-合并” (Split-Apply-Combine) 的思想。
Split-Apply-Combine Diagram
示意图展示 groupby 将表格拆成多组、逐组应用函数,再把结果合并成新表格。
Key
Data
A
1
B
5
A
3
A
2
B
4
DataFrame
拆分
1
3
2
5
4
应用 (Apply)
sum()
6
9
合并
Key
Sum
A
6
B
9
Result
groupby() 实战:按性别分析收入和投资
问题 : 计算男性和女性用户各自的平均收入/投资额、中位数和标准差。
我们可以使用 .agg() 方法一次性应用多个聚合函数。
代码
# 我们想对 'income' 和 'stock' 这两列进行操作
# 我们想计算 'mean', 'median', 'std' 这三个统计量
grouped_stats = df.groupby('gender' )[['income' , 'stock' ]].agg(['mean' , 'median' , 'std' ])
# 输出性别分组统计,比较各组收入与持股金额的聚合结果。
print (grouped_stats)
income stock \
mean median std mean median
gender
F 199090.6 146958.0 120183.237462 100656.361559 64899.691009
M 182116.4 162337.0 58612.864725 145851.156290 181397.055782
std
gender
F 79680.450713
M 96791.836696
步骤7: 数据合并 - 整合多个数据源
在现实世界中,数据往往来自不同的文件或数据库。我们需要将它们整合起来进行统一分析。
场景 : 我们有另一份 gpa.csv 文件,包含了用户的大学GPA信息。
合并工具: pd.merge() - 类似SQL的连接操作
pd.merge() 函数可以根据一个或多个共同的键(key)将两个 DataFrame 连接在一起。
代码
# 先读入gpa数据
df_gpa = pd.read_csv('data/gpa.csv' )
# 标出合并前的 GPA 右表预览。
print ('GPA 数据预览:' )
# 输出 GPA 表前两行,确认合并前右表的主键与字段。
print (df_gpa.head(2 ))
# 按 'id' 列合并两个DataFrame
df_merged = pd.merge(df, df_gpa, on= 'id' )
# 标出按用户编号合并后的表格预览。
print (' \n 合并后的数据预览:' )
# 输出合并表前两行,确认 `id` 匹配后收入与 GPA 位于同一记录。
print (df_merged.head(2 ))
GPA 数据预览:
id gpa
0 i_1 2.37
1 i_2 2.61
合并后的数据预览:
id income gender stock stock_income_ratio gpa
0 i_1 146958 F 51658.712279 0.351520 2.37
1 i_2 171867 M 28649.648860 0.166697 2.61
步骤8: 数据存储 - 保存你的分析结果
分析处理完成后,通常需要将最终的干净、完整的数据集保存到硬盘,以备后续使用。
我们将把合并后的 df_merged 保存为一个新的CSV文件。
保存工具: .to_csv()
.to_csv() 方法可以将 DataFrame 写入一个CSV文件。
最佳实践 : 设置 index=False,避免将Pandas的行索引写入文件,防止下次读入时多出一列。
代码
# 保存表格数据版本,供后续读取示例复用。
df_merged.to_csv('data/merged.csv' , index= False )
# 报告合并结果已经写入 CSV,便于核对输出位置。
print ('文件 "merged.csv" 已成功保存。' )
实战演练:数据可视化与探索
数字是抽象的,而图形是直观的。数据可视化是理解数据、发现模式、沟通见解的最有效方式。
工具 : 我们将主要使用 Seaborn,并用 Matplotlib 进行一些定制。
数据 : 我们将使用公开 stock/financial_statement.h5(key=financial_data)中的中国上市公司年度财报样本。
步骤0: 准备可视化环境和数据
字段 :公司、季度、披露日、总资产、营业收入与归母净利润。
样本 :2018—2024 年 Q4;每个公司—财年保留最后披露版本。
任务边界 :这是披露后探索,不是预测任务。
信息边界 :任何特征只有在披露日之后才可使用。
代码
# 为“步骤0: 准备可视化环境和数据”导入 `seaborn` 并绑定 `sns`,用于编码当前任务的统计分布或分组关系。
import seaborn as sns
# 为“步骤0: 准备可视化环境和数据”导入 `matplotlib.pyplot` 并绑定 `plt`,用于构建当前任务的坐标轴并呈现比较结果。
import matplotlib.pyplot as plt
# 导入 Matplotlib 字体管理器,用于在绘图前检查项目指定的中文字体确已安装。
from matplotlib import font_manager
# 设置一个美观的绘图风格
sns.set_theme(style= "whitegrid" )
# 固定项目的已安装衬线字体栈,使思源宋体优先渲染中文标题与坐标轴。
project_serif_font_families = ["Source Han Serif SC" , "DejaVu Serif" ]
# 禁用默认回退并定位思源宋体文件,避免只声明一个未安装的字体名。
source_han_serif_path = font_manager.findfont(project_serif_font_families[0 ], fallback_to_default= False )
# 从字体文件反查实际家族名,作为渲染环境的字体解析证据。
resolved_source_han_family = font_manager.FontProperties(fname= source_han_serif_path).get_name()
# 在字体解析被替换时立即失败,防止中文标签以缺字方框静默输出。
assert resolved_source_han_family == "Source Han Serif SC" , f"中文字体解析异常: { resolved_source_han_family} "
# 将经实测可用的思源宋体置于字体栈首位,并保留已安装的西文衬线回退。
plt.rcParams["font.family" ] = project_serif_font_families
# 保留 Unicode 减号 U+2212,使负值刻度与中文标签在同一字体说明下正确呈现。
plt.rcParams["axes.unicode_minus" ] = True
# 优先使用 Linux 或 Windows 的本地数据,读者环境再使用章节缓存或公开下载。
from pathlib import Path
from urllib.request import urlretrieve # 复用本章隐藏设置单元安装的浏览器标识下载器
# 按本地共享数据、项目缓存的顺序选择已存在文件,全部缺失时才准备下载。
finance_path = next ((candidate_path for candidate_path in [Path('/home/ubuntu/r2_data_mount/data/stock/financial_statement.h5' ), Path('C:/qiufei/data/stock/financial_statement.h5' ), Path('data/course/financial_statement.h5' )] if candidate_path.exists()), Path('data/course/financial_statement.h5' ))
if not finance_path.exists():
finance_path.parent.mkdir(parents= True , exist_ok= True )
urlretrieve('https://assets.qiufei.site/data/stock/financial_statement.h5' , finance_path)
# 确定年度 Q4 行条件,使大型 HDF5 在读取前先完成行筛选。
finance_quarters = [f' { year} q4' for year in range (2018 , 2025 )]
# 只读取探索图所需的公司、时点与三项财务字段,避免全量载入宽表。
finance_columns = ['order_book_id' , 'quarter' , 'info_date' , 'total_assets' , 'operating_revenue' , 'net_profit_parent_company' ]
# 从表式 HDF5 键同时选择年度行与必要字段,保持数据读取与绘图逻辑分离。
finance_raw = pd.read_hdf(finance_path, key= 'financial_data' , where= 'quarter in finance_quarters' , columns= finance_columns)
# 统一披露日期类型,为公司—财年版本选择提供可复算时点。
finance_raw['info_date' ] = pd.to_datetime(finance_raw['info_date' ])
# 检查 HDF 行过滤确实只返回事先确定年度,阻止季度口径静默漂移。
assert finance_raw['quarter' ].isin(finance_quarters).all (), 'HDF 行筛选返回了事先确定范围外季度'
# 从季度字段提取财年,供版本去重和分期比较。
finance_raw['fiscal_year' ] = finance_raw['quarter' ].str [:4 ].astype(int )
# 对公司—财年保留最后披露版本,防止同一报告修订重复计数。
finance_sample = finance_raw.sort_values(['order_book_id' , 'fiscal_year' , 'info_date' ]).drop_duplicates(['order_book_id' , 'fiscal_year' ], keep= 'last' )
# 删除三项绘图指标缺失的记录,确保各图使用一致完整样本。
finance_sample = finance_sample.dropna(subset= ['total_assets' , 'operating_revenue' , 'net_profit_parent_company' ]).copy()
# 将大额人民币字段转换为亿元,提升坐标轴可读性。
finance_sample[['assets_yi' , 'revenue_yi' , 'net_profit_yi' ]] = finance_sample[['total_assets' , 'operating_revenue' , 'net_profit_parent_company' ]].div(1e8 ).to_numpy()
# 把财年分为三个连续阶段,用于箱形图比较而不暗示随机分组。
finance_sample['period_group' ] = pd.cut(finance_sample['fiscal_year' ], bins= [2017 , 2020 , 2022 , 2024 ], labels= ['2018—2020' , '2021—2022' , '2023—2024' ])
# 按归母净利润正负建立经营结果组,只作描述性比较。
finance_sample['profit_status' ] = np.where(finance_sample['net_profit_yi' ].ge(0 ), '盈利' , '亏损' )
# 显示公开财务样本前五行,核对公司、财年、披露日与亿元口径。
finance_sample[['order_book_id' , 'fiscal_year' , 'info_date' , 'assets_yi' , 'revenue_yi' , 'net_profit_yi' ]].rename(columns= {
'order_book_id' : '证券代码' , 'fiscal_year' : '财年' , 'info_date' : '披露日' ,
'assets_yi' : '总资产(亿元)' , 'revenue_yi' : '营业收入(亿元)' , 'net_profit_yi' : '归母净利润(亿元)'
}).head()
3
000001.XSHE
2018
2021-02-02
34185.92
1167.16
248.18
3
000001.XSHE
2019
2022-03-10
39390.70
1379.58
281.95
3
000001.XSHE
2020
2023-03-09
44685.14
1535.42
289.28
3
000001.XSHE
2021
2024-03-15
49213.80
1693.83
363.36
3
000001.XSHE
2022
2025-03-15
53215.14
1798.95
455.16
可视化任务1: 理解单个变量的分布
研究问题 : 中国上市公司年度归母净利润(亿元)的分布怎样?是否偏态并存在极端观测?
适用图表 : 直方图 (Histogram)
直方图:观察数据分布的形状
直方图通过将数据分组成等宽的“箱子”(bins),并统计落在每个箱子内的数据点数量,来展示数据的分布情况。
代码
plt.figure(figsize= (9 , 5 )) # 设置画布大小
sns.histplot(data= finance_sample, x= 'net_profit_yi' , kde= True , color= 'dodgerblue' ) # 展示公开公司财年利润分布
# 将图题设为描述性问题,避免在执行前硬编码分布结论。
plt.title('年度归母净利润的分布与尾部观测' )
# 将横轴标为年度归母净利润及亿元单位。
plt.xlabel('归母净利润(亿元)' )
# 为“直方图:观察数据分布的形状”,将纵轴标为“频数”,明确纵向编码的变量。
plt.ylabel('频数' )
# 显示利润直方图与核密度曲线,检查集中区间、偏态和极端公司财年。
plt.show()
可视化任务2: 比较不同组别的分布
研究问题 : 2018—2020、2021—2022 与 2023—2024 三个阶段的归母净利润分布有何不同?
适用图表 : 箱形图 (Box Plot)
箱形图:简洁地展示五数概括
箱形图(又称盒须图)用一种简洁的方式展示了数据的关键统计特征,对于比较多个组的分布和识别异常值 尤其有效。
Anatomy of a Box Plot
箱线图结构标出最小值、第一四分位数、中位数、第三四分位数、最大值与离群点。
异常值 (Outlier)
最大值 (Maximum)
第三四分位数 (Q3)
中位数 (Median)
第一四分位数 (Q1)
最小值 (Minimum)
绘制箱形图:使用 sns.boxplot()
代码
# 为“绘制箱形图:使用 `sns.boxplot()`”建立投影画布,给坐标、图例与标注预留空间。
plt.figure(figsize= (9 , 5 ))
# 用 `sns.boxplot` 绘制分组四分位数、须与离群点。
sns.boxplot(data= finance_sample, x= 'period_group' , y= 'net_profit_yi' , palette= 'pastel' , order= ['2018—2020' , '2021—2022' , '2023—2024' ])
# 用描述性图题提示比较中位数、离散度和离群点。
plt.title('三个财年阶段的利润分布比较' )
# 将横轴标为财年阶段,明确分组的时间顺序。
plt.xlabel('财年阶段' )
# 将纵轴标为归母净利润及亿元单位。
plt.ylabel('归母净利润(亿元)' )
# 显示按时间阶段分组的利润箱形图,比较中位数、四分位距与离群点。
plt.show()
解读 : 箱体中线、四分位距与离群点分别回答典型水平、离散程度与尾部公司财年;阶段差异是描述性关联,不能归因于某项政策或冲击。
可视化任务3: 比较不同类别的平均值
研究问题 : 当前样本中盈利与亏损公司财年的平均营业收入有差异吗?
适用图表 : 条形图 (Bar Plot)
绘制条形图:使用 sns.barplot()
Seaborn的条形图默认计算的是每个类别的均值 ,并用误差棒 (error bars)表示该均值的95%置信区间。
代码
# 为“绘制条形图:使用 `sns.barplot()`”建立投影画布,给坐标、图例与标注预留空间。
plt.figure(figsize= (9 , 3.0 ))
# 按盈利状态分组,以营业收入为柱高,比较中国上市公司财年的组均值。
sns.barplot(data= finance_sample, x= 'profit_status' , y= 'revenue_yi' , palette= 'coolwarm' )
# 用描述性图题提示读取组均值与误差线,不硬编码执行结果。
plt.title('盈利状态与平均营业收入的样本比较' , fontsize= 18 )
# 将横轴标为盈利状态,明确横向分组变量。
plt.xlabel('公司财年盈利状态' , fontsize= 16 )
# 将纵轴标为平均营业收入及亿元单位。
plt.ylabel('平均营业收入(亿元)' , fontsize= 16 )
plt.xticks(fontsize= 16 ) # 保持分组标签达到投影可读字号
plt.yticks(fontsize= 16 ) # 保持数值刻度达到投影可读字号
# 显示盈利与亏损组平均营业收入及误差线,检查组间差异与不确定性。
plt.show()
解读 : 柱高是当前样本组均值;误差棒是否重叠不能替代预先定义的统计检验。若要推断,应报告均值差、区间、公司内依赖与抽样边界,并避免因果措辞。
可视化任务4: 探索两个连续变量的关系
研究问题 : 营业收入与归母净利润之间存在怎样的样本内关系?
适用图表 : 散点图 (Scatter Plot)
绘制散点图:使用 sns.scatterplot()
散点图在二维平面上用点来表示两个数值变量的值,用于揭示变量间的相关性。
代码
# 为“绘制散点图:使用 `sns.scatterplot()`”建立投影画布,给坐标、图例与标注预留空间。
plt.figure(figsize= (9 , 5 ))
# 以营业收入为横轴、归母净利润为纵轴绘制公司财年散点。
sns.scatterplot(data= finance_sample, x= 'revenue_yi' , y= 'net_profit_yi' , alpha= 0.6 )
# 用描述性图题引导观察方向与离散程度,不先写死相关结论。
plt.title('营业收入与归母净利润的样本内关系' )
# 将横轴标为营业收入及亿元单位。
plt.xlabel('营业收入(亿元)' )
# 将纵轴标为归母净利润及亿元单位。
plt.ylabel('归母净利润(亿元)' )
# 显示收入与利润散点关系,检查样本内方向、离散程度与极端观测。
plt.show()
散点图解读边界
解读 : 点云只描述当前披露样本的关联方向、非线性与异方差;公司规模、行业和重复公司观测都会影响图形,相关不等于因果。
可视化任务5: 量化多个变量间的关系
研究问题 : 总资产、营业收入与归母净利润三个规模指标的样本相关性是多少?
适用图表 : 热图 (Heatmap)
绘制热图:先算相关性,再用 sns.heatmap()
这个过程分两步:
使用 Pandas 的 .corr() 方法计算相关系数矩阵。
将这个矩阵传入 sns.heatmap() 进行可视化。
代码
# 步骤1: 选取数值列并计算相关系数矩阵
corr = finance_sample[['assets_yi' , 'revenue_yi' , 'net_profit_yi' ]].corr() # 计算公开财务规模指标的样本相关矩阵
# 步骤2: 绘制热图
plt.figure(figsize= (8.5 , 3.4 ))
# 把 `corr` 编码为热图色块,比较“核心数值变量的相关性热图”中的成对关系。
sns.heatmap(corr, annot= True , cmap= 'vlag' , center= 0 , fmt= '.2f' , annot_kws= {'size' : 16 })
# annot=True 在格子上显示数值
# cmap='vlag' 使用一个在0点发散的色板
# fmt='.2f' 格式化数值为两位小数
plt.title('资产、收入与利润的样本相关矩阵' , fontsize= 18 )
plt.xticks(fontsize= 16 ) # 保持横轴财务字段标签投影可读
plt.yticks(fontsize= 16 ) # 保持纵轴财务字段标签投影可读
# 显示公开财务字段相关系数热图,核对线性关联方向和强度。
plt.show()
热图解读
热图数值由当前公开数据版本实时计算;同公司跨年依赖与共同规模因子会影响相关系数,相关不等于因果,也不等于样本外预测价值。
阶段小结
今天,我们从理论到实践,学习了数据管理与探索的全过程:
数据存储
理解了内存与硬盘的权衡,以及CSV、Parquet等格式的选择。
数据管理 (Pandas)
掌握了数据读写、查询、筛选、聚合、合并等一系列核心操作。
数据探索 (Seaborn)
学会了使用多种图表来发现数据背后的故事和模式。
这些技能是进行任何严肃的金融数据分析的基石 。
检查:输入说明能否复现?
给出文件、key、字段、单位、频率、样本期与可得日;缺任何一项时,说明为何不能进入模型。独立作答 2 分钟。
反馈:七项输入说明缺一不可
对象 :文件与 key 确定读取哪个逻辑数据表。
含义 :字段与单位固定变量定义和量纲。
范围 :频率与样本期固定观察粒度和覆盖区间。
时间边界 :可得日用于排除未来信息。
缺任一项都不能复现同一输入或排除前瞻偏差。答漏可得日者回看 相关内容 ,修正七项说明并重新提交;七项齐全者进入 相关内容 。
动手实践:课堂练习
使用本章生成的 income_stock.csv 与 gpa.csv,独立完成并提交八项结果:
两表字段与主键检查;
按 id 合并与行数检查;
女性子样本描述统计;
女性子样本 Parquet 导出与回读检查;
按性别分组均值;
收入—投资散点图;
性别分组柱状图;
三变量相关矩阵与热图。
查看顺序 :先提交八项结果,再开放答案区。
练习:先完成再查看答案:先交作业结果
提交代码、8 项输出和一句失败诊断;缺主键唯一性检查或行数检查者先修正再进入反馈。自查标准为输出可复现、合并不增删用户、统计口径与题意一致。
练习1-2:数据读入与合并
读入数据 : 读入 income_stock.csv 数据。
合并数据 : 将其与 gpa.csv 数据以用户 id 为键进行合并。
练习1-2参考答案
代码
# 读取收入与投资练习表,恢复用户主表的字段和行顺序。
df_income = pd.read_csv('data/income_stock.csv' )
# 读取 GPA 练习表,随后按用户主键与收入表合并。
df_gpa = pd.read_csv('data/gpa.csv' )
# 统计收入表重复主键,作为一对一连接前的硬门禁。
income_duplicate_keys = int (df_income['id' ].duplicated().sum ())
# 统计 GPA 表重复主键,避免多对多连接静默放大样本。
gpa_duplicate_keys = int (df_gpa['id' ].duplicated().sum ())
# 用断言阻止任一输入表主键不唯一时继续合并。
assert income_duplicate_keys == 0 and gpa_duplicate_keys == 0 , '重复 id:返回输入表去重并核对业务主键'
# 按唯一 `id` 执行一对一连接,使错误连接基数立即失败。
df_full = pd.merge(df_income, df_gpa, on= 'id' , how= 'inner' , validate= 'one_to_one' )
# 统计收入表中无法匹配 GPA 的用户,防止内连接静默丢行。
income_unmatched_rows = int ((~ df_income['id' ].isin(df_gpa['id' ])).sum ())
# 统计 GPA 表中无法匹配收入记录的用户,完整检查左右未匹配。
gpa_unmatched_rows = int ((~ df_gpa['id' ].isin(df_income['id' ])).sum ())
# 汇总两表键唯一性、连接前后行数与左右未匹配证据。
merge_audit = pd.DataFrame([{'收入表行数' : len (df_income), 'GPA表行数' : len (df_gpa), '收入表重复键' : income_duplicate_keys, 'GPA表重复键' : gpa_duplicate_keys, '左未匹配' : income_unmatched_rows, '右未匹配' : gpa_unmatched_rows, '合并后行数' : len (df_full)}])
# 展示连接检查表,使第 1—2 项作业结果可以直接自查。
display(merge_audit)
# 强制本题的一对一完整匹配要求,行数异常时停止后续统计。
assert income_unmatched_rows == 0 and gpa_unmatched_rows == 0 and len (df_full) == len (df_income) == len (df_gpa), '合并行数或未匹配异常:返回检查 id 口径'
# 展示合并后前五行,核对字段和值未因连接发生错位。
display(df_full.head())
0
i_1
146958
F
51658.712279
2.37
1
i_2
171867
M
28649.648860
2.61
2
i_3
156932
M
218551.394257
3.05
3
i_4
390838
F
156262.027760
2.86
4
i_5
284178
M
181397.055782
2.58
练习3-4:数据筛选与汇总
选取子集 : 选取所有女性用户 的数据,并存入一个新的数据帧。
数据总结 : 对这个新的女性用户数据帧进行总结,计算 income, stock, 和 gpa 的描述性统计信息。
练习3-4参考答案
代码
# 按性别字段筛选女性用户,形成练习描述统计与导出的目标子样本。
df_female = df_full[df_full['gender' ] == 'F' ].copy() # 使用 .copy() 避免 SettingWithCopyWarning
# 投影只展示六项核心统计量;完整 describe 可在折叠代码中复算。
female_summary = df_female[['income' , 'stock' , 'gpa' ]].describe().loc[['count' , 'mean' , 'std' , 'min' , '50%' , 'max' ]]
# 用紧凑表核对筛选后的样本量、中心、离散度与范围。
display(female_summary.round (2 ))
count
5.00
5.00
5.00
mean
199090.60
100656.36
2.75
std
120183.24
79680.45
0.32
min
79886.00
19837.36
2.37
50%
146958.00
64899.69
2.73
max
390838.00
210624.02
3.22
练习5:数据存储为Parquet格式
存为Parquet : 将上一步创建的女性用户数据帧 df_female 存为一个 female_data.parquet 文件。
练习5参考答案
代码
# 将女性样本写入指定 Parquet 文件,保留列类型供后续回读检查。
df_female.to_parquet('data/female_data.parquet' )
# 重新读取刚写出的文件,验证行数、列名与女性子样本完全一致。
female_roundtrip = pd.read_parquet('data/female_data.parquet' )
# 用断言阻止“文件写出但内容或模式变化”被误报为完成。
assert female_roundtrip.shape == df_female.shape and female_roundtrip.columns.equals(df_female.columns)
练习6:重新读入并可视化关系
重新读入 : 重新读入原始的 income_stock.csv 数据。
散点图 : 使用散点图来可视化 income 与 stock 之间的关系。
练习6参考答案
代码
df_income_reload = pd.read_csv('data/income_stock.csv' ) # 重新读取练习保存的收入与投资记录,核对持久化结果
# 为“练习6参考答案”建立投影画布,给坐标、图例与标注预留空间。
plt.figure(figsize= (9 , 5 ))
# 以 `'income'` 为横轴、`'stock'` 为纵轴绘制散点,数据来自 `df_income_reload`,展示“收入与股票投资关系”。
sns.scatterplot(data= df_income_reload, x= 'income' , y= 'stock' )
# 将图题设为“收入与股票投资额关系”,直接说明当前图形的比较目的。
plt.title('收入与股票投资额关系' )
# 将横轴标为“收入”,明确横向编码的变量。
plt.xlabel('收入' )
# 将纵轴标为“股票投资额”,明确纵向编码的变量。
plt.ylabel('股票投资额' )
# 显示收入与股票投资额散点图,检查两者的方向、离散程度和高收入样本位置。
plt.show()
练习7:可视化性别差异
条形图 : 使用条形图来可视化和比较男性与女性在平均投资总额 (stock) 上的区别。
练习7参考答案
代码
# 按性别显式计算样本数与股票投资均值,提供可复算的第 5 项数值产出。
gender_stock_means = df_full.groupby('gender' , as_index= False ).agg(样本数= ('stock' , 'size' ), 平均股票投资额= ('stock' , 'mean' ))
# 展示分组均值表,使柱高可以回查到精确数值与分母。
display(gender_stock_means.round (2 ))
0
F
5
100656.36
1
M
5
145851.16
练习7参考答案:柱图
代码
plt.figure(figsize= (8 , 5 )) # 创建适合比较两组均值的紧凑画布
# 以 `'gender'` 分组、`'stock'` 为柱高(数据 `df_full`),比较“男女平均股票投资额对比”。
sns.barplot(data= df_full, x= 'gender' , y= 'stock' , palette= ['lightblue' , 'salmon' ])
# 将图题设为“男性与女性的平均股票投资额对比”,直接说明当前图形的比较目的。
plt.title('男性与女性的平均股票投资额对比' )
# 将横轴标为“性别”,明确横向编码的变量。
plt.xlabel('性别' )
# 将纵轴标为“平均股票投资额”,明确纵向编码的变量。
plt.ylabel('平均股票投资额' )
# 显示按性别分组的平均股票投资额及误差线,比较两组均值与不确定性。
plt.show()
练习8:可视化多变量相关性
热图 : 使用热图来可视化 income, stock, 以及 gpa 三个变量之间的相关性。
练习8参考答案:相关矩阵与热图
代码
# 首先计算相关性矩阵
corr_matrix_ex = df_full[['income' , 'stock' , 'gpa' ]].corr()
# 然后绘制热图
plt.figure(figsize= (8 , 5 ))
# 把 `corr_matrix_ex` 编码为热图色块,比较“收入、投资与GPA的相关性”中的成对关系。
sns.heatmap(corr_matrix_ex, annot= True , cmap= 'coolwarm' , center= 0 , fmt= '.2f' )
# 将图题设为“收入、股票投资与GPA的相关性热图”,直接说明当前图形的比较目的。
plt.title('收入、股票投资与GPA的相关性热图' )
# 显示收入、股票投资额与 GPA 的相关系数矩阵,核对三组两两线性关联。
plt.show()
八项作业结果自查
数据结构
展示字段与两表主键;重复键为 0
合并完整性
一对一合并前后行数一致;左右未匹配均为 0
子样本统计
女性描述统计包含样本数、中心与离散度
文件回读
Parquet 回读后的形状与列一致
分组汇总
分组均值表同时给出各组分母与均值
两幅比较图
散点图字段、轴名正确;柱图与分组均值同源
相关分析
相关矩阵与热图同源
通过条件 :八项全部满足。若出现重复键、未匹配或合并行数变化,返回 相关内容 修复;若图表与数值表不一致,返回相应答案页复算。
公开中国数据练习:上市公司覆盖
代码
from pathlib import Path # 管理公开公司基础信息路径
from urllib.request import urlretrieve # 复用本章已安装的浏览器标识下载器
import pandas as pd # 读取并汇总中国上市公司数据版本
# 按本地共享数据、项目缓存的顺序选择公司基础信息文件。
basic_path = next ((candidate_path for candidate_path in [Path('/home/ubuntu/r2_data_mount/data/stock/stock_basic_data.h5' ), Path('C:/qiufei/data/stock/stock_basic_data.h5' ), Path('data/course/stock_basic_data.h5' )] if candidate_path.exists()), Path('data/course/stock_basic_data.h5' ))
if not basic_path.exists():
basic_path.parent.mkdir(parents= True , exist_ok= True )
urlretrieve('https://assets.qiufei.site/data/stock/stock_basic_data.h5' , basic_path)
basic_columns = ['order_book_id' , 'symbol' , 'province' , 'industry_name' , 'status' ] # 限定覆盖检查所需字段
basic_companies = pd.read_hdf(basic_path, key= 'stock_basic_info' , columns= basic_columns) # 选择性读取小型基础信息表
yangtze_provinces = ['上海市' , '江苏省' , '浙江省' , '安徽省' ] # 确定长三角地区口径
yangtze_companies = basic_companies.query('province in @yangtze_provinces and status == "Active"' ).copy() # 筛选当前上市的长三角公司
coverage_table = yangtze_companies.groupby('province' ).agg(公司数= ('order_book_id' , 'nunique' ), 行业数= ('industry_name' , 'nunique' )).reset_index() # 汇总地区覆盖
print ({'文件' : basic_path.name, '键' : 'stock_basic_info' , '全表行数' : len (basic_companies), '长三角公司数' : len (yangtze_companies)}) # 输出数据来源与样本数量变化
display(coverage_table) # 展示真实中国公司覆盖而非生成案例
{'文件': 'stock_basic_data.h5', '键': 'stock_basic_info', '全表行数': 5524, '长三角公司数': 1974}
本章小结
能提交含文件、key、字段、单位、频率、样本期与可得日的数据字典。
选择存储格式取决于访问模式、类型与规模,不由“流行”决定。
本章图形只描述样本,不建立因果或交易结论。
下一章学习向量、矩阵与统计量,为后续模型公式建立可复算基础。