从多级分组Pandas DataFrame中筛选各账户每周Top3程序
嘿,我完全懂你遇到的问题——在多级分组的Pandas DataFrame上用nlargest()筛选TopN时,要是分组逻辑没搞对,很容易要么只返回寥寥几行,要么丢了关键数据。我来给你梳理下正确的解决思路,再说说你之前可能踩的坑。
首先,先明确你的核心需求:给每个账户的每一周,筛选出Duration平均值最高的前3个程序。关键是要按「周 + 账户」的组合来分组,而不是只按账户或者只按周单独分组。
先模拟你的数据场景
假设你的聚合后数据结构大概是这样的(我造个示例方便演示):
import pandas as pd import numpy as np # 构造模拟数据 dates = pd.date_range('2024-01-01', periods=28, freq='D') accounts = ['A001', 'A002', 'A003'] programs = ['ProgX', 'ProgY', 'ProgZ', 'ProgW'] data = { 'Date': np.random.choice(dates, 100), 'AccountID': np.random.choice(accounts, 100), 'ProgramName': np.random.choice(programs, 100), 'Duration': np.random.randint(10, 100, 100) } df = pd.DataFrame(data) # 按周、账户、程序聚合出每周平均时长 df['Week'] = df['Date'].dt.to_period('W') agg_df = df.groupby(['Week', 'AccountID', 'ProgramName'])['Duration'].mean().reset_index()
正确的Top3筛选方法
方法1:groupby + apply + nlargest(直观易懂)
直接按「Week + AccountID」分组,对每个组内的数据取Duration前3的行:
# 按周和账户分组,每组取Top3程序 top3_result = agg_df.groupby(['Week', 'AccountID']).apply( lambda group: group.nlargest(3, 'Duration') ).reset_index(drop=True)
这里的reset_index(drop=True)是为了去掉分组后生成的冗余索引,让结果更整洁。
方法2:用rank()筛选(大数据量更高效)
如果你的数据量很大,apply可能会有点慢,这时候可以用排名的方式来筛选:
# 给每个「周+账户」组内的Duration降序排名 agg_df['Duration_Rank'] = agg_df.groupby(['Week', 'AccountID'])['Duration'].rank( ascending=False, method='first' # method='first'处理并列值,避免结果超过3个 ) # 筛选排名前3的行 top3_result = agg_df[agg_df['Duration_Rank'] <= 3].drop('Duration_Rank', axis=1)
为什么你之前的方法会出错?
大概率是分组逻辑踩了坑:
- 如果你直接对整个聚合后的DataFrame用
nlargest(3),那只会返回全表中Duration最高的3行,自然只有3个结果,不是每个账户每周的。 - 如果你只按
AccountID分组,那每个账户只会取所有周里的Top3程序,而不是每周单独算Top3。 - 要是你的聚合结果是多级索引(没做
reset_index()),直接用nlargest会忽略索引层级,导致筛选逻辑混乱。
验证结果
你可以用下面的代码检查每个「周+账户」组合的行数,确认是不是大部分都是3行(如果某个账户某周的程序不足3个,那就是实际数量):
top3_result.groupby(['Week', 'AccountID']).size().value_counts()
这样应该就能得到你想要的每个账户每周的Top3程序啦!
内容的提问来源于stack exchange,提问作者IrinaS
相关产品推荐
相关产品推荐

