【问题标题】:Identifying Duplicate/Unique Teams (and Restructuring Data) in R在 R 中识别重复/独特的团队(和重组数据)
【发布时间】:2015-01-05 21:52:01
【问题描述】:

我有一个如下所示的数据集:

 Person Team
   1     30
   2     30
   3     30
   4     30
   11    40
   22    40
   1     50
   2     50
   3     50
   4     50
   15    60
   16    60
   17    60
   1     70
   2     70
   3     70
   4     70
   11    80
   22    80

我的总体目标是组织该团队标识代码,以便轻松查看哪些团队是彼此重复的,哪些团队是独一无二的。我想总结一下数据,使其看起来像这样:

 Team   Duplicate1  Duplicate2
  30        50          70
  40        80  
  60        

如您所见,团队 30、50 和 70 的成员相同,因此他们共享一行。同样,团队 40 和 80 具有相同的成员,因此他们共享一行。只有 60 队(在本例中)是唯一的。

在团队重复的情况下,我不在乎哪个团队 id 在哪个列中。此外,一个团队可能有 2 个以上的重复。团队规模从 2 人到 8 人不等。

【问题讨论】:

    标签: r


    【解决方案1】:

    这个答案给出了您要求的输出数据格式。我将重复的团队留在一个变量中,因为我认为这是处理任意数量的重复项的更好方法。

    require(dplyr)
    
    df %>%
      arrange(Team, Person) %>%   # this line is necessary in case the rest of your data isn't sorted
      group_by(Team) %>%
      summarize(players = paste0(Person, collapse = ",")) %>%
      group_by(players) %>%
      summarize(teams = paste0(Team, collapse = ",")) %>%
      mutate(
        original_team = ifelse(grepl(",", teams), substr(teams, 1, gregexpr(",", teams)[[1]][1]-1), teams),
        dup_teams = ifelse(grepl(",", teams), substr(teams, gregexpr(",", teams)[[1]][1]+1, nchar(teams)), NA)
      )
    

    结果:

    Source: local data frame [3 x 4]
    
       players    teams original_team dup_teams
    1  1,2,3,4 30,50,70            30     50,70
    2    11,22    40,80            40        80
    3 15,16,17       60            60        NA
    

    【讨论】:

    • 请先进行排序!我也在将其编辑到我的答案中。
    【解决方案2】:

    不完全是您想要的格式,但非常有用:

    # using MrFlick's data
    library(dplyr)
    dd %>% group_by(Team) %>%
        arrange(Person) %>%
        summarize(team.char = paste(Person, collapse = "_")) %>%
        group_by(team.char) %>%
        arrange(team.char, Team) %>%
        mutate(duplicate = 1:n())
    
    Source: local data frame [6 x 3]
    Groups: team.char
    
      Team team.char duplicate
    1   40     11_22         1
    2   80     11_22         2
    3   60  15_16_17         1
    4   30   1_2_3_4         1
    5   50   1_2_3_4         2
    6   70   1_2_3_4         3
    

    (在arrange(Person) 行中编辑,以防数据尚未排序,从@Reed 的回答中得到这个想法。)

    【讨论】:

      【解决方案3】:

      将其用于您的示例数据

      dd<-structure(list(Person = c(1L, 2L, 3L, 4L, 11L, 22L, 1L, 2L, 3L, 
      4L, 15L, 16L, 17L, 1L, 2L, 3L, 4L, 11L, 22L), Team = c(30L, 30L, 
      30L, 30L, 40L, 40L, 50L, 50L, 50L, 50L, 60L, 60L, 60L, 70L, 70L, 
      70L, 70L, 80L, 80L)), .Names = c("Person", "Team"), 
      class = "data.frame", row.names = c(NA, -19L))
      

      您可以尝试使用 table()/interaction() 来查找重复的组。例如

      tt <- with(dd, table(Team, Person))
      grp <- do.call("interaction", c(data.frame(unclass(tt)), drop=TRUE))
      split(rownames(tt), grp)
      

      返回

      $`1.1.1.1.0.0.0.0.0`
      [1] "30" "50" "70"
      
      $`0.0.0.0.0.1.1.1.0`
      [1] "60"
      
      $`0.0.0.0.1.0.0.0.1`
      [1] "40" "80"
      

      所以组“名称”实际上只是每个人的成员身份的指标。如果您愿意,可以使用setNames() 轻松重命名它们。但在这里它会瓦解适当的团队。

      【讨论】:

        【解决方案4】:

        另外两个基本 R 选项(虽然不完全是所需的输出):

        DF2 <- aggregate(Person ~ Team, DF, toString)
        > split(DF2$Team, DF2$Person)
        $`1, 2, 3, 4`
        [1] 30 50 70
        
        $`11, 22`
        [1] 40 80
        
        $`15, 16, 17`
        [1] 60
        

        或者

        ( DF2$DupeGroup <- as.integer(factor(DF2$Person)) )
          Team     Person DupeGroup
        1   30 1, 2, 3, 4         1
        2   40     11, 22         2
        3   50 1, 2, 3, 4         1
        4   60 15, 16, 17         3
        5   70 1, 2, 3, 4         1
        6   80     11, 22         2
        

        请注意,问题中显示的预期输出需要在某些列条目中添加 NA 或空字符串,因为在 data.frame 中,所有列必须具有相同的行数。正如您在一些答案中看到的那样,列表中的情况有所不同。


        第二个选项,但使用 data.table,因为 aggregate 对于大数据往往很慢:

        library(data.table)
        setDT(DF)[, toString(Person), by=Team][,DupeGroup := .GRP, by=V1][]
           Team         V1 DupeGroup
        1:   30 1, 2, 3, 4         1
        2:   40     11, 22         2
        3:   50 1, 2, 3, 4         1
        4:   60 15, 16, 17         3
        5:   70 1, 2, 3, 4         1
        6:   80     11, 22         2
        

        【讨论】:

          【解决方案5】:

          使用mgcv 包中的uniquecombs

          library(mgcv)
          library(magrittr) # for the pipe %>%
          
          # Using MrFlick's data
          team_names <- sort(unique(dd$Team))
          unique_teams <- with(dd, table(Team, Person)) %>% uniquecombs %>% attr("index")
          printout <- unstack(data.frame(team_names, unique_teams))
          
          > printout
          $`1`
          [1] 60
          
          $`2`
          [1] 40 80
          
          $`3`
          [1] 30 50 70
          

          现在您可以使用 this answer 之类的东西以表格形式打印它(请注意,这些组是按列排列的,而不是像您的问题那样按行排列):

          attributes(printout) <- list(names = names(printout)
                                       , row.names = 1:max(sapply(printout, length))
                                       , class = "data.frame")
          > printout
               1    2  3
          1   60   40 30
          2 <NA>   80 50
          3 <NA> <NA> 70
          Warning message:
          In format.data.frame(x, digits = digits, na.encode = FALSE) :
            corrupt data frame: columns will be truncated or padded with NAs
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-04-10
            • 1970-01-01
            • 1970-01-01
            • 2019-11-09
            • 1970-01-01
            • 2022-01-17
            • 1970-01-01
            • 2021-09-11
            相关资源
            最近更新 更多