pandas v0.6.1 版本技术解读:快速标量访问、秩相关与稀疏数据结构演进
【免费下载链接】pandasFlexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more项目地址: https://gitcode.com/gh_mirrors/pa/pandas
pandas v0.6.1(2011 年 12 月 13 日发布)是 pandas 早期迭代中的一次重要版本更新,其核心聚焦于数据访问性能优化、统计相关能力的扩展(Spearman/Kendall 秩相关)以及稀疏数据结构的基础重构(SparseArray/SparseList)。本文以 v0.6.1 发布说明 为骨架,结合当前仓库中的源码实现,逐一拆解该版本引入的新特性与性能改进,帮助读者理解这些 API 的设计初衷、底层实现与在当代 pandas 中的演化。
一、低开销标量访问:get_value与set_value
v0.6.1 为Series、DataFrame和Panel新增了get_value/set_value方法,用于对标量元素进行极低开销的访问。发布说明明确指出,在多数场景下这一访问路径比常规索引快 2 倍以上(对应 issue #437、#438),并且set_value具备"扩大对象"(enlarged object)的能力——即在写入不存在的行列标签时自动扩展数据结构。
从当前源码仍可看到这一设计的内部雏形。DataFrame._get_value的实现位于 pandas/core/frame.py:
- 当
takeable=True时,直接把index/col当作位置索引器使用:先取列再取行值; - 否则先通过
_get_item(col)取得列,再对行索引调用get_loc定位; - 对于
MultiIndex,走_engine.get_loc快速路径,遇到TypeError(如 DatetimeIndex 上的部分字符串切片)再回退到index.get_loc。
其对应写入路径DataFrame._set_value位于 pandas/core/frame.py,Series的_get_value/_set_value同样保留在 pandas/core/series.py 中。之所以"快 2 倍以上",是因为这一路径绕过了__getitem__/__setitem__中完整的索引器分发(切片、布尔掩码、位置/标签混合等)逻辑,直连索引引擎与底层 block 存储,代价是要求行索引唯一(调用方负责保证self.index._index_as_unique)。
在现代 pandas 中,get_value/set_value已从公开 API 中移除,公开用法被.at/.iat(标签/位置标量访问)取代,但_get_value/_set_value仍作为内部快速路径被大量核心代码调用,是理解 pandas 索引性能的关键入口。
二、秩相关:Spearman 与 Kendall 相关系数
v0.6.1 为Series.corr和DataFrame.corr增加了method选项,支持'pearson'(默认)、'spearman'与'kendall'三种相关系数(issue #428)。
'spearman':Spearman 秩相关系数,即对两序列分别做秩变换后再计算 Pearson 相关,度量单调(而非线性)关联;'kendall':Kendall Tau 系数,基于序对(concordant/discordant pairs)的秩相关,对异常值更稳健;- 同时支持传入可调用对象(接收两个一维 ndarray、返回 float 的函数),例如直方图交集函数
histogram_intersection。
当前实现可在 pandas/core/nanops.py 中看到:
if method == "kendall": from scipy.stats import kendalltau return kendalltau(a, b)[0] elif method == "spearman": from scipy.stats import spearmanr return spearmanr(a, b)[0]即 Kendal/Spearman 分支在依赖scipy可用时调用scipy.stats.kendalltau/scipy.stats.spearmanr。Series.corr的完整参数签名为(pandas/core/series.py):
def corr( self, other: Series, method: CorrelationMethod = "pearson", min_periods: int | None = None, ) -> floatDataFrame.corr则在此基础上增加了min_periods(每对列的最小观测数)与numeric_only(2.0.0 起默认False)参数,见 pandas/core/frame.py。需要留意两点行为约定:
- 三种方法均使用逐对完整观测(pairwise complete observations)计算;
- 传入 callable 时,返回值矩阵的对角线仍被强制为 1、矩阵保持对称,与 callable 本身行为无关。
import pandas as pd s1 = pd.Series([1, 2, 3], index=[0, 1, 2]) s2 = pd.Series([1, 2, 3], index=[2, 1, 0]) s1.corr(s2) # -1.0(自动按索引对齐) s1.corr(s2, method="kendall") s1.corr(s2, method="spearman") df = pd.DataFrame({"a": [1, 2, 3, 4], "b": [4, 3, 2, 1]}) df.corr(method="spearman") # 相关系数矩阵三、快速数据排名:Series.rank/DataFrame.rank
与秩相关配套,v0.6.1 实现了 Series 与 DataFrame 的快速排名功能,发布说明称其为scipy.stats.rankdata的快速版本(issue #428)。这一功能后来演化为核心 APIrank(),目前统一实现在 pandas/core/algorithms.py,并由 pandas/core/generic.py 暴露给 Series/DataFrame。
核心参数包括:
| 参数 | 取值 | 含义 |
|---|---|---|
method | 'average'/'min'/'max'/'first'/'dense' | 并列值的打破策略,默认'average'(取平均名次) |
na_option | 'keep'/'top' | NaN 的处理方式,'keep'保持 NaN 排名,'top'用 ±inf 替换使 NaN 排到最前 |
ascending | True/False | 升序或降序排名 |
pct | True/False | 是否以百分位形式输出(如0.333...) |
实现上,一维排名直接调用 Cython 层的algos.rank_1d(位于 pandas/_libs/algos.pyx),这也是"快速排名"名称的由来;时间日期类数据(needs_i8_conversion)会先转换底层整数表示再排名。排名结果被DataFrame.corr(method='spearman')等统计功能复用,构成 v0.6.1 相关性扩展的底层支撑。
四、DataFrame.from_items:替代构造函数
v0.6.1 引入DataFrame.from_items作为 DataFrame 的替代构造函数(issue #444),允许用[(key, value), ...]形式的键值对列表构造 DataFrame。在后续版本中该 API 逐步被更通用的DataFrame(dict)与DataFrame.from_dict取代并最终移除,但其历史价值在于:在当时 dict 构造的语义尚未稳定时,为"有序键值对列表 → 列式结构"提供了显式入口。当前仓库中from_items已不在公开实现中,其演化测试仍保留在 pandas/tests/frame/constructors/test_from_dict.py,可用pd.DataFrame(list_of_tuples)实现等价效果。
五、pivot_table的margins参数:子组聚合
v0.6.1 为pivot_table增加了margins选项(issue #114),用于计算分组聚合的边际合计(行/列小计与总计)。该参数从 v0.6.1 一直延续至今,当前签名位于 pandas/core/reshape/pivot.py:
def pivot_table( data: DataFrame, values=None, index=None, columns=None, aggfunc: AggFuncType = "mean", fill_value=None, margins: bool = False, dropna: bool = True, margins_name: Hashable = "All", observed: bool = True, sort: bool = True, **kwargs, ) -> DataFrame:margins=True时结果会额外出现名为margins_name(默认"All")的行与列,存放各子组的聚合结果;- 需要特别注意:计算 margins 前会先剔除任一列含 NA 的行,因此含缺失值时边际值与逐行汇总可能不一致;
dropna=False时行/列索引中的 NA 键也会参与汇总。
import pandas as pd df = pd.DataFrame({ "类别": ["A", "A", "B", "B", "A"], "区域": ["东", "西", "东", "西", "东"], "销售额": [10, 20, 30, 40, 50], }) pd.pivot_table(df, values="销售额", index="类别", columns="区域", aggfunc="mean", margins=True, margins_name="合计")六、稀疏数据结构:SparseArray与SparseList
v0.6.1 实现了新的SparseArray与SparseList数据结构(issue #463),并让SparseSeries改为派生自SparseArray,完成了稀疏数据从"专用 Series 容器"到"通用 ExtensionArray"的架构转变。这一设计在当代代码中仍然延续:SparseArray目前定义于 pandas/core/arrays/sparse/array.py,继承OpsMixin/PandasObject/ExtensionArray,采用"稀疏索引(sp_index)+ 填充值(fill_value)"的内存布局,仅存储非填充值及其位置,从而在大量零/NA 数据上显著节省内存。稀疏子模块的整体结构位于 pandas/core/arrays/sparse/,包含array.py、dtype.py、accessor.py等文件。
import pandas as pd arr = pd.arrays.SparseArray([1, 0, 0, 2, 0, 3], fill_value=0) print(arr.sp_values) # 仅存储 [1, 2, 3] print(arr.sp_index) # 稀疏索引对象 print(arr.density) # 非填充值占比七、数据整形与类型推断
7.1DataFrame.align接受 Series 与 axis 参数
v0.6.1 让DataFrame.align能够接受 Series 参数,并新增axis选项(issue #461)。此前对齐操作仅支持 DataFrame 之间,此改动打通了 Series 与 DataFrame 的按轴对齐,为后续的join/算术运算统一对齐逻辑铺路。
7.2DataFrame.convert_objects:对象列 dtype 推断
DataFrame.convert_objects用于对 object 列推断更合适的 dtype(issue #302),例如把"看起来是数字"的字符串列推断为数值列。该 API 后续被infer_objects()取代,其思想(对每列调用infer_dtype并尝试转换)沿用至今,当前实现对应 pandas/core/dtypes/cast.py 中的推断逻辑。
7.3MultiIndex.get_level_values接受层级名
v0.6.1 起MultiIndex.get_level_values除位置序号外,还接受 level name 字符串作为参数,使多级索引取值不再依赖层级位置记忆,显著改善可读性。该能力在后续版本中持续保留,MultiIndex.get_level_values的当前实现见 pandas/core/indexes/multi.py。
八、滚动相关/协方差与成对相关矩阵
v0.6.1 同时扩展了滚动统计:
rolling_corr/rolling_cov支持混合输入:DataFrame/DataFrame与DataFrame/Series均可传入(GH #462),为滚动相关性计算提供了更灵活的配对方式;- 新增
rolling_corr_pairwise:用于计算相关矩阵的 Panel(issue #189),即对一组序列在滚动窗口内逐一计算两两相关,输出每个窗口期的相关矩阵集合。
这些 API 的当代继承者位于 pandas/core/window/rolling.py(Rolling.corr)与 pandas/core/window/expanding.py,且支持pairwise参数:
df = pd.DataFrame({"x": [1, 2, 3, 4, 5], "y": [2, 4, 1, 5, 3]}) df.rolling(window=3).corr() # 成对滚动相关矩阵 df.rolling(window=3).cov()九、追加单行与Series.from_csv
9.1 向 DataFrame 追加单行
v0.6.1 允许以 Series 形式向 DataFrame 追加单行,这是DataFrame.append的一次重要增强(此前更多面向 DataFrame 拼接)。追加单行的经典用法为:
df = pd.DataFrame({"a": [1, 2], "b": [3, 4]}) new_row = pd.Series({"a": 5, "b": 6}, name="row3") df = pd.concat([df, new_row.to_frame().T])需要说明的是:DataFrame.append在 pandas 1.4.0 起已被移除,官方推荐统一使用pd.concat实现行/列拼接,上述写法是当代的等价实践。
9.2Series.from_csv
v0.6.1 还新增了Series.from_csv类方法(issue #482),用于从 CSV 文件直接构造 Series(将某列作为索引、另一列作为值)。该 API 属于早期"从文件直构 Series"的快捷方式,后续被pd.read_csv(..., squeeze=True)及Series(data, index=...)的组合用法取代。
十、性能改进:更省内存的describe与更快的标量查找
v0.6.1 同步包含四项性能优化(详见 doc/source/whatsnew/v0.6.1.rst):
DataFrame.describe内存优化(PR #425):不再不必要地复制数据,避免描述性统计时产生整份数据的临时副本;- 标量查找优化:Series 与 DataFrame 的标量取值在常规场景下提升25% 以上,与新增的
get_value/set_value快速路径相呼应; dropna速度修复:修复了横截面(cross-sectional)count 的性能回归,直接影响DataFrame.dropna的执行速度;- 列删除零拷贝(GH #158):删除 DataFrame 列时不再复制数据,而是在 block 上计算视图,将删除操作从 O(n) 拷贝降为 O(1) 引用调整。
其中第 2、4 项体现了 pandas 一贯的"按需拷贝(copy-on-write 前身)"思想:能引用就不复制,能走引擎就不走通用分发。这一方向在后续版本中持续演进,最终在 pandas 2.0+ 中发展为正式的 Copy-on-Write 模式(相关设计说明见 doc/source/development/copy_on_write.rst)。
十一、更好的控制台打印选项
v0.6.1 提供了更好的控制台打印选项(issue #453),让用户在终端中展示 DataFrame/Series 时有更多可控维度。当前这些选项集中在 pandas/_config/display.py 与 pandas/core/config_init.py 中,常用项包括:
import pandas as pd pd.set_option("display.max_rows", 20) # 最多显示行数 pd.set_option("display.max_columns", 10) # 最多显示列数 pd.set_option("display.width", 120) # 输出总宽度 pd.set_option("display.max_colwidth", 50) # 单列最大字符宽度 pd.set_option("display.float_format", "{:.2f}".format) # 浮点格式化这些选项为后续pd.set_option/pd.describe_option的完整选项体系奠定了基础,也是大规模 DataFrame 在 REPL/日志中可读输出的核心工具。
十二、PyQt 表格控件实验(sandbox)
v0.6.1 在 sandbox 中加入了 PyQt 表格控件(issue #435),用于在桌面 GUI 环境中可视化 DataFrame。这属于实验性质组件,未进入主 API 面,但其"DataFrame ↔ 表格控件"的桥接思路后来影响了DataFrame.style(pandas/io/formats/style.py)与 notebook 中的 HTML 渲染体系,可视为 pandas 前端展示能力的前期探索。
结语
v0.6.1 虽然是一个小版本号,却集中引入了多项影响深远的 API 与架构决策:get_value/set_value确立了低开销标量访问的性能范式(演化出.at/.iat),Spearman/Kendall 与快速rank补齐了秩统计能力,SparseArray奠定了 ExtensionArray 时代的稀疏数据基础,margins则成为透视表分析不可或缺的组成部分。对照 当前仓库 中的实现路径,可以清晰看到这些 2011 年的设计如何在十余年间被继承、重构与规范化——这也正是阅读历史发布说明对理解现代 pandas 的价值所在。
【免费下载链接】pandasFlexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more项目地址: https://gitcode.com/gh_mirrors/pa/pandas
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考