资讯动态

yfinance 多级列索引(Multi-Level Column Index)完全指南:理解、存储与展平

发布时间:2026/9/12 2:23:13 来源:尧图企业网站定制
yfinance 多级列索引Multi-Level Column Index完全指南理解、存储与展平【免费下载链接】yfinanceDownload market data from Yahoo! Finances API项目地址: https://gitcode.com/GitHub_Trending/yf/yfinanceyfinance 的download()在下载多个股票代码时返回的是一个带有两层列索引MultiIndex的 pandas DataFrame——一层是 Ticker股票代码一层是股票价格字段Open/High/Low/Close/Volume 等。本指南以 multi_level_columns.rst 为核心结合 multi.py 的源码实现讲清楚三件事多级列的结构是怎么产生的、用to_csv()保存后如何正确读回多级列、以及如何得到单级列名 独立 Ticker 列的扁平化 DataFrame方便直接用于建模、入库或机器学习。一、多级列索引从何而来源码视角yfinance 的多级列结构并非巧合而是在 multi.py 的_download_impl()中显式构造出来的。当download()完成所有 ticker 的抓取后会执行一次关键拼接data _pd.concat( ctx.dfs.values(), axis1, sortTrue, keysctx.dfs.keys(), names[Ticker, Price] )这段代码位于 multi.py。含义是每个 ticker 的行情 DataFrame 沿axis1列方向拼接拼接结果的外层键keys就是各 ticker 代码层级命名为Ticker每个 ticker 内部的原有列Open、High、Low、Close、Adj Close、Volume 等形成第二层级命名为Price。于是得到了columns形如MultiIndex([(Ticker,Price), ...])的两层结构这与原文档一个层级放 Ticker、一个层级放价格数据的描述完全一致。group_by 决定层级的上下顺序拼接完成后_download_impl()根据group_by参数对两层列索引做了一次重排multi.pyif group_by column and isinstance(data.columns, _pd.MultiIndex): data.columns data.columns.swaplevel(0, 1) data.sort_index(level0, axis1, inplaceTrue)也就是说group_by 取值最外层level 0内层level 1视觉排列column默认Price价格字段Ticker股票代码同名字段相邻方便横向对比各股的价格tickerTicker股票代码Price价格字段同一只股票的所有字段聚在一起group_by的默认值是column见 multi.py 的函数签名。Tickers类在内部走download()时也遵循同样的重排逻辑见 tickers.py。访问某一层的方法无论采用哪种group_by都可以用 pandas 的层级访问手段操作列import yfinance as yf df yf.download([AAPL, MSFT], period1mo) # 默认 group_bycolumn # 查看两层列名 print(df.columns) # MultiIndex: (Price, Ticker) print(df.columns.levels) # 各层的取值集合 # 取某一层 print(df.columns.get_level_values(Price)) # [Open,High,Low,Close,...] print(df.columns.get_level_values(Ticker)) # [AAPL,MSFT,...]仓库测试 test_prices.py 正是通过df.columns.levels[1]断言内层即 Ticker 层包含全部请求的股票代码df_tkrs df.columns.levels[1] self.assertEqual(sorted(tkrs), sorted(df_tkrs))二、核心痛点to_csv()保存后多级列如何读回原文档指出的第一个关键问题是直接把多级列 DataFrame 用pandas.DataFrame.to_csv存盘再直接pd.read_csv()读回来多级列就丢了。原因是to_csv()会把两层列索引序列化成 CSV 文件开头的多行表头第一行是外层 Price第二行是内层 Ticker而默认的pd.read_csv()只把第一行当作列名第二层信息被当成数据行或直接丢失。正确的保存与读取方式保存保存时无需特殊处理正常调用即可df yf.download([AAPL, MSFT], period1mo) # 多级列 DataFrame df.to_csv(prices.csv)读取——关键在于用header[0, 1]告诉 pandas 前两行都是表头用index_col0说明第一列是日期索引import pandas as pd df_restored pd.read_csv( prices.csv, header[0, 1], # 恢复两层列索引 index_col0, # 第一列是日期DatetimeIndex parse_datesTrue, # 解析日期 )读回后df_restored.columns依然是MultiIndex([(Open,AAPL), ...])的两层结构与保存前等价之后可以继续用get_level_values()、.loc[:, (Close, AAPL)]等方式访问。进阶技巧读取后手动重建 MultiIndex如果 CSV 来自其他工具、表头行数不确定也可以在读取后手动重建列索引df_flat pd.read_csv(prices.csv, index_col0, parse_datesTrue) # 方法一按列名规律切分如 Close.AAPL 形式具体分隔符取决于 to_csv 版本与 columns 设置 # 方法二直接手工指定两层结构 new_cols pd.MultiIndex.from_tuples( [(name.split(.)[0], name.split(.)[1]) for name in df_flat.columns], names[Price, Ticker], ) df_flat.columns new_cols提示to_csv()输出列名时默认用.连接两层如Close.AAPL这正是原始 Stack Overflow 问答中用户困惑的来源之一。理解这一点后无论是正则拆分还是MultiIndex.from_tuples重建都变得直观可控。三、扁平化方案单级列名 独立 Ticker 列原文档讨论的第二个核心问题是如何把多级列 DataFrame 转成单级列名 一行一个 Ticker 列的整齐结构——这种形态最便于groupby聚合、pandas 以外的库处理或直接写入关系型数据库。方案一stack()reset_index()多 ticker 通用利用 pandas 自带的stack()把 Ticker 这一层从列索引压到行索引上再把行索引里的层级提升为普通列import yfinance as yf df yf.download([AAPL, MSFT], period1mo, group_byticker) # 将 Ticker 层从列压到行并把行索引中的 Ticker 层转为普通列 df_long df.stack(levelTicker).reset_index(levelTicker).rename( columns{Ticker: ticker} ) # 结果列名为单级的 Open/High/Low/Close/Volume...多出一列 ticker print(df_long.head()) print(df_long.columns) # Index([ticker, Open, High, Low, Close, ...])group_byticker时列层级顺序为(Ticker, Price)第一层是 Ticker因此stack(levelTicker)能正确把 Ticker 摘出。若使用默认的group_bycolumn列层级为(Price, Ticker)则相应写成stack(levelTicker)依旧可行因为stack按层级名字定位与层级顺序无关。方案二multi_level_indexFalse仅限单 tickerdownload()还提供了一个专门参数multi_level_index默认True用于在只下载一个 ticker时直接返回单级列 DataFrame。其实现位于 multi.pyif not multi_level_index and len(tickers) 1: data data.droplevel(0 if group_by ticker else 1, axis1).rename_axis(None, axis1)使用方式df yf.download(AAPL, period1mo, multi_level_indexFalse) print(df.columns) # 单级Index([Open,High,Low,Close,...])必须注意适用前提从源码可见multi_level_indexFalse只在len(tickers) 1时生效当下载多个 ticker 时该参数不产生去层级效果列仍是两层 MultiIndex。这一点仓库测试 test_ticker.py 有明确断言if (not mli) and n 1: self.assertFalse(isinstance(data.columns, pd.MultiIndex)) else: self.assertIsInstance(data.columns, pd.MultiIndex)即仅当multi_level_indexFalse且单 ticker 时列不再是MultiIndex其余情况一律是MultiIndex。多 ticker 需要扁平化时请使用方案一的stack()路线。方案三多 ticker 场景下按需抽取子集如果并不需要完整的长表也可以直接利用多级列做切片用.loc按字段取值本质上仍然保留多级结构df yf.download([AAPL, MSFT], period1mo) # (Price, Ticker) 布局 closes df.loc[:, Close] # 只剩 Ticker 一层Close 下各股收盘价 aapl_close df.loc[:, (Close, AAPL)] # 单只股票的收盘序列这种做法适合快速分析若目标是稳定输出单级列 Ticker 列的正式数据管道仍推荐方案一。四、单 ticker 与多 ticker 的行为差异总结综合源码 multi.py 与测试用例可将行为归纳如下场景返回列结构备注多 tickermulti_level_indexTrue默认两层MultiIndexTicker / Pricegroup_by决定层级顺序多 tickermulti_level_indexFalse仍为两层MultiIndex源码len(tickers)1条件不满足单 tickermulti_level_indexFalse单级列无 Ticker 层见 multi.py单 tickermulti_level_indexTrue两层MultiIndex层级中只有 1 个 Ticker 取值可通过droplevel自行去除另外注意download()的threads多线程抓取与多级列结构相互独立并发下载的线程安全由_DownloadCtx的锁机制保证测试 test_multi.py 专门验证了并发download()不会把不同 ticker 的数据混进彼此的列中columns.get_level_values(Ticker)各自只含本组代码。五、实操速查三种数据形态一键切换把上面所有方案汇成一张速查表方便直接对照使用import yfinance as yf tickers [AAPL, MSFT] df yf.download(tickers, period1mo, group_bycolumn) # 形态 1保持多级列默认用两层定位 open_price df.loc[:, (Open, AAPL)] # 形态 2存 CSV 后正确读回多级列 df.to_csv(prices.csv) restored pd.read_csv(prices.csv, header[0, 1], index_col0, parse_datesTrue) # 形态 3扁平化为单级列 Ticker 列多 ticker df_t yf.download(tickers, period1mo, group_byticker) long_df df_t.stack(levelTicker).reset_index(levelTicker) # 形态 4单 ticker 直接拿单级列 single yf.download(AAPL, period1mo, multi_level_indexFalse)小结多级列索引是 yfinancedownload()的默认返回形态其(Ticker, Price)两层命名由 multi.py 的pd.concat(..., names[Ticker, Price])定义group_by只改变层级的上下顺序而不改变层的内容。处理它有三条主线理解用get_level_values/levels访问、持久化用header[0, 1]读回to_csv产物、展平多 ticker 用stack(levelTicker)单 ticker 可用multi_level_indexFalse。掌握这些无论后续做因子分析、机器学习还是数据入库都能在 yfinance 的原始输出与业务所需格式之间自由切换。延伸阅读price_repair 价格修复指南、配置与网络参数、download 函数参考。【免费下载链接】yfinanceDownload market data from Yahoo! Finances API项目地址: https://gitcode.com/GitHub_Trending/yf/yfinance创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价