在R中按组补全缺失的游戏参与数据
补全球员缺失的游戏记录(tidyverse实现)
现有数据
球员数据(p)
player game_id points <chr> <int> <int> 1 Bob 1 3 2 Bob 2 18 3 Bob 3 10 4 Andy 1 20 5 Andy 2 5 6 George 1 4 7 George 2 13 8 George 3 6 9 George 4 2
游戏数据(g)
game_id location <int> <chr> 1 1 H 2 2 A 3 3 A 4 4 H
问题说明
我需要补全每个球员未参与的游戏记录,原本以为用full_join能实现,但直接对整个数据集使用时,并不会为每个球员补全缺失的game_id;只有单独对某个球员过滤后再做full_join才生效:
# 全量join无法补全每个球员的缺失记录 full_join(p, g, by = 'game_id') player game_id points location <chr> <int> <int> <chr> 1 Bob 1 4 H 2 Bob 2 10 A 3 Bob 3 12 A 4 Andy 1 7 H 5 Andy 2 20 A 6 George 1 2 H 7 George 2 11 A 8 George 3 17 A 9 George 4 1 H # 单个球员过滤后join可补全缺失记录 full_join(p |> filter(player == 'Bob'), g, by = 'game_id') player game_id points location <chr> <int> <int> <chr> 1 Bob 1 4 H 2 Bob 2 10 A 3 Bob 3 12 A 4 NA 4 NA H
希望用tidyverse实现如下理想结果(注:示例中points数值仅为示意,核心是每个球员对应所有game_id记录,未参与的游戏对应的points留空):
player game_id points <chr> <int> <int> 1 Bob 1 17 2 Bob 2 1 3 Bob 3 16 4 Bob 4 NA 5 Andy 1 12 6 Andy 2 4 7 Andy 3 NA 8 Andy 4 NA 9 George 1 9 10 George 2 8 11 George 3 14 12 George 4 18
另外,考虑到数据量较大时full_join效率不高,想知道是否可以按球员分组执行join,或者有更高效的实现方式?
解决方案
方法1:分组后执行full_join
按player分组,对每组单独和游戏数据做full_join,再合并结果。逻辑直观,但数据量极大时性能一般:
library(tidyverse) p |> group_by(player) |> group_modify(~ full_join(.x, g, by = "game_id")) |> ungroup()
方法2:expand生成全组合 + left_join(更高效)
先生成所有球员和游戏ID的全组合,再和原始球员数据做left_join,避免分组循环,效率更高:
p |> expand(player, game_id = g$game_id) |> left_join(p, by = c("player", "game_id")) |> left_join(g, by = "game_id")
核心是expand()函数,它会创建player和所有game_id的笛卡尔积,确保每个球员对应所有游戏;再通过left_join关联原始数据,缺失的points自动显示为NA,同时保留location信息。
内容的提问来源于stack exchange,提问作者codeweird
相关产品推荐
相关产品推荐

