尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从NumPy到Pandas再到量化项目:一条完整的数据分析学习路径

从NumPy到Pandas再到量化项目:一条完整的数据分析学习路径 学数据分析时Pandas 是绕不开的库也是最容易让人半途而废的库。第一个原因很实际不少教程把 NumPy、Pandas、Matplotlib 拆成独立章节每个章节又单独讲 API读者学完 NumPy 的 ndarray 后并不知道它和 DataFrame 是什么关系等进入 Pandas又要消化 Series、Index、groupby、merge 一堆概念于是很容易卡在“每个函数都见过组合起来不会用”的状态。第二个原因是学习目标太分散缺少一个能让知识串起来的项目。这篇文章按“NumPy 基础 - Pandas 数据操作 - 数据清洗 - 小型量化项目”这条线把 Pandas 的学习过程拆开讲。目标不是罗列所有 API而是搭出一条从数组到二维表再到指标计算和项目验证的完整链路。“从 numpy 到量化项目”并不是一句口号而是数据分析学习中很实用的一条路径先有数组思维再理解表格再用表格完成一个可复盘的项目。文章后面会给出代码、排查方法和学习清单适合刚开始学 Pandas或者学了一半总觉得缺根的读者。1. 先理解 NumPy 数组Pandas 才不是堆 API1.1 为什么先学 NumPy数据结构决定思维方式Pandas 的 DataFrame 底层依赖 NumPy很多操作也沿用了“按整行整列批量计算”的向量化思路。如果跳过 NumPy直接背df.groupby().agg()遇到报错时很难判断是数据问题还是 API 用法问题。NumPy 的核心不是“比 Python 列表快一点”而是提供了多维数组ndarray。多维数组有三个关键特点数组中的元素类型通常一致便于底层连续存储。支持按轴切片和布尔索引不需要写多层 for 循环。支持广播机制可以自动补全形状让向量化计算自然发生。Pandas 的 Series 可以理解成“带标签的一维 ndarray”DataFrame 可以理解成“多个 Series 在列方向上拼起来”再额外增加行索引和列索引。因此 NumPy 里的切片、索引、形状、广播规则到 Pandas 里大多仍然成立只是多了一层标签处理。1.2 NumPy 最小入门创建、切片、布尔索引先演示最常见的创建方式import numpy as np arr np.array([1, 2, 3, 4, 5]) print(arr.shape) # (5,) print(arr.dtype) # int64 arr2 np.arange(12).reshape(3, 4) print(arr2)输出结果是[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]]reshape(3, 4)把长度为 12 的一维数据改成 3 行 4 列。这个“形状”概念在整个 Pandas 里都会反复出现例如df.shape返回的就是行数和列数。切片和布尔索引是 pandas 中筛数据的底层基础print(arr2[1, 2]) # 第 1 行第 2 列结果是 6 print(arr2[0:2, 1:3]) # 前两行、第 1 到第 3 列 mask arr2 5 print(arr2[mask]) # 输出所有大于 5 的元素布尔索引在 Pandas 里表现为df[df[price] 100]思路完全一致先生成一个布尔数组再根据布尔数组选择满足条件的行。1.3 广播机制避免 for 循环的关键向量化的意思是“对整组数据做同一个操作”而不是用循环一个元素一个元素算。例如同时计算多只股票每天的收益率prices np.array([100.0, 101.5, 102.3, 101.0]) prev np.array([99.0, 100.0, 101.5, 102.3]) ret prices / prev - 1 print(ret)这里没有写for i in range(len(prices))程序照样完成整组计算。原因是 NumPy 会对两个形状一致的数组做逐元素运算这就是向量化。广播机制更进一步当两个数组形状不一致但兼容时NumPy 会自动扩展维度。matrix np.arange(12).reshape(3, 4) row np.array([1, 2, 3, 4]) print(matrix row)matrix是 3 行 4 列row是长度为 4 的一维数组NumPy 会把row横向复制成 3 行再做加法。这个机制在 Pandas 里也常用例如“对每一列都减去平均值”本质就是按轴广播。1.4 常见坑别把数组形状和标量混在一起第一个坑是误以为ndarray和 Python 列表完全等价。列表支持做拼接数组的是逐元素相加结果容易让人意外。list1 [1, 2, 3] arr np.array(list1) print(list1 list1) # [1, 2, 3, 1, 2, 3] print(arr arr) # [2 4 6]第二个坑是广播失效时没有认真看形状。比如一个形状为(3, 4)的数组和一个长度为 3 的数组相加会抛ValueError而不是自动转置。解决办法是先用reshape(3, 1)或np.newaxis调整维度。第三个坑是np.random使用全局随机状态。学习时为了结果可复现推荐显式创建随机数生成器rng np.random.default_rng(42) data rng.normal(0, 1, 100)这在量化项目中将直接决定你能否稳定复现一份回测结果。2. Pandas 两大核心Series 与 DataFrame先建立数据结构直觉2.1 Series 是带索引的一维数组Series 由两个部分组成数据值和索引。索引不一定是 0,1,2,3也可以是日期、字符串或任意标签。import pandas as pd s pd.Series([10, 20, 30], index[a, b, c]) print(s) print(s[b]) print(s[s 15])这里的s[s 15]和 NumPy 布尔索引是同一个套路。Series 对数据分析的意义在于当你对 Series 做计算时索引会自动对齐。这是 Pandas 和纯 NumPy 数组最大的区别之一也是后续处理不同来源数据时的关键能力。2.2 DataFrame 是由多个 Series 在列方向拼成的二维表DataFrame 可以理解成 Excel 表格行是样本列是字段。每一列本质都是一个 Series并且共享同一个行索引。最直接的习惯是先用字典创建 DataFramedata { date: [2024-01-01, 2024-01-02, 2024-01-03], symbol: [HS300, HS300, HS300], close: [3500.1, 3520.2, 3480.0], volume: [1200000, 1350000, 1150000] } df pd.DataFrame(data) print(df) print(df.dtypes)注意上面的date列在创建后是字符串类型。如果后续要按日期排序、按月份聚合必须先转成datetime64否则会出现“看起来是日期但排序结果完全不对”的问题。创建 DataFrame 的常见方式有很多除了字典之外还有从 NumPy 数组创建、从列表创建、从外部文件读取等。从外部文件读取时经常用到这几类df pd.read_csv(data.csv) df pd.read_excel(data.xlsx) df pd.read_parquet(data.parquet)如果要处理很大的数据Parquet 和 Feather 格式比 CSV 更节省磁盘空间读取速度也更快。它们依赖 PyArrow安装pyarrow后即可使用。2.3 索引方式loc、iloc、布尔条件很多 Pandas 初学者会在df[close]、df.loc[:, close]、df.iloc[:, 2]之间犹豫。这三者本质不同df[close]按列名取一列返回 Series。如果列名正好和 DataFrame 的方法名冲突会引发歧义。df.loc[行标签, 列标签]基于标签索引取值时会包含结束位置。df.iloc[行位置, 列位置]基于整数位置索引类似 NumPy 切片。print(df.loc[1, close]) # 取出行索引为 1 的 close 值 print(df.iloc[1, 2]) # 取出第 1 行第 2 列的值 print(df.loc[df[volume] 1000000]) # 布尔条件筛选布尔筛选和loc组合是数据分析中最常用的筛选方式。不要把df[df[volume] 1000000]和df[volume] 1000000搞混后者返回的是布尔 Series前者才返回筛选后的 DataFrame。2.4 数据类型转换astype 与 to_datetime热搜词里经常看到“pandas 数据类型转换”因为数据清洗中类型不匹配是最常见的坑之一。df[close] df[close].astype(float) df[date] pd.to_datetime(df[date]) df[volume] pd.to_numeric(df[volume], errorscoerce)errorscoerce表示转换失败时置为NaN而不是直接抛错。这在处理脏数据时很有用但也要注意置为NaN后需要额外处理缺失值否则后续聚合结果会受影响。字符串列中如果混杂数字和符号优先用pd.to_numeric而不是astype(float)因为前者能提供更优雅的容错方式。日期列统一用pd.to_datetime它还能解析2024/01/01、20240101等不同格式。2.5 数据分析学习中的一个必要检查点拿到任何 DataFrame第一件事不是直接调用业务方法而是先回答四个问题有多少行、多少列每列是什么数据类型有没有缺失值索引是否唯一、是否有序对应代码是print(df.shape) print(df.dtypes) print(df.isna().sum()) print(df.index.is_unique)把这些基础检查写成固定习惯后面做量化项目、商业数据分析、金融风控数据都会少踩很多坑。很多报错的根因并不是代码写错而是数据在入口处已经不符合预期。3. 数据清洗与聚合分析从 DataFrame 到分析结果3.1 数据清洗的第一轮去重、排序、补缺实际项目里拿到数据很少是已经整理好的“干净数据”。常见的清洗顺序是先看整体再处理重复值再处理缺失值最后按业务需求做排序和分析。重复值处理常遇到一个需求如果两列的值均相同则取第一条数据即可。这个需求在 Pandas 里是drop_duplicates的典型用法df pd.DataFrame({ date: [2024-01-01, 2024-01-01, 2024-01-02], symbol: [A, A, B], close: [10.0, 10.0, 12.0], volume: [100, 100, 200] }) df_clean df.drop_duplicates( subset[date, symbol], keepfirst ).copy() print(df_clean)subset指定用来判断重复的列。如果不写默认是整行全部相同才去重如果只希望“date 和 symbol 相同就算重复”必须把subset传进去。keepfirst表示保留排序后的第一条keeplast则保留最后一条。这里有一个容易被忽略的细节drop_duplicates默认不会修改原 DataFrame必须赋值给新变量或者使用inplaceTrue。推荐直接赋值因为inplace在链式操作下容易出现隐式副作用。3.2 缺失值处理isna、dropna、fillna判断缺失值最直接的方法是print(df.isna().sum())每列缺失数量出来后再决定是删除还是填充。删除通常用于缺失比例很高、或缺失列与业务无关的情况填充则更适合时间序列因为删除会让时间不连续。时间序列类数据比如股票价格最常使用前向填充df[close] df[close].fillna(methodffill)但这里要注意Pandas 2.x 已经不建议直接写methodffill推荐使用df[close].ffill()。版本升级后很多老教程代码会抛FutureWarning这是正常的不要 panic。另一个常见做法是按分组填充均值或中位数df[volume] df.groupby(symbol)[volume].transform(lambda x: x.fillna(x.median()))使用transform可以保持返回结果的索引与原始 DataFrame 一致便于后续直接赋值。3.3 分组聚合 groupby 与透视表分组聚合是 Pandas 最强大的能力之一。它的运行逻辑是三步拆分、应用、合并。daily pd.DataFrame({ date: [2024-01-01, 2024-01-01, 2024-01-02, 2024-01-02], symbol: [A, B, A, B], close: [10.0, 20.0, 10.5, 19.8], volume: [1000, 2000, 1100, 1900] }) summary daily.groupby(date).agg({ close: mean, volume: sum }).reset_index() print(summary)groupby(date)表示按日期分组agg可以同时对不同列使用不同聚合函数。最后用reset_index()把分组键从索引变成普通列方便后续合并或展示。透视表pivot_table解决的问题和groupby类似但更适合生成“行、列、值”的交叉表结构pivot pd.pivot_table( daily, indexdate, columnssymbol, valuesclose, aggfuncmean ) print(pivot)pivot 输出结果中A、B 两列会并排形成一张宽表。宽表适合看对比长表适合做统计聚合。很多入门教程没有强调这个区别导致读者在pivot_table和groupby之间反复切换却不知道何时用哪个。更简单的判断方法如果需要“每一行是一个日期、每一列是一个品种”的分类对比用pivot_table如果需要“按某个字段求汇总指标、并继续参与后续计算”用groupby().agg()。3.4 条件筛选、apply 与 transform 的边界条件筛选用来生成业务标记变量比如“成交量是否放大”df[volume_ratio] df[volume] / df[volume].rolling(20).mean() df[is_active] np.where(df[volume_ratio] 1.5, 1, 0)np.where是向量化的条件分支比apply(lambda ...)快得多。apply适合无法用向量化表达的逻辑但也要小心滥用因为它本质上是逐行调用 Python 函数数据量一大就会变慢。一个常见误区是“能用循环就不用 Pandas API”。实际上能写成df[a] * 2的操作就不应该写成df.apply(lambda x: x[a] * 2, axis1)。前者性能高一个数量级而且代码更短。3.5 总结一条可复用的清洗链路在实际项目中可以把数据清洗整理成固定流程每次拿到新数据后按顺序执行读取数据统一列名格式df.columns [c.lower() for c in df.columns]。查看shape、dtypes、isna().sum()。将日期列转为datetime数值列转为合适类型。按业务主键去重df.drop_duplicates(subset[...], keepfirst)。排序df.sort_values([date, symbol]).reset_index(dropTrue)。处理缺失值时间序列优先ffill。创建派生列比如收益率、移动平均、涨跌幅。在进入分析前保存一份清洗后的副本避免反复改原始数据。这条链路不只在量化项目里有效做 Excel 数据分析、Spark 数据分析、金融风控数据分析时思路也一样。区别只是数据规模变大后需要把 Pandas 换成 Spark 或 PyArrow 等工具但“先检查再清洗再聚合”的流程不会变。4. 用 Pandas 完成一个小型量化项目数据准备与指标计算4.1 项目需求拆解只做技术复盘不预测涨跌很多初学者看到“量化项目”就想到自动交易、赚钱、财务自由这是错误的学习心态。在教学项目里量化更合适的定位是“用数据和规则验证一个策略逻辑是否成立”。本项目只做四件事准备一份模拟的 OHLCV 数据。计算移动平均线、收益率、年化波动率。用双均线生成交易信号。比较策略净值与买入持有净值。需要说明这里的策略只是教学样例不构成投资建议。真实量化环境还要考虑交易成本、滑点、停牌、涨跌停、资金管理等大量因素。4.2 生成或读取 OHLCV 数据没有真实行情时可以用 NumPy 生成模拟数据。为了让结果稳定可复现显式指定随机种子import numpy as np import pandas as pd rng np.random.default_rng(42) n 500 dates pd.date_range(2023-01-01, periodsn, freqD) returns rng.normal(0.0005, 0.01, n) close 100 * np.cumprod(1 returns) df pd.DataFrame({ date: dates, close: close }) print(df.head())np.cumprod(1 returns)模拟了净值曲线的累计效果。这里的核心是理解价格序列可以看成“每日收益率连乘”的结果后续策略净值也会用同样的思路计算。如果已有 CSV 数据可以直接用pd.read_csv读取。要注意把日期列解析为时间类型并排序。df pd.read_csv(market_data.csv, parse_dates[date]) df.sort_values(date, inplaceTrue)4.3 计算收益率、移动平均和波动率量化分析中大部分指标都依赖三个基础算子差值、滚动窗口和分组聚合。df[ret] df[close].pct_change() df[ma5] df[close].rolling(5).mean() df[ma20] df[close].rolling(20).mean() df[volatility] df[ret].rolling(20).std() * np.sqrt(252)pct_change()计算当前值相对上一日的变化比例结果是每日收益率。rolling(5).mean()是最近 5 日收盘价的移动平均。rolling(20).std()是 20 日收益率标准差乘以sqrt(252)是为了换算成年化波动率因为 A 股一年通常按 252 个交易日估算。这几行代码充分体现了 Pandas 和 NumPy 的关系ret是 NumPy 数组批量计算的延伸rolling是 Pandas 新增的窗口能力。4.4 用向量化方式生成交易信号双均线策略的思路是当短期均线ma5从下方穿过长期均线ma20时产生买入信号当ma5从上方穿过ma20时产生卖出信号。先定义一个signal列1 表示持仓0 表示空仓df[signal] 0 df.loc[df[ma5] df[ma20], signal] 1 df[position] df[signal].diff().fillna(0)这里的signal是“状态”position是“状态变化”。position 1表示从空仓变为持仓position -1表示从持仓变为空仓。之所以先计算状态再取差分而不是直接判断“某天是否上穿”是因为状态变化需要在连续的时间序列中检测逐行 if 容易出错且性能差。4.5 简单回测与结果验证策略收益率的计算方式是用前一天的持仓状态乘以当天的收益率。原因是在实际操作中每天收盘后才能确认信号次日才能执行。df[strategy_ret] df[position].shift(1) * df[ret] df[strategy_nav] (1 df[strategy_ret]).cumprod() df[buy_hold_nav] (1 df[ret]).cumprod()这里shift(1)是关键它把持仓状态向后平移一天避免“当天信号当天交易”的未来函数。未来函数是量化回测中最隐蔽的错误很多策略回测曲线非常漂亮实盘却失效原因之一就在这里。最后查看尾部结果print(df[[date, close, ma5, ma20, signal, strategy_nav, buy_hold_nav]].tail())运行后会有 500 行数据。对比strategy_nav和buy_hold_nav能看出双均线策略在模拟数据上是否优于持有不动。不过这只是单次随机样本不能说明策略本身好坏。更严谨的方式是多次模拟或者使用历史真实数据。这个项目虽然小但已经覆盖了 NumPy、Pandas、时间序列、滚动窗口、信号生成和回测验证。完整教程里通常会把这一节拆成多集每集花时间讲一个函数并把每一步的中间结果打印出来。建议学习时也这样做每计算一个指标立即print(df.head())或df[[date, column]].tail()确认中间结果符合预期。5. 环境准备与版本适配学习、测试、生产三种环境的不同选择5.1 本地学习环境怎么装 pandas 和 numpy最直接的方式是使用 pip 安装pip install numpy pandas如果需要画图再加上pip install matplotlib如果使用 Anaconda也可以使用 condaconda install numpy pandas安装完成后在 Python 交互环境里验证版本import numpy as np import pandas as pd print(np.__version__) print(pd.__version__)这一步非常重要。后续教程中的代码如果是基于旧版本写的某些 API 行为可能已经变化。例如 Pandas 2.x 对字符串类型默认使用str类型和旧版的object类型存在差异虽然多数操作兼容但dtype输出看起来会不一样。5.2 PyCharm 中安装 pandas 包的常见方式在 PyCharm 中打开File - Settings - Project - Python Interpreter点击右上角搜索pandas和numpy选中后点击Install Package即可。这里容易踩坑的是选择了错误的 Python 解释器。PyCharm 默认可能使用全局 Python而不是当前项目创建的虚拟环境。安装包后项目里仍然报ModuleNotFoundError: No module named pandas通常就是因为解释器没对。解决办法是先确认右下角或Settings里当前使用的解释器路径然后打开终端执行pip list检查pandas是否在当前解释器的包列表中。如果不在就在同一解释器环境下重新pip install pandas。5.3 Python 3.10 与 pandas 版本适配怎么确认不少学习者会遇到“Python 3.10 到底装哪个 pandas 版本”的问题。这个问题没有固定答案因为 pandas 会随新版本持续提供对最新 Python 版本的轮子支持。稳妥的做法是先确认自己的 Python 版本python --version。安装 pandas 时不指定版本让 pip 自动选择当前 Python 可用的最新版本pip install pandas。如果项目需要固定版本再根据官方文档或pip index versions pandas查询可用版本。遇到“当前 Python 版本不支持该 pandas 版本”时优先升级 pandas 到最新版本而不是降级 pandas 去迁就旧环境。从经验上看Python 3.10 配合 pandas 1.4、1.5、2.x 这几种常见组合都能正常使用。但“能用”和“生产级稳定”是两回事。生产项目建议把 Python 版本、pandas 版本、numpy 版本都写进requirements.txt或pyproject.toml并锁定版本范围。numpy1.23,2.0 pandas1.5,2.2如果原始材料没有给出明确版本落地前先确认依赖版本这是工程习惯而不是多此一举。5.4 生产项目还要补齐哪些能力学习环境能跑通代码生产环境还需要考虑配置外置化数据路径、参数、数据库连接不要写死在代码里。日志与监控记录每次数据读取、清洗、聚合的耗时和异常。数值稳定性大文件读取时观察内存使用必要时分块处理。数据版本管理清洗后的数据要保存快照方便回溯。错误处理捕捉读取文件、类型转换、聚合计算中的异常并记录上下文。例如读取大文件时可以先看文件大小再用pd.read_csv(..., nrows10000)做小规模探测避免一次性加载把内存打满。6. 常见报错与排查链路看到 traceback 后从哪里开始查6.1 常见报错速查表报错信息常见原因处理建议KeyError: close列名不存在或列名大小写不匹配先打印df.columns.tolist()核对ValueError: cannot reindex索引不一致合并或赋值时对齐失败检查两个 DataFrame 的索引是否唯一且类型一致SettingWithCopyWarning对 DataFrame 切片后直接赋值使用.copy()创建副本或改为df.loc[...]FutureWarning: methodffillAPI 即将变更改为df.ffill()TypeError: unsupported operand数据类型不对字符串参与了数值计算先astype或pd.to_numericMemoryError数据量太大一次性加载内存不足分块读取、筛选列、使用 Parquet 列式格式6.2 KeyError先检查列名而不是代码很多新手看到KeyError第一反应是代码写错了实际上更常见的原因是数据文件列名和预期不一致。比如 CSV 里列名可能是Close而代码里写的是close。排查顺序print(df.columns.tolist()) print(df.dtypes)这两个输出能解决 80% 的KeyError。如果列名带空格或 BOM 字符可以提前做列名标准化df.columns df.columns.str.strip().str.lower()6.3 SettingWithCopyWarning链式赋值的隐患这是一个经典警告很多教程只是说“忽略它”这并不负责任。警告出现的典型场景sub df[df[volume] 1000] sub[flag] 1sub可能只是原始 DataFrame 的一个视图也可能是一份副本。Pandas 为了避免修改不确定性就会提示警告。最直接的解决方案是显式复制sub df[df[volume] 1000].copy() sub[flag] 1这样明确告诉 Pandas我要独立操作这份数据。生产代码里只要你不希望原数据被意外修改就在筛选、切片后加.copy()。6.4 从现象倒推根因的排查顺序一个系统性排查思路是按下面顺序走不要一上来就怀疑框架输入数据是否正确文件路径、编码、分隔符、列名。数据类型是否正确日期是不是datetime数值列是不是float/int。索引是否唯一且有序重复索引会导致loc返回多行。中间结果是否符合预期每步操作后打印shape、head、tail。依赖版本是否匹配在更新 pandas 后部分老代码行为会变化。日志是否出现明确异常优先看第一个 traceback不要看最后一行。例如回测结果全是NaN按这个顺序排查print(df.isna().sum()) # 缺失是否过多 print(df[ret].head()) # 收益率是否正常 print(df[position].shift(1).head()) # 持仓是否延迟6.5 用最小数据复现问题遇到复杂报错时最好的办法是摆脱真实数据用 3 到 5 行构造一个最小可复现样本。比如怀疑merge导致行数膨胀可以left pd.DataFrame({key: [a, a], value: [1, 2]}) right pd.DataFrame({key: [a], other: [10]}) merged left.merge(right, onkey) print(merged)接着对比实际业务数据与最小样本的结构差异。这个方法几乎适用于所有 Pandas 报错也适用于 NumPy 数组形状不匹配的问题。完整教程中排错能力比背 API 更重要因为真实项目里的错误组合远多于教程示例。7. 把 34 集教程拆成学习路径从 NumPy 基础到量化项目7.1 阶段一NumPy 基础与数组思维目标理解数组形状、切片、广播、随机数并养成“先确认 shape 再计算”的习惯。练习清单创建长度为 10 的随机数组输出奇数位置的元素。创建 5 行 4 列矩阵计算每列平均值。把两个形状不同的数组通过广播相加观察结果。用np.cumprod模拟净值曲线。验收标准不看文档能写出从收盘价序列计算每日收益率的代码。7.2 阶段二Pandas 数据操作目标掌握 Series、DataFrame 的创建、索引、切片、类型转换和文件读写。练习清单从字典创建 DataFrame并把日期列转为datetime。用loc和iloc分别取出某个具体值。把一个长表通过pivot_table转成宽表。用drop_duplicates对指定列去重只保留首次出现的数据。读取一个 CSV 文件输出缺失值统计。验收标准能快速说出loc、iloc、drop_duplicates的关键参数。7.3 阶段三数据清洗与分析实战目标把零散操作整合成一条完整流水线为后续项目打底。练习清单对一份含缺失值和重复值的表格执行完整清洗流程。用groupby().agg()计算分组统计量。用transform对分组内的缺失值填充。用np.where创建条件标记列。对比apply和向量化操作的运行时长。验收标准拿到一份脏数据后能按固定流程完成清洗并解释每一步为什么这样做。7.4 阶段四量化项目落地目标把 NumPy、Pandas 的知识串成一个可运行的小项目并理解回测中的基本概念。练习清单生成或读取模拟行情数据。计算移动平均、收益率、波动率。生成双均线信号并用shift(1)避免未来函数。计算策略净值和买入持有净值。对比结果并解释潜在偏差。验收标准能独立写出一个小型回测脚本并能通过打印中间结果解释策略逻辑。7.5 学习里程碑与自检清单阶段关键能力达标标准NumPy数组创建、切片、广播能写出无显式循环的收益率计算Pandas 基础Series/DataFrame 索引与类型转换能准确处理日期和列名数据清洗去重、缺失值、分组聚合能整理一份完整清洗流程量化项目指标计算、信号、回测验证能解释shift(1)的作用自检时每次只问三个问题数据从哪里来中间结果长什么样结果怎么验证如果三个问题都能回答说明这个知识点已经进入工程实践层面而不是只停留在 API 名称上。8. 最佳实践与扩展方向8.1 日常编码中的 Pandas 规范列名统一小写避免空格和特殊符号。日期列尽早转为datetime数值列尽早转为float/int。所有清洗步骤都生成新变量不要反复修改原始 DataFrame。使用df.copy()保护视图。每条分析链路最后都输出一份中间结果快照便于回溯。写自定义函数时优先使用向量化操作禁止在超大 DataFrame 上写 for 循环。针对第 6 条常见的例外是复杂字符串清洗或无法用现有 Pandas API 表达的业务逻辑这个时候可以用apply但也要限制在数据规模可控的范围内。8.2 性能建议向量化、分块、减少复制数据分析的机器学习场景下Pandas 性能瓶颈往往不在计算速度而在内存复制和循环遍历。推荐做法筛选列时只保留需要的字段减少内存占用。读取 CSV 时使用usecols指定列分块读取用chunksize。数据量达到数 GB 时优先使用 Parquet 格式替代 CSV。合并多个 DataFrame 前先确认连接键是否唯一避免笛卡尔积膨胀。能使用agg完成的分组聚合不要写groupby().apply()。一个典型的读取优化df pd.read_csv( large_data.csv, usecols[date, symbol, close, volume], parse_dates[date] )只用需要的列读取时间会显著下降。这也符合数据分析项目“低成本验证、再上全量数据”的思路。8.3 大数据量场景的扩展Parquet、Feather、SparkPandas 适合单机内存中的数据分析。当数据量超过单机内存或者需要分布式计算时常见方向是使用 PyArrow 读写 Parquet 和 Feather格式上比 CSV 更高效。使用 Dask 或 Polars 处理单机超大数据。使用 Spark 的 DataFrame API 处理分布式数据。热搜词中出现的“pandas 与 numpy 实现 spark 在格式 parquet 及语言 feather 等上的案例操作”本质就是在说数据格式是通用中间层Pandas 和 Spark 都能读写 Parquet因此跨工具协作时先统一数据格式比统一代码更重要。df.to_parquet(output.parquet, indexFalse) df2 pd.read_parquet(output.parquet)这段代码可以成为 Pandas 项目接入 Spark 的前置步骤。Spark 读入同一个 Parquet 文件后后续聚合逻辑可以逐步改写。8.4 继续往前走的三条路线完成从 NumPy 到 Pandas 再到量化项目这条线之后下一步可以根据兴趣选择方向。第一条是数据可视化用 Matplotlib、Seaborn、Plotly 把清洗后的结果画成图。很多分析结论只靠print看不出来画出净值曲线和均线交叉后理解会比文字深刻得多。第二条是统计分析用scipy、statsmodels做假设检验、回归分析把“看起来有变化”变成“统计上是否显著”。第三条是机器学习与商业数据分析把 Pandas 清洗后的结果输入到scikit-learn、XGBoost 或深度学习框架中做分类、回归、风控评分等任务。金融风控数据分析里的评分卡、违约预测都离不开 Pandas 阶段的数据准备能力。对于刚开始看教程、学不明白 Pandas 的读者最值得做的练习不是把 34 集都看一遍而是从第 1 集开始每一段代码都运行一遍并试图修改参数观察结果变化。学完一个阶段就对应做一次“输入是什么、输出是什么、为什么这样写”的复盘。把 API 当成工具而不是知识来记把项目当成验证工具而不是收藏夹来对待才能真正把从 NumPy 到量化项目这条链路串起来。
返回列表