0

0

Pandas中基于分组和扩展窗口计算百分位排名

霞舞

霞舞

发布时间:2025-11-10 10:52:01

|

738人浏览过

|

来源于php中文网

原创

pandas中基于分组和扩展窗口计算百分位排名

本文旨在详细阐述如何在Pandas中使用`groupby()`、`expanding()`和`apply()`结合`scipy.stats.percentileofscore`函数,正确计算数据集中按组和扩展窗口的百分位排名。我们将重点解析`apply`函数中`lambda x`参数的正确用法,避免常见的引用错误,并提供两种计算场景的示例代码与深入解释,以帮助读者准确实现动态的百分位排名分析。

引言:动态百分位排名计算的需求

在数据分析中,我们经常需要计算某个值在其所属群体中的相对位置,这通常通过百分位排名(Percentile Rank)来实现。当这个计算还需要考虑数据的分组(groupby)以及随时间或序列不断增长的窗口(expanding)时,问题会变得更复杂。例如,我们可能需要计算每个用户当前得分相对于其历史所有得分的百分位排名。Pandas提供了强大的工具来处理这类复杂聚合,但正确结合使用这些工具,尤其是apply函数中的lambda表达式,是实现目标的关键。

一个常见的错误是在apply的lambda函数内部错误地引用了整个DataFrame的列,而非当前操作的窗口数据。这将导致计算结果不符合预期,因为它没有针对每个动态窗口进行计算。

理解 groupby().expanding().apply() 的工作机制

在深入代码之前,我们首先需要理解groupby().expanding().apply()链式操作的含义:

  1. groupby(['Category']): 这一步将DataFrame按照指定的Category列进行分组。后续的所有操作都将在这些独立的组内进行。
  2. expanding(1): 对于每个组,expanding(1)创建一个扩展窗口对象。这意味着对于组内的每一行,它会考虑从该组的第一个元素到当前行的所有数据。参数1表示最小包含的元素数量,即窗口至少包含1个元素。
  3. apply(lambda x: ...): apply方法对每个扩展窗口执行一个自定义函数。这里的关键在于lambda x中的x。在每次调用lambda函数时,x不再是整个DataFrame或Series,而是当前扩展窗口内的数据(一个Series或DataFrame片段)。因此,在lambda函数内部,我们必须使用x来引用当前窗口的数据。

scipy.stats.percentileofscore 函数简介

scipy.stats.percentileofscore(a, score, kind='rank') 是一个非常有用的函数,用于计算给定score在数组a中的百分位排名。

  • a: 这是一个数值数组(或列表),代表了要计算百分位排名的参照数据集。
  • score: 这是我们要查找其百分位排名的具体数值。
  • kind: 定义了计算方式,默认为'rank',表示分数小于或等于score的百分比。

错误的尝试与核心问题解析

假设我们有一个DataFrame df,包含Category和values两列,我们想计算每个values在对应Category的扩展窗口内的百分位排名。

一个常见的错误尝试可能类似于:

# 错误示范 (请勿直接运行)
# df['pct'] = df.groupby(['Category']).expanding(1).apply(lambda x: stats.percentileofscore(df['values'], 1)).reset_index(0, drop=True)

这里的核心问题在于percentileofscore(df['values'], 1)。在lambda x:的作用域内,x代表当前窗口的数据。但是,df['values']却引用了整个DataFrame的values列,而不是当前窗口的values。这意味着无论窗口如何变化,percentileofscore总是参照整个df['values']列来计算,这显然违背了“按组和扩展窗口”的要求。

Joker AIx
Joker AIx

一站式AI创意生产平台,覆盖图像、视频、音频、文案全品类创作

下载

正确的实现策略

为了正确实现,我们需要确保percentileofscore的第一个参数(参照数据集)是当前的扩展窗口x,而第二个参数(要计算百分位排名的分数)则根据具体需求来定。

我们将通过一个示例DataFrame来演示两种常见的计算场景。

import pandas as pd
import numpy as np
from scipy.stats import percentileofscore

# 示例数据
df = pd.DataFrame([
        ['alex', 'alex', 'bob', 'alex', 'bob', 'alex', 'bob', 'bob'],
        [0, 3, 10, 1, 15, 6, 12, 18]              
    ]).T
df.columns = ['Category', 'values']
df['values'] = df['values'].astype(int) # 确保values列是数值类型

print("原始DataFrame:")
print(df)
print("-" * 30)

# 确保数据按Category和索引排序,以保证expanding窗口的顺序一致性
df = df.sort_values(by=['Category', df.index])

场景一:计算固定分数在每个扩展窗口中的百分位排名

有时,我们可能需要评估一个固定的阈值(例如,分数1)在每个不断增长的历史数据中的表现。

# 场景一:计算固定分数(例如,1)在每个扩展窗口中的百分位排名
# 这里的x是当前窗口的Series,1是我们要计算百分位排名的固定分数
df['pct_fixed_score_1'] = df.groupby(['Category'])['values'].\
                            expanding(1).\
                            apply(lambda x: percentileofscore(x, 1)).\
                            reset_index(level=0, drop=True)

print("场景一:计算固定分数1的百分位排名")
print(df)
print("-" * 30)

代码解释:

  • df.groupby(['Category'])['values']: 首先按Category分组,并选择values列进行操作。这样x在lambda函数中将是一个Series。
  • expanding(1): 为每个组创建扩展窗口。
  • apply(lambda x: percentileofscore(x, 1)): 这是核心部分。x代表当前窗口的values Series。我们计算固定分数1在x这个Series中的百分位排名。
  • reset_index(level=0, drop=True): groupby().expanding()操作会引入多级索引(Category和原始索引)。reset_index(level=0, drop=True)用于删除Category这一级索引,使结果Series的索引与原始DataFrame的索引对齐,方便合并。

场景二:计算当前行值在每个扩展窗口中的百分位排名(更常见)

更常见的需求是,计算当前行的values值在它所属的Category的扩展窗口(即,从该组开头到当前行的所有values)中的百分位排名。

# 场景二:计算当前行值在每个扩展窗口中的百分位排名
# 这里的x是当前窗口的Series,x.iloc[-1]是当前窗口的最后一个值(即当前行的值)
df['pct_current_value'] = df.groupby(['Category'])['values'].\
                            expanding(1).\
                            apply(lambda x: percentileofscore(x, x.iloc[-1])).\
                            reset_index(level=0, drop=True)

print("场景二:计算当前行值的百分位排名")
print(df)
print("-" * 30)

代码解释:

  • 与场景一类似,主要区别在于percentileofscore的第二个参数。
  • x.iloc[-1]: 在lambda函数中,x是当前扩展窗口的Series。x.iloc[-1]安全地获取了该窗口中的最后一个值,即当前行对应的values。这样,我们就能计算当前值在其自身历史数据中的百分位排名。

注意事项与性能考量

  1. 索引排序: 在进行expanding操作之前,确保你的DataFrame已经按照分组键和时间/序列顺序进行了排序。否则,扩展窗口的顺序可能不是你期望的。在上面的例子中,我们添加了df = df.sort_values(by=['Category', df.index])来确保这一点。
  2. min_periods: expanding(min_periods=N)中的N参数非常重要。它指定了窗口中至少需要有多少个非NaN值才能进行计算。如果窗口中的元素数量少于min_periods,则结果将是NaN。默认值为1。
  3. 处理 NaN 值: percentileofscore函数默认会处理NaN值,但如果你的数据中存在大量NaN,可能需要考虑在apply之前或lambda函数内部进行显式的NaN处理(例如,x.dropna())。
  4. 性能: apply方法在Pandas中虽然灵活,但对于大型数据集而言,它通常不是最高效的。因为它在Python循环中执行自定义函数。如果性能成为瓶颈,可以考虑以下替代方案:
    • 对于简单的百分位排名(如rank(pct=True)),Pandas内置的rank()函数通常更快。但percentileofscore的语义略有不同(它计算的是分数小于或等于给定值的百分比),所以不能直接替换。
    • 如果可能,尝试寻找Numba或Cython等工具进行性能优化,或者将apply函数向量化(如果percentileofscore有NumPy等价的向量化版本)。然而,对于percentileofscore这种需要对每个窗口独立计算的场景,apply往往是最直接和可读性最好的方法。

总结

通过本文的讲解和示例,我们深入探讨了如何在Pandas中利用groupby().expanding().apply()结合scipy.stats.percentileofscore函数来计算基于分组和扩展窗口的百分位排名。核心要点在于:在lambda x: ...中,x代表当前窗口的数据,而不是整个Series或DataFrame。理解并正确使用x是解决这类问题的关键。无论是计算固定分数的排名还是当前值的排名,遵循这一原则都能帮助我们构建准确且功能强大的数据分析流程。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

80

2025.12.04

Python 数据清洗与预处理实战
Python 数据清洗与预处理实战

本专题系统讲解 Python 在数据清洗与预处理中的核心技术,包括使用 Pandas 进行缺失值处理、异常值检测、数据格式化、特征工程与数据转换,结合 NumPy 高效处理大规模数据。通过实战案例,帮助学习者掌握 如何处理混乱、不完整数据,为后续数据分析与机器学习模型训练打下坚实基础。

32

2026.01.31

Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

80

2025.12.04

Python 数据清洗与预处理实战
Python 数据清洗与预处理实战

本专题系统讲解 Python 在数据清洗与预处理中的核心技术,包括使用 Pandas 进行缺失值处理、异常值检测、数据格式化、特征工程与数据转换,结合 NumPy 高效处理大规模数据。通过实战案例,帮助学习者掌握 如何处理混乱、不完整数据,为后续数据分析与机器学习模型训练打下坚实基础。

32

2026.01.31

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

215

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

192

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

61

2026.01.05

PHP 高并发与性能优化
PHP 高并发与性能优化

本专题聚焦 PHP 在高并发场景下的性能优化与系统调优,内容涵盖 Nginx 与 PHP-FPM 优化、Opcode 缓存、Redis/Memcached 应用、异步任务队列、数据库优化、代码性能分析与瓶颈排查。通过实战案例(如高并发接口优化、缓存系统设计、秒杀活动实现),帮助学习者掌握 构建高性能PHP后端系统的核心能力。

113

2025.10.16

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 5万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号