【问题标题】:Select rows with common ids in grouped data frame在分组数据框中选择具有共同 id 的行
【发布时间】:2019-11-17 14:59:25
【问题描述】:

我正在寻找以下问题的更简单的解决方案。这是我的设置:

test <- tibble::tribble(
  ~group_name, ~id_name, ~varA, ~varB,
     "groupA",   "id_1",     1,   "a",
     "groupA",   "id_2",     4,   "f",
     "groupA",   "id_3",     5,   "g",
     "groupA",   "id_4",     6,   "x",
     "groupA",   "id_4",     6,   "h",
     "groupB",   "id_1",     2,   "s",
     "groupB",   "id_2",    13,   "y",
     "groupB",   "id_4",    14,   "t",
     "groupC",   "id_1",     3,   "d",
     "groupC",   "id_2",     7,   "j",
     "groupC",   "id_3",     8,   "k",
     "groupC",   "id_4",     9,   "l",
     "groupC",   "id_5",     0,   "o",
     "groupC",   "id_6",    12,   "u"
  )

我只想选择 id_name 中所有组共有的 group_name 中的元素 - 即删除所有组中不存在的 id 的行。我的实际数据很大(200k 行),有 4-20 个组(我事先不知道组的数量,因此解决方案必须适用于任意数量的组)。每个组中的id_name 不是唯一的。期望的结果是:

test_result <- tibble::tribble(
  ~group_name, ~id_name, ~varA, ~varB,
     "groupA",   "id_1",     1,   "a",
     "groupA",   "id_2",     4,   "f",
     "groupA",   "id_4",     6,   "x",
     "groupA",   "id_4",     6,   "h",
     "groupB",   "id_1",     2,   "s",
     "groupB",   "id_2",    13,   "y",
     "groupB",   "id_4",    14,   "t",
     "groupC",   "id_1",     3,   "d",
     "groupC",   "id_2",     7,   "j",
     "groupC",   "id_4",     9,   "l",
  )

(删除至少一组中不存在 id 的行)。理想情况下,我不希望我的输出在最后加入列。我想“简单地”删除任何一组中缺少的行,但保持数据框的形状。

而且我知道我可以从每个组中提取所有 id,然后将它们全部相交以获得所有组中存在的唯一 id 列表,然后仅过滤这些 ID 的主数据框。但这听起来像是很多工作;-)

任何提示将不胜感激。

【问题讨论】:

    标签: r dplyr tidyverse


    【解决方案1】:

    在基础R中,我们可以通过group_namesplitid_name找到共同的id's然后subset

    subset(test, id_name %in% Reduce(intersect, split(id_name, group_name)))
    
    #   group_name id_name  varA varB 
    #   <chr>      <chr>   <dbl> <chr>
    # 1 groupA     id_1        1 a    
    # 2 groupA     id_2        4 f    
    # 3 groupA     id_4        6 x    
    # 4 groupA     id_4        6 h    
    # 5 groupB     id_1        2 s    
    # 6 groupB     id_2       13 y    
    # 7 groupB     id_4       14 t    
    # 8 groupC     id_1        3 d    
    # 9 groupC     id_2        7 j    
    #10 groupC     id_4        9 l    
    

    tidyverse中使用类似的概念,它会是

    library(tidyverse)
    test %>%
      filter(id_name %in% (test %>%
                             group_split(group_name)  %>%
                             map(~pull(., id_name)) %>%
                             reduce(intersect)))
    

    【讨论】:

    • 这很快!惊人的。这些嵌入式管道肯定很有帮助。
    【解决方案2】:

    您可以通过 group_name 将 id_name 的出现列表:

    table(test$group_name,test$id_name)
    

    如果每个组中都存在 id_name,我们希望列的所有条目都 > 0。我们可以使用 >0 和 colMeans 的组合来简化这个逻辑:

    keep = names(which(colMeans(table(test$group_name,test$id_name)>0)==1))
    

    使用这个:

    test[test$id_name %in% keep,]
    

    【讨论】:

    • 是的,这更像是我认为我可以做到的方式 - 如果我想保持任意数量的组共有的 ID,此解决方案很有用。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-08
    • 1970-01-01
    • 1970-01-01
    • 2023-01-11
    相关资源
    最近更新 更多