14

我需要将许多大表添加到现有表中,因此我将 rbind 与优秀的包 data.table 一起使用。但是后来的一些表比原来的表有更多的列(需要包括在内)。data.table 是否有 rbind.fill 的等价物?

library(data.table)

aa <- c(1,2,3)
bb <- c(2,3,4)
cc <- c(3,4,5)

dt.1 <- data.table(cbind(aa, bb))
dt.2 <- data.table(cbind(aa, bb, cc))

dt.11 <- rbind(dt.1, dt.1)  # Works, but not what I need
dt.12 <- rbind(dt.1, dt.2)  # What I need, doesn't work
dt.12 <- rbind.fill(dt.1, dt.2)  # What I need, doesn't work either

我需要在拥有所有表之前开始 rbinding,因此无法知道将来会调用哪些新列。缺失的数据可以用 NA 填充。

4

5 回答 5

14

由于v1.9.2,data.table的 rbind 函数获得了fill参数。从?rbind.data.table文档:

如果 TRUE 用 NA 填充缺失的列。默认情况下为假。当为 TRUE 时,use.names 必须为 TRUE,并且输入列表的所有项目都必须具有非空的列名。

因此您可以这样做(大约在 v1.9.6 之前):

data.table::rbind(dt.1, dt.2, fill=TRUE) 
#    aa bb cc
# 1:  1  2 NA
# 2:  2  3 NA
# 3:  3  4 NA
# 4:  1  2  3
# 5:  2  3  4
# 6:  3  4  5

v1.9.6 的更新:

这现在可以直接工作:

rbind(dt.1, dt.2, fill=TRUE)
#    aa bb cc
# 1:  1  2 NA
# 2:  2  3 NA
# 3:  3  4 NA
# 4:  1  2  3
# 5:  2  3  4
# 6:  3  4  5
于 2014-10-10T07:35:28.463 回答
5

这是一种将更新缺失列的方法

rbind.missing <- function(A, B) { 

  cols.A <- names(A)
  cols.B <- names(B)

  missing.A <- setdiff(cols.B,cols.A)
  # check and define missing columns in A
  if(length(missing.A) > 0L){
   # .. means "look up one level"
   class.missing.A <- lapply(B[, ..missing.A], class)
   nas.A <- lapply(class.missing.A, as, object = NA)
   A[,c(missing.A) := nas.A]
  }
  # check and define missing columns in B
  missing.B <- setdiff(names(A), cols.B)
  if(length(missing.B) > 0L){
    class.missing.B <- lapply(A[, ..missing.B], class)
    nas.B <- lapply(class.missing.B, as, object = NA)
    B[,c(missing.B) := nas.B]
  }
  # reorder so they are the same
  setcolorder(B, names(A))
  rbind(A, B)

}

rbind.missing(dt.1,dt.2)

##    aa bb cc
## 1:  1  2 NA
## 2:  2  3 NA
## 3:  3  4 NA
## 4:  1  2  3
## 5:  2  3  4
## 6:  3  4  5

这对于许多或大型 data.tables 来说效率不高,因为它一次只能工作两个。

于 2013-02-22T05:05:30.443 回答
4

答案很棒,但看起来,这里建议了一些功能例如 plyr::rbind.fill 和 gtools::smartbind 似乎对我来说非常有效。

于 2015-10-09T19:02:30.380 回答
2

基本概念是在两个方向上添加缺失的列:从运行master表到newTable另一个方向并返回。

正如@menl 在评论中指出的那样,简单地分配一个 NA 是一个问题,因为这将使整个列成为class logical.

一种解决方案是强制使用单一类型(即as.numeric(NA))的所有列,但这太严格了。

相反,我们需要分析每个新列的类别。然后我们可以使用as(NA, cc)_(cc作为类) 作为我们将分配给新列的向量。我们将其包装在 the 的 语句lapplyRHS并使用to 分配。eval(columnName)LHS

然后我们可以将它包装在一个函数中并使用 S3 方法,以便我们可以简单地调用

rbindFill(A, B)

下面是函数。

rbindFill.data.table <- function(master, newTable)  {
# Append newTable to master

    # assign to Master
    #-----------------#
      # identify columns missing
      colMisng     <- setdiff(names(newTable), names(master))

      # if there are no columns missing, move on to next part
      if (!identical(colMisng, character(0)))  {
           # identify class of each
            colMisng.cls <- sapply(colMisng, function(x) class(newTable[[x]]))

            # assign to each column value of NA with appropriate class 
            master[ , eval(colMisng) := lapply(colMisng.cls, function(cc) as(NA, cc))]
          }

    # assign to newTable
    #-----------------#
      # identify columns missing
      colMisng     <- setdiff(names(master), names(newTable))

      # if there are no columns missing, move on to next part
      if (!identical(colMisng, character(0)))  {
        # identify class of each
        colMisng.cls <- sapply(colMisng, function(x) class(master[[x]]))

        # assign to each column value of NA with appropriate class 
        newTable[ , eval(colMisng) := lapply(colMisng.cls, function(cc) as(NA, cc))]
      }

    # reorder columns to avoid warning about ordering
    #-----------------#
      colOrdering <- colOrderingByOtherCol(newTable, names(master))
      setcolorder(newTable,  colOrdering)

    # rbind them! 
    #-----------------#
      rbind(master, newTable)
  }

  # implement generic function
  rbindFill <- function(x, y, ...) UseMethod("rbindFill")


示例用法:

    # Sample Data: 
    #--------------------------------------------------#
    A  <- data.table(a=1:3, b=1:3, c=1:3)
    A2 <- data.table(a=6:9, b=6:9, c=6:9)
    B  <- data.table(b=1:3, c=1:3, d=1:3, m=LETTERS[1:3])
    C  <- data.table(n=round(rnorm(3), 2), f=c(T, F, T), c=7:9)
    #--------------------------------------------------#

    # Four iterations of calling rbindFill
    master <- rbindFill(A, B)
    master <- rbindFill(master, A2)
    master <- rbindFill(master, C)

    # Results:
    master
    #      a  b c  d  m     n     f
    #  1:  1  1 1 NA NA    NA    NA
    #  2:  2  2 2 NA NA    NA    NA
    #  3:  3  3 3 NA NA    NA    NA
    #  4: NA  1 1  1  A    NA    NA
    #  5: NA  2 2  2  B    NA    NA
    #  6: NA  3 3  3  C    NA    NA
    #  7:  6  6 6 NA NA    NA    NA
    #  8:  7  7 7 NA NA    NA    NA
    #  9:  8  8 8 NA NA    NA    NA
    # 10:  9  9 9 NA NA    NA    NA
    # 11: NA NA 7 NA NA  0.86  TRUE
    # 12: NA NA 8 NA NA -1.15 FALSE
    # 13: NA NA 9 NA NA  1.10  TRUE
于 2013-02-22T03:42:25.090 回答
2

插入缺失列(具有正确的类型和 NA)的另一种方法是使用具有第二个 data.table 结构的空 data.table 插入merge()第一个 data.table。这节省了在用户函数中引入错误的可能性(我知道它比我自己的代码更可靠;))。从上面使用 mnel 的表格,执行类似于下面的代码的操作。AA2[0]merge()

此外,rbindlist()在处理data.tables.

定义表(与上面 mnel 的代码相同):

library(data.table)
A  <- data.table(a=1:3, b=1:3, c=1:3)
A2 <- data.table(a=6:9, b=6:9, c=6:9)
B  <- data.table(b=1:3, c=1:3, d=1:3, m=LETTERS[1:3])
C  <- data.table(n=round(rnorm(3), 2), f=c(T, F, T), c=7:9)

在表 A 中插入缺失的变量:(注意使用A2[0]

A <- merge(x=A, y=A2[0], by=intersect(names(A),names(A2)), all=TRUE)

在表 A2 中插入缺失的列:

A2 <- merge(x=A[0], y=A2, by=intersect(names(A),names(A2)), all=TRUE)

现在应该有相同的列,具有相同的类型AA2将列顺序设置为匹配,以防万一(可能不需要,不确定是否rbindlist()跨列名或列位置绑定):

setcolorder(A2, names(A))
DT.ALL <- rbindlist(l=list(A,A2))
DT.ALL

对其他表重复...也许将其放入函数中而不是手动重复会更好...

DT.ALL <- merge(x=DT.ALL, y=B[0], by=intersect(names(DT.ALL), names(B)), all=TRUE)
B <- merge(x=DT.ALL[0], y=B, by=intersect(names(DT.ALL), names(B)), all=TRUE)
setcolorder(B, names(DT.ALL))
DT.ALL <- rbindlist(l=list(DT.ALL, B))

DT.ALL <- merge(x=DT.ALL, y=C[0], by=intersect(names(DT.ALL), names(C)), all=TRUE)
C <- merge(x=DT.ALL[0], y=C, by=intersect(names(DT.ALL), names(C)), all=TRUE)
setcolorder(C, names(DT.ALL))
DT.ALL <- rbindlist(l=list(DT.ALL, C))
DT.ALL

结果看起来与 mnels 的输出相同(随机数和列顺序除外)。

PS1:原作者没有说如果有匹配的变量怎么办——我们是真的想做 arbind()还是在想 a merge()

PS2:(因为我没有足够的声誉发表评论)问题的要点似乎与此问题重复。data.table对于plyr大型数据集的基准测试也很重要。

于 2013-08-03T12:17:09.773 回答