【问题标题】:How to create a random matching between the rows of two data.tables (or data.frames)如何在两个 data.tables(或 data.frames)的行之间创建随机匹配
【发布时间】:2015-05-06 20:13:16
【问题描述】:

对于本例,我将使用 data.table 包。

假设你有一张教练桌

coaches <- data.table(CoachID=c(1,2,3), CoachName=c("Bob","Sue","John"), NumPlayers=c(2,3,0))
coaches
   CoachID CoachName NumPlayers
1:       1       Bob          2
2:       2       Sue          3
3:       3      John          0

还有一桌玩家

players <- data.table(PlayerID=c(1,2,3,4,5,6), PlayerName=c("Abe","Bart","Chad","Dalton","Egor","Frank"))
players
   PlayerID PlayerName
1:        1        Abe
2:        2       Bart
3:        3       Chad
4:        4     Dalton
5:        5       Egor
6:        6      Frank

您希望将每个教练与一组球员进行匹配

  • 与每位教练关联的球员人数由 NumPlayers 字段定义
  • 没有两个教练与同一个球员联系在一起
  • 球员和教练随机匹配

你是怎么做到的?

exampleResult <- data.table(CoachID=c(1,1,2,2,2,3), PlayerID=c(3,1,2,5,6,NA))
exampleResult

   CoachID PlayerID
1:       1        3
2:       1        1
3:       2        2
4:       2        5
5:       2        6
6:       3       NA

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    您可以从玩家 ID 中进行采样而不用替换,从而获取您需要的玩家总数:

    set.seed(144)
    (selections <- sample(players$PlayerID, sum(coaches$NumPlayers)))
    # [1] 1 4 3 2 6
    

    每个玩家都有相同的概率被包含在selections 中,并且该向量的顺序是随机的。因此,您可以将这些球员分配到每个教练位置:

    data.frame(CoachID=rep(coaches$CoachID, coaches$NumPlayers),
               PlayerID=selections)
    #   CoachID PlayerID
    # 1       1        1
    # 2       1        4
    # 3       2        3
    # 4       2        2
    # 5       2        6
    

    如果您想为没有球员选择的任何教练设置NA 值,您可以执行以下操作:

    rbind(data.frame(CoachID=rep(coaches$CoachID, coaches$NumPlayers),
                     PlayerID=selections),
          data.frame(CoachID=coaches$CoachID[coaches$NumPlayers==0],
                     PlayerID=rep(NA, sum(coaches$NumPlayers==0))))
    #   CoachID PlayerID
    # 1       1        1
    # 2       1        4
    # 3       2        3
    # 4       2        2
    # 5       2        6
    # 6       3       NA
    

    【讨论】:

      【解决方案2】:

      可以这么说,了解双方的需求和供应:

      demand <- with(coaches,rep(CoachID,NumPlayers))
      supply <- players$PlayerID
      

      那我就……

      randmatch <- function(demand,supply){
        n_demand  <- length(demand)
        n_supply  <- length(supply)
        n_matches <- min(n_demand,n_supply)
      
        if (n_demand >= n_supply) 
          data.frame(d=sample(demand,n_matches),s=supply)
        else 
          data.frame(d=demand,s=sample(supply,n_matches))
      }
      

      例子:

      set.seed(1)
      randmatch(demand,supply)    # some players unmatched, OP's example
      randmatch(rep(1:3,1:3),1:4) # some coaches unmatched 
      

      不过,我不确定这是否是 OP 想要涵盖的情况。


      对于 OP 想要的输出...

      m <- randmatch(demand,supply)
      merge(m,coaches,by.x="d",by.y="CoachID",all=TRUE)
      #   d  s CoachName NumPlayers
      # 1 1  2       Bob          2
      # 2 1  6       Bob          2
      # 3 2  3       Sue          3
      # 4 2  4       Sue          3
      # 5 2  1       Sue          3
      # 6 3 NA      John          0
      

      同样...

      merge(m,players,by.x="s",by.y="PlayerID",all=TRUE)
      #   s  d PlayerName
      # 1 1  2        Abe
      # 2 2  1       Bart
      # 3 3  2       Chad
      # 4 4  2     Dalton
      # 5 5 NA       Egor
      # 6 6  1      Frank
      

      【讨论】:

        【解决方案3】:

        这是使用简单 dplyr 的答案。首先选择教练需求,然后抽样球员需求,最后全部绑定。

        library(dplyr)
        
        set.seed(1234)
        
        coach_needs <- coaches %>%
          group_by( CoachID ) %>%
          do( sample_n(., size=.$NumPlayers, replace=TRUE) ) %>%
          select( -CoachID ) %>% ungroup()
        
        player_needs <- players %>%
          sample_n( size = nrow(coach_needs))
        
        result <- cbind(coach_needs, player_needs)
        
        result
        

        这给了我:

           CoachID CoachName NumPlayers PlayerID PlayerName
        1:       1       Bob          2        4     Dalton
        2:       1       Bob          2        1        Abe
        3:       2       Sue          3        5       Egor
        4:       2       Sue          3        2       Bart
        5:       2       Sue          3        3       Chad
        

        更新:如果有NumPlayer == 0 的教练需要NAs,那么这很简单:

        result <- cbind(coach_needs, player_needs) %>%
          rbind( coaches %>% filter(NumPlayers == 0), fill=TRUE )
        
        result
        

        这给了我这个:

           CoachID CoachName NumPlayers PlayerID PlayerName
        1:       1       Bob          2        4     Dalton
        2:       1       Bob          2        1        Abe
        3:       2       Sue          3        5       Egor
        4:       2       Sue          3        2       Bart
        5:       2       Sue          3        3       Chad
        6:       3      John          0       NA         NA
        

        【讨论】:

        • 您的最终结果不是 PlayerID 6,而是 NA
        • @Frank,是的。这是因为 CoachID 3 (John) 的 NumPlayers == 0,因此不应为他分配任何人。
        猜你喜欢
        • 2016-02-28
        • 1970-01-01
        • 1970-01-01
        • 2013-02-14
        • 1970-01-01
        • 2013-11-22
        • 1970-01-01
        • 1970-01-01
        • 2013-02-24
        相关资源
        最近更新 更多