如何将按 gameId 和 teamId 汇总的统计展开为主客场列?
数据阿明
结论
先根据每场比赛的 homeTeam 和 awayTeam,将球队统计标记为 home 或 away。然后给两侧的统计列分别添加前缀,并按 gameId 合并。你目前按 gameId 和 teamId 合并 skater 与 goalie 数据,这样每队仍占一行;要区分主队和客队,还需要增加主客队标记。
以下示例假设统计表已重置索引,包含 gameId、teamId 和各项统计列;比赛元数据表每场比赛一行,包含 gameId、homeTeam 和 awayTeam。统计表中每场比赛的每支球队也应各有一行。
展开主客队统计
import pandas as pd
def to_home_away(stats, games, game_id="gameId", team_id="teamId",
home_team="homeTeam", away_team="awayTeam"):
# stats 应包含 gameId、teamId 和统计列;games 每场比赛一行。
stat_cols = [
col for col in stats.columns
if col not in {game_id, team_id}
]
metadata = games[[game_id, home_team, away_team]]
rows = stats.merge(
metadata,
on=game_id,
how="left",
validate="many_to_one",
)
rows["side"] = None
rows.loc[rows[team_id].eq(rows[home_team]), "side"] = "home"
rows.loc[rows[team_id].eq(rows[away_team]), "side"] = "away"
if rows["side"].isna().any():
raise ValueError("存在无法匹配 homeTeam 或 awayTeam 的球队记录")
home = (
rows.loc[rows["side"].eq("home"), [game_id, *stat_cols]]
.set_index(game_id)
.add_prefix("home_")
)
away = (
rows.loc[rows["side"].eq("away"), [game_id, *stat_cols]]
.set_index(game_id)
.add_prefix("away_")
)
return home.join(away, how="outer").reset_index()
先对 skaters_reduced 和 goalies_reduced 调用 reset_index(),将 gameId 和 teamId 转为列,再分别展开。之后按 gameId 合并两张宽表:
skaters_reduced = skaters_reduced.reset_index()
goalies_reduced = goalies_reduced.reset_index()
skater_wide = to_home_away(skaters_reduced, games)
goalie_wide = to_home_away(goalies_reduced, games)
game_stats = skater_wide.merge(
goalie_wide,
on="gameId",
how="outer",
validate="one_to_one",
)
这里的 games 是比赛元数据 DataFrame,列名应与示例中的 gameId、homeTeam、awayTeam 一致。若实际列名不同,可传入对应的列名。validate="many_to_one" 会检查元数据中的 gameId 是否唯一;最终合并使用的 validate="one_to_one" 会检查展开后的每张表是否每场比赛只有一行。
注意事项
展开后,每场比赛对应一行,teamId 无法再单独表示某一支球队,因为这一行包含主队和客队的数据。如需保留球队标识,可另外保留 homeTeam 和 awayTeam 两列。如果同一场比赛中的同一支球队有多行统计,应先按实际业务规则聚合,再进行展开;不要默认将重复记录相加。
备注:内容仅供参考。
