【问题标题】:Get number of same individuals for different groups获取不同组的相同个体数量
【发布时间】:2015-11-09 16:27:47
【问题描述】:

我有一个数据集,其中包含可以属于多个组的个人 (ID)。

例子:

library(data.table)
DT <- data.table(
  ID = rep(1:5, c(3:1, 2:3)),
  Group = c("A", "B", "C", "B",
            "C", "A", "A", "C",
            "A", "B", "C")
)
DT
#     ID Group
#  1:  1     A
#  2:  1     B
#  3:  1     C
#  4:  2     B
#  5:  2     C
#  6:  3     A
#  7:  4     A
#  8:  4     C
#  9:  5     A
# 10:  5     B
# 11:  5     C

我想知道两组相同个体的总和。

结果应该是这样的:

  Group.1    Group.2    Sum
    A           B        2
    A           C        3
    B           C        3

其中总和表示两组共有的个体数量。

【问题讨论】:

  • 另见post。您可以将该答案与as.data.frame(as.table())complete.cases 结合使用。
  • out &lt;- with(dt, crossprod(table(ID, Group))); out[lower.tri(out, diag=TRUE)] &lt;- NA; na.omit(as.data.frame.table(out)) 来自上面的链接
  • @alexis_laz 和 @rawr 也是不错的解决方案!谢谢。

标签: r sum data.table


【解决方案1】:

这是我的版本:

# size-1 IDs can't contribute; skip
DT[ , if (.N > 1) 
  # simplify = FALSE returns a list;
  #   transpose turns the 3-length list of 2-length vectors
  #   into a length-2 list of 3-length vectors (efficiently)
  transpose(combn(Group, 2L, simplify = FALSE)), by = ID
  ][ , .(Sum = .N), keyby = .(Group.1 = V1, Group.2 = V2)]

有输出:

#    Group.1 Group.2 Sum
# 1:       A       B   2
# 2:       A       C   3
# 3:       B       C   3

【讨论】:

  • 或者,您可以使用DT[ , as.data.table(combinat::combn2(unique(Group))), by = ID][, .(Sum = .N), keyby = .(Group.1 = V1, Group.2 = V2)]combinat::combn2().N &lt; 2 的情况下不会抛出错误,并且不需要transpose()
  • @Uwe cool :) 看一下combn2source,我发现它只是在内部执行t,所以我猜transpose 会更有效率:) ( ps 最后编辑于 1994 年!?:o)
  • 基准比较会非常有趣(如果尚未在 SO 上发布)。
  • @Uwe 也有同样的想法,但没有立即想到基准测试的方法,所以我放弃了:p
【解决方案2】:

截至 1.9.8 版(2016 年 11 月 25 日在 CRAN 上),data.table 已获得执行非等值连接的能力。因此,可以使用 self non-equi join

library(data.table) # v1.9.8+
setDT(DT)[, Group:= factor(Group)]
DT[DT, on = .(ID, Group < Group), nomatch = 0L, .(ID, x.Group, i.Group)][
  , .N, by = .(x.Group, i.Group)]
   x.Group i.Group N
1:       A       B 2
2:       A       C 3
3:       B       C 3

解释

ID, Group &lt; Group 上的非等连接combn()data.table 版本(但按组应用):

DT[DT, on = .(ID, Group < Group), nomatch = 0L, .(ID, x.Group, i.Group)]
   ID x.Group i.Group
1:  1       A       B
2:  1       A       C
3:  1       B       C
4:  2       B       C
5:  4       A       C
6:  5       A       B
7:  5       A       C
8:  5       B       C

【讨论】:

  • 很好地使用自我NE加入!非常 SQL :p ;感谢您让我注意到这个问题,我的回答很草率,我有机会改进它:)
  • @MichaelChirico 我正在尝试通过data.table 学习 SQL ;-)
  • 那么,你的下一个任务是在 SQL 中实现我自己的方法:p
【解决方案3】:

我们使用相同的数据集on'ID'进行自连接,对'Group'列不同的行进行子集化,获取nrows(.N),按'Group'列分组,对'进行排序使用 pmin/pmax 逐行分组 Group.1' 和 'Group.2' 列,并获取 unique 的值 'N'。

 library(data.table)#v1.9.6+
 DT[DT, on='ID', allow.cartesian=TRUE][Group!=i.Group, .N ,.(Group, i.Group)][, 
      list(Sum=unique(N)) ,.(Group.1=pmin(Group, i.Group), Group.2=pmax(Group, i.Group))]

#   Group.1 Group.2 Sum
#1:       A       B   2
#2:       A       C   3
#3:       B       C   3

或者正如@MichaelChirico 和@Frank 在cmets 中提到的,我们可以将'Group' 转换为factor 类,基于as.integer(Group) &lt; as.integer(i.Group) 对行进行子集化,按'Group'、'i.Group' 分组并得到nrow (.N)

DT[, Group:= factor(Group)]
DT[DT, on='ID', allow.cartesian=TRUE][as.integer(Group) < as.integer(i.Group), .N, 
                       by = .(Group.1= Group, Group.2= i.Group)] 

【讨论】:

  • 谢谢!正是我想要的!
  • @akrun 我喜欢您的方法,但我会执行以下操作:首先将Group 转换为factor(如果还没有),然后只需将as.integer(Group)&lt;as.integer(i.Group) 添加到逻辑语句中第二个[ ] 对。那你就不用担心pmax这个烂摊子了。
  • 是的,我同意迈克尔的观点。我会做dt[dt, on="ID", allow.cartesian=TRUE][Group &lt; i.Group, .N, by=.(G1=Group,G2=i.Group)] 假设人们没有在同一组中多次列出(因此不需要uniqueN)。
【解决方案4】:

上面的答案很好。 如果您或其他人有兴趣,可以使用 dplyr 替代。

library(dplyr)

cmb = combn(unique(dt$Group),2)

data.frame(g1 = cmb[1,],
           g2 = cmb[2,]) %>%
  group_by(g1,g2) %>%
  summarise(l=length(intersect(DT[DT$Group==g1,]$ID,
                               DT[DT$Group==g2,]$ID)))

    #       g1     g2     l
    #    (fctr) (fctr) (int)
    # 1      A      B     2
    # 2      A      C     3
    # 3      B      C     3

【讨论】:

    【解决方案5】:

    另一个解决方案(基础 R):

    tmp <- split(DT, DT[, 'Group'])
    ans <- apply(combn(LETTERS[1 : 3], 2), 2, FUN = function(ind){
                out <- length(intersect(tmp[[ind[1]]][, 1], tmp[[ind[2]]][, 1]))
                c(group1 = ind[1], group2 = ind[2], sum_ = out) 
                    }
                )
    
    data.frame(t(ans))
    
    #  group1 group2 sum_
    #1      A      B    2
    #2      A      C    3
    #3      B      C    3
    

    首先将数据拆分为组列表,然后使用length(intersect(...为两组的每个唯一配对组合查看他们有多少共同主题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多