【问题标题】:Count occurrences per data.table row计算每个 data.table 行的出现次数
【发布时间】:2020-02-20 12:25:11
【问题描述】:

我的 data.table 太大而无法在 RAM 中融化/投射(数百万行)。

从中我需要提取每行没有重复值的行。可以保留重复的零和 NA。

在这个 MWE 中,“重复”列是我要计算的。

DT <- data.table(C1=c(0L, 7L, 0L, 0L),
                 C2=c(0L, 0L, 0L, 4L),
                 C3=c(2L, 0L, 2L, 3L),
                 C4=c(0L, NA_integer_, 2L, 6L),
                 C5=c(1L, 3L, 1L, 1L),
                 c6=c(0L, 4L, 2L, 4L),
                 Duplicates=c(FALSE, FALSE, TRUE, TRUE))

DT.wanted <- DT[Duplicates==FALSE, ]

列数不同。

编辑:这是一个 for 循环示例。太慢了。

DT <- data.table(C1=c(0L, 7L, 0L, 0L),
                 C2=c(0L, 0L, 0L, 4L),
                 C3=c(2L, 0L, 2L, 3L),
                 C4=c(0L, NA_integer_, 2L, 6L),
                 C5=c(1L, 3L, 1L, 1L),
                 c6=c(0L, 4L, 2L, 4L))
DT[, Duplicates:=FALSE]
for (i in 1L:nrow(DT)){
  dt.i <- data.table(table(as.integer(DT[i, ])))
  if (max(dt.i[V1>0, N])>1) DT[i, Duplicates:=TRUE]
}

【问题讨论】:

  • 我必须使用 data.table 还是可以使用其他 pkgs/base R?
  • 据我所知,只有 data.table 可以做到这一点而无需重写表。没有足够的内存。

标签: r data.table


【解决方案1】:

这里是另一个 data.table 的事情......

DT[ lapply( data.table::transpose( DT ), 
            function(x) anyDuplicated( x[!is.na(x) & !x == 0] ) ) == 0, ]

不确定它是如何在内存方面保持稳定的.. 但它比 for 循环快得多..

基准测试

# Unit: microseconds
#      expr     min         lq      mean    median         uq       max neval
#      loop 11764.5 12164.2010 13987.589 12725.601 13775.0010 25885.601   100
# transpose   524.5   550.3515   592.633   566.601   597.4515  1116.201   100

基准代码

mydata <- data.table(C1=c(0L, 7L, 0L, 0L),
                 C2=c(0L, 0L, 0L, 4L),
                 C3=c(2L, 0L, 2L, 3L),
                 C4=c(0L, NA_integer_, 2L, 6L),
                 C5=c(1L, 3L, 1L, 1L),
                 C6=c(0L, 4L, 2L, 4L))

microbenchmark::microbenchmark(
 loop = {
   DT <- copy(mydata)   
   DT[, Duplicates:=FALSE]
   for (i in 1L:nrow(DT)){
     dt.i <- data.table(table(as.integer(DT[i, ])))
     if (max(dt.i[V1>0, N])>1) DT[i, Duplicates:=TRUE]
   }
 },
 transpose = {
   DT <- copy(mydata)
   DT[ lapply( transpose(DT), function(x) anyDuplicated( x[!is.na(x) & ! x == 0] ) ) == 0, ]
 },times = 100L )

【讨论】:

  • 这肯定也会导致 RAM 问题,但您可以将此解决方案与我的循环结合使用。也许将 data.table 分成 10 个部分并逐步应用此解决方案。
  • 是的,仍然是 RAM 密集型的。感谢拆分 data.table 的提示
【解决方案2】:

这是一个解决方案,应该还是会稍微快一点(~3x)。

foo_dup <- function(x) {
  anyDuplicated(x[!is.na(x) & x != 0L]) != 0L
}

cols <- paste0("C", 1:6)
for (i in 1L:nrow(DT)) {
  set(DT, i, "Duplicates", foo_dup(unlist(DT[i, cols])) )
}

DT
   C1 C2 C3 C4 C5 C6 Duplicates
1:  0  0  2  0  1  0      FALSE
2:  7  0  0 NA  3  4      FALSE
3:  0  0  2  2  1  2       TRUE
4:  0  4  3  6  1  4       TRUE

PS。我将示例数据名称 c6 更改为 C6

【讨论】:

    【解决方案3】:

    这是另一种使用基础 R + data.table::rowidv 的方法(比选择的 1mio 行数据解决方案快大约 8 倍):

    mSD <- as.matrix(DT)
    mSD[mSD==0L] <- NA_integer_
    m <- cbind.data.frame(ROW=rep(1L:nrow(mSD), ncol(mSD)), COL=as.vector(mSD))
    mcc <- m[complete.cases(m),]
    dup <- unique(mcc$ROW[rowidv(mcc) > 1L])
    DT[-dup]
    

    计时码:

    library(data.table)
    set.seed(0L)
    nr <- 1e5L
    nc <- 6L
    dat <- as.data.table(matrix(sample(c(0L:7L, NA_integer_), nr*nc, TRUE), nrow=nr))
    DT0 <- copy(dat)
    DT1 <- copy(dat)
    DT2 <- copy(dat)
    
    mtd0 <- function(DT) {
        DT[ lapply(transpose(DT),
            function(x) anyDuplicated( x[!is.na(x) & !x == 0] ) ) == 0, ]
    }
    
    mtd1 <- function(DT) {
        foo_dup <- function(x) {
            anyDuplicated(x[!is.na(x) & x != 0L]) != 0L
        }
    
        cols <- copy(names(DT))
        for (i in 1L:nrow(DT)) {
            set(DT, i, "Duplicates", foo_dup(unlist(DT[i, ..cols])) )
        }
    }
    
    mtd2 <- function(DT) {
    
        mSD <- as.matrix(DT)
        mSD[mSD==0L] <- NA_integer_
        m <- cbind.data.frame(ROW=rep(1L:nrow(mSD), ncol(mSD)), COL=as.vector(mSD))
        mcc <- m[complete.cases(m),]
        dup <- unique(mcc$ROW[rowidv(mcc) > 1L])
    
        DT[-dup]
    }
    
    microbenchmark::microbenchmark(times=1L, mtd0(DT0), mtd1(DT1), mtd2(DT2))
    

    时间安排:

    Unit: milliseconds
          expr          min           lq         mean       median           uq          max neval
     mtd0(DT0)    999.47159    999.47159    999.47159    999.47159    999.47159    999.47159     1
     mtd1(DT1) 110725.50423 110725.50423 110725.50423 110725.50423 110725.50423 110725.50423     1
     mtd2(DT2)     64.79497     64.79497     64.79497     64.79497     64.79497     64.79497     1
    

    1e6 行数据的时序:

    Unit: seconds
          expr      min       lq     mean   median       uq      max neval
     mtd0(DT0) 9.902526 9.902526 9.902526 9.902526 9.902526 9.902526     1
     mtd2(DT2) 1.234894 1.234894 1.234894 1.234894 1.234894 1.234894     1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-15
      • 2021-01-14
      • 2018-10-24
      • 1970-01-01
      • 1970-01-01
      • 2015-01-15
      • 1970-01-01
      • 2011-05-12
      相关资源
      最近更新 更多