【问题标题】:R - count all combinationsR - 计算所有组合
【发布时间】:2016-03-22 14:34:20
【问题描述】:

我想计算 data.frame 中的所有组合。

数据是这样的

   9 10 11 12
1  1  1  1  1
2  0  0  0  0
3  0  0  0  0
4  1  1  1  1
5  1  1  1  1
6  0  0  0  0
7  1  0  0  1
8  1  0  0  1
9  1  1  1  1
10 1  1  1  1

我想要的输出很简单

comb     n 
1 1 1 1  5
0 0 0 0  3 
1 0 0 1  2 

你知道任何简单的功能吗?

谢谢

dt = structure(list(`9` = c(1, 0, 0, 1, 1, 0, 1, 1, 1, 1), `10` = c(1, 
0, 0, 1, 1, 0, 0, 0, 1, 1), `11` = c(1, 0, 0, 1, 1, 0, 0, 0, 
1, 1), `12` = c(1, 0, 0, 1, 1, 0, 1, 1, 1, 1)), .Names = c("9", 
"10", "11", "12"), class = "data.frame", row.names = c(NA, -10L
))

【问题讨论】:

  • 如果您的数据确实是二进制的,您可以通过使用 (as.matrix(dt) %*% 2 ^ (0:(length(dt) - 1)))[, 1L] 将每一行映射到唯一的十进制数来避免任何数据库操作,然后使用 duplcatedtabulate 从那里继续以匹配您想要的输出.看到这个QA

标签: r count combinations


【解决方案1】:

我们可以使用data.tabledplyr。这些非常有效。我们将 'data.frame' 转换为 'data.table' (setDT(dt)),按 'dt' (names(dt)) 的所有列分组,我们得到 nrow (.N) 作为 'Count'

library(data.table)
setDT(dt)[,list(Count=.N) ,names(dt)]

或者我们可以使用类似的方法,使用dplyr

library(dplyr)
names(dt) <- make.names(names(dt))
dt %>%
   group_by_(.dots=names(dt)) %>%
   summarise(count= n())

基准测试

如果有人想查看一些指标(以及早先备份我的声明 (efficient!)),

set.seed(24)
df1 <- as.data.frame(matrix(sample(0:1, 1e6*6, replace=TRUE), ncol=6))

akrunDT <-  function() {
  as.data.table(df1)[,list(Count=.N) ,names(df1)]
 }

akrunDplyr <- function() {
  df1 %>%
    group_by_(.dots=names(df1)) %>%
    summarise(count= n())
}

cathG <- function() {
 aggregate(cbind(n = 1:nrow(df1))~., df1, length)
  }

docendoD <- function() {
  as.data.frame(table(comb = do.call(paste, df1)))
}

deena <- function() {
   table(apply(df1, 1, paste, collapse = ","))
}

这里是microbenchmark 结果

library(microbenchmark)
microbenchmark(akrunDT(), akrunDplyr(), cathG(), docendoD(),  deena(),
  unit='relative', times=20L)
#   Unit: relative
#        expr       min        lq      mean   median        uq        max neval  cld
#     akrunDT()  1.000000  1.000000  1.000000  1.00000  1.000000  1.0000000    20     a   
#  akrunDplyr()  1.512354  1.523357  1.307724  1.45907  1.365928  0.7539773    20     a   
#       cathG() 43.893946 43.592062 37.008677 42.10787 38.556726 17.9834245    20    c 
#    docendoD() 18.778534 19.843255 16.560827 18.85707 17.296812  8.2688541    20    b  
#       deena() 90.391417 89.449547 74.607662 85.16295 77.316143 34.6962954    20    d

【讨论】:

  • 感谢@akrun,但dplyr 解决方案不起作用。我得到9 10 11 12 count 1 9 10 11 12 10
  • @giacomoV 我猜这是基于以数字开头的不寻常的列名。
  • @giacomoV 请检查更新后的代码。它现在应该可以工作了。
  • btw akrun,我确实支持你,我想找到一个基本的 R sol,但我确实非常喜欢 data.table 并且相信它更有效 ;-) 我认为它值得更多的投票(没有关注所有关于投票的讨论,只是看到了最后的 cmets)
  • @Tjebo 这是group_by_ 仍然存在时的旧代码。现在,_ 已弃用
【解决方案2】:

aggregate 的基本 R 解决方案:

aggregate(seq(nrow(dt))~., data=dt, FUN=length)
#  9 10 11 12 seq(nrow(dt))
#1 0  0  0  0             3
#2 1  0  0  1             2
#3 1  1  1  1             5

编辑

要让 colnames 更符合您的输出,您可以这样做:

`colnames<-`(aggregate(seq(nrow(dt))~., data=dt, FUN=length), c("c", "o", "m", "b", "n"))
#  c o m b n
#1 0 0 0 0 3
#2 1 0 0 1 2
#3 1 1 1 1 5

或者,更短的:

aggregate(cbind(n = 1:nrow(dt))~., dt, length)
#  9 10 11 12 n
#1 0  0  0  0 3
#2 1  0  0  1 2
#3 1  1  1  1 5

【讨论】:

    【解决方案3】:

    您可以尝试仅使用基本 R 的以下方法:

    as.data.frame(table(comb = do.call(paste, dt)))
    #     comb Freq
    #1 0 0 0 0    3
    #2 1 0 0 1    2
    #3 1 1 1 1    5
    

    【讨论】:

    • 要完全匹配所需的输出(将列计数为“n”),您可以将代码稍微调整为as.data.frame(table(comb = do.call(paste, dt)), responseName = "n")
    【解决方案4】:

    也许也是这样:table(apply(dt, 1, paste, collapse = ","))

    【讨论】:

      【解决方案5】:

      也在基础 R 中:

      使用unique.matrix 获取唯一组合列表。

      uncs <- unique.matrix(as.matrix(df), MARGIN = 1)
      

      然后进行比较和计数:

      cnts <- colSums(apply(uncs, 1, function(r) apply(dt, 1, function(r2) all(r == r2))))
      cbind(comb = apply(uncs, 1, paste), n = cnts)
      

      【讨论】:

        【解决方案6】:

        使用 group_by_all() 可以更轻松地完成上述 dplyr 解决方案...

        dt %>% group_by_all %>% count
        

        ...据我了解,它已被 cross() 方法取代。加上一点排序,你得到:

        dt %>% group_by(across()) %>% count %>% arrange(desc(n))
        
        > dt %>% group_by(across()) %>% count %>% arrange(desc(n))
        # A tibble: 3 x 5
        # Groups:   9, 10, 11, 12 [3]
            `9`  `10`  `11`  `12`     n
          <dbl> <dbl> <dbl> <dbl> <int>
        1     1     1     1     1     5
        2     0     0     0     0     3
        3     1     0     0     1     2
        

        如果您愿意,可以将其转换为矩阵。

        【讨论】:

          猜你喜欢
          • 2016-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多