【问题标题】:Joining two data frames by sequential column matching通过顺序列匹配连接两个数据帧
【发布时间】:2021-11-19 17:42:12
【问题描述】:

假设我有两个这样的数据框:

test_1 = game_df = read.table(text = "id winner_name
1 Jon
2 Bob
3 Lucas
4 Marcus
5 Toad
6 Donkey", header = T)

test_2 = game_df = read.table(text = "id_1 id_2 loser_name
9 1 Henry
2 2 George
3 3 Bagel
4 4 Cat
5 5 Giraffe
7 6 Monkey", header = T)

我想做的是首先在id = id_=1匹配上left_jointest_1,如下所示:

test_1 %>% left_join(test_2 %>% select(id_1, loser_name), by = c('id' = 'id_1'))

会有某些北美比赛(约翰和驴)

  id winner_name loser_name
1  1         Jon       <NA>
2  2         Bob     George
3  3       Lucas      Bagel
4  4      Marcus        Cat
5  5        Toad    Giraffe
6  6      Donkey       <NA>

然后我想使用id_2 作为匹配列来填充 NA,所以我目前做这样的事情:

test_1 %>% left_join(test_2 %>% select(id_1, loser_name), by = c('id' = 'id_1')) %>%
  left_join(test_2 %>% select(id_2, loser_name), by = c('id' = 'id_2'))

  id winner_name loser_name.x loser_name.y
1  1         Jon         <NA>        Henry
2  2         Bob       George       George
3  3       Lucas        Bagel        Bagel
4  4      Marcus          Cat          Cat
5  5        Toad      Giraffe      Giraffe
6  6      Donkey         <NA>       Monkey

这似乎可行,但它会生成一堆带有xy 后缀的重复列。在我的实际数据集中,我必须通过大量 id 匹配来通过这种条件匹配方法,因此它会生成大量重复的列,然后我必须手动取消选择和重命名。

问题是实际test_2 data.frame 中有数百列(loser_name、loser_country、loser_elo、loser_record、loser_win_rate)等,所以我需要手动指定名称和列为每一个结合。此外,因为我用多个 id 进行这种顺序 id 匹配,所以我会有 losser_name.x、loser_name.y、loser_name.z,而且我事先不知道每一列会有多少个后缀。

有没有更简单的方法?

【问题讨论】:

  • 你的预期输出是什么
  • 预期输出是最后一个数据帧,除了只显示没有 NA (loser_name.y) 且没有.y 后缀的完整列的结果。但是,在我的实际数据集中,我有数百个正在匹配的列,因此手动重命名它们并删除不完整的列很多。

标签: r dplyr merge


【解决方案1】:

我们可以在最后做一个coalesce

library(dplyr)
test_1 %>%
   left_join(test_2 %>% select(id_1, loser_name), by = c('id' = 'id_1')) %>%  
   left_join(test_2 %>% select(id_2, loser_name), by = c('id' = 'id_2')) %>%
   transmute(id, winner_name, loser_name = coalesce(loser_name.x, loser_name.y))

-输出

   id winner_name loser_name
1  1         Jon      Henry
2  2         Bob     George
3  3       Lucas      Bagel
4  4      Marcus        Cat
5  5        Toad    Giraffe
6  6      Donkey     Monkey

如果 'test_2' 中有很多 'id_' 列,这里有一个选项

library(purrr)
library(stringr)
nm1 <- grep("id_", names(test_2), value = TRUE)
out <-  map(nm1, ~ test_2 %>% 
          select( -starts_with('id'), all_of(.x)) %>%
           left_join(test_1, ., by = setNames(.x, 'id'))) %>% 
           reduce(left_join, by = c("id", "winner_name")) 
out %>% 
  select(starts_with('loser')) %>% 
  split.default(str_remove(names(.), "\\..*")) %>% 
  map_dfc(~ invoke(coalesce, .)) %>% 
  bind_cols(test_1, .)

【讨论】:

  • 谢谢,阿克伦。问题是实际test_2 data.frame 中有数百列(loser_nameloser_countryloser_eloloser_recordloser_win_rate)等,所以我需要手动为每个指定名称和要合并的列。此外,因为我使用多个 id(id_1id_2id_3id_4 等)进行此顺序 id 匹配,所以我将拥有loser_name.xloser_name.yloser_name.z ,而且我事先不知道会有多少个后缀。
  • @Parseltongue 但是你只做两个left_join 对(即使有多个列?)
  • 嗯,对于我的特定用例,现在只有两个 left_join,但稍后我需要添加 left_join(test_2 %&gt;% select(id_3, loser_name), by = c('id' = 'id_3')) 并可能为 id_4 添加另一个 left_join。
  • @Parseltongue 你可以检查更新。我尝试创建更多的 id 列和失败者列,但它似乎可以工作
  • 干得好!非常感谢
【解决方案2】:

您可以尝试在 test_2 的融合(=长)版本上加入 test_1。仅当熔体 test_2 的顺序与您的 id 的搜索顺序相似时才有效。 现在你可以拥有 id_1, id_2, ..., id_100

library(data.table)
#make them data.tables
setDT(test_1)
setDT(test_2)

#join on molten set
test_1[melt(test_2, id.vars = "loser_name"), 
       loser_name := i.loser_name, 
       on = .(id = value)]

#    id winner_name loser_name
# 1:  1         Jon      Henry
# 2:  2         Bob     George
# 3:  3       Lucas      Bagel
# 4:  4      Marcus        Cat
# 5:  5        Toad    Giraffe
# 6:  6      Donkey     Monkey

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-12
    • 2021-11-27
    • 2021-09-15
    • 2021-03-10
    • 2016-06-12
    • 2021-09-10
    • 1970-01-01
    相关资源
    最近更新 更多