Pandas:按gameId分组后关联生成related_player列
问题描述
我有一个约一万行的DataFrame,数据示例如下:
import pandas as pd import numpy as np data = {'gameId': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2], 'eventId': [1, 2, 3, 4, 5, 1, 2, 3, 4, 5], 'player': ['A', 'B', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'E'], 'related_eventId': [2, 1, 4, 3, np.nan, 2, 1, 4, 3, np.nan]} df = pd.DataFrame(data)
需要生成名为related_player的列,该列的值为同gameId分组下,eventId与当前行related_eventId相等的行对应的player值。
之前无gameId分组时,可以用merge实现:
result = df.merge(df[['eventId', 'player']], left_on='related_eventId', right_on='eventId', how='left', suffixes=('', '_related')) result.rename(columns={'player_related': 'related_player', 'eventId_related': 'related_eventId'}, inplace=True) result = result[['eventId', 'player', 'related_eventId', 'related_player']]
但加入gameId分组后,上述方法无法得到正确结果,求Pandas中的正确实现方式。
预期输出如下:
| gameId | eventId | player | related_eventId | related_player |
|---|---|---|---|---|
| 1 | 1 | A | 2 | B |
| 1 | 2 | B | 1 | A |
| 1 | 3 | C | 4 | D |
| 1 | 4 | D | 3 | C |
| 1 | 5 | E | NaN | NaN |
| 2 | 1 | A | 2 | B |
| 2 | 2 | B | 1 | A |
| 2 | 3 | C | 4 | D |
| 2 | 4 | D | 3 | C |
| 2 | 5 | E | NaN | NaN |
解决方案
方法1:多键Merge(最直观高效)
直接在merge时同时匹配gameId和related_eventId,确保关联的是同分组内的eventId对应数据:
# 准备用于关联的临时表,保留分组、事件ID和玩家信息 lookup = df[['gameId', 'eventId', 'player']] # 多键匹配merge result = df.merge( lookup, left_on=['gameId', 'related_eventId'], right_on=['gameId', 'eventId'], how='left', suffixes=('', '_related') ) # 整理列名和结构 result = result.rename(columns={'player_related': 'related_player'}).drop(columns='eventId_related')
该方法逻辑清晰,一万行数据的处理效率完全够用。
方法2:GroupBy + 分组内映射
按gameId分组后,在每个分组内构建eventId到player的字典映射,再匹配related_eventId:
def add_related_player(group): # 构建当前分组内的事件ID-玩家映射 event_player_map = group.set_index('eventId')['player'].to_dict() # 映射关联事件对应的玩家 group['related_player'] = group['related_eventId'].map(event_player_map) return group # 分组应用处理函数 result = df.groupby('gameId', group_keys=False).apply(add_related_player)
这种方式更贴合分组逻辑,代码可读性强,适合理解分组内的匹配规则。
方法3:多层索引映射(简洁写法)
先构建(gameId, eventId)为键的多层索引映射,再通过组合键匹配:
# 构建分组+事件ID到玩家的映射 mapping = df.set_index(['gameId', 'eventId'])['player'] # 每行用(gameId, related_eventId)作为键查找对应玩家 df['related_player'] = df.apply(lambda x: mapping.get((x['gameId'], x['related_eventId'])), axis=1)
写法简洁,但apply的效率略低于前两种方法,不过一万行数据完全可以正常运行。
内容的提问来源于stack exchange,提问作者Delopera
相关产品推荐
相关产品推荐

