【问题标题】:Rbind with new columns and data.table使用新列和 data.table 进行 Rbind
【发布时间】:2013-02-07 11:45:37
【问题描述】:

我需要将许多大表添加到现有表中,因此我将 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填充。

【问题讨论】:

  • 列是否总是以相同的顺序排列(即第 1 列是否在所有表中都保持为第 1 列?)
  • 不,很遗憾没有。
  • 列名至少会对应吗?
  • 是的,所有表中大约有 20 列具有相同的名称,但有些表有更多列(使用新名称)
  • @Chris,你的列总是整数吗?

标签: r data.table


【解决方案1】:

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

正如@menl 在 cmets 中指出的那样,简单地分配一个 NA 是一个问题,因为这将 制作classlogical的整列。

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

相反,我们需要分析每个新列的类别。然后我们可以使用as(NA, cc) _(cc 是类) 作为我们将分配给新列的向量。我们将其包装在 RHS 上的 lapply 语句中并使用 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

【讨论】:

  • 您需要通过引用来分配正确的类(NAlogical),否则在示例数据集中,dt.1[,cc := NA]; rbind(dt.1,dt.2).
  • 在不做更多预处理的情况下,这是一个很难解决的问题。像foo &lt;- lapply(lapply(dt.1, class), as,object=NA); dt.1[,names(foo) := foo] 这样的东西会起作用,但显然这会将NA 分配给所有列,所以只需分配你想要的那些。
  • 我在答案中模拟了一些东西。还不是一个完整的方法,但可能更接近了一步。
  • 非常感谢你们的帮助。我得到了 mnel 的解决方案,并意识到它远远超出了我自己的想法。谢谢!
【解决方案2】:

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

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 来说效率不高,因为它一次只能工作两个。

【讨论】:

    【解决方案3】:

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

    此外,在处理data.tables 时,使用rbindlist() 应该更快。

    定义表格(与上面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:原作者并没有说如果有匹配的变量怎么办——我们真的想做一个rbind()还是我们正在考虑一个merge()

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

    【讨论】:

      【解决方案4】:

      由于v1.9.2data.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
      

      【讨论】:

      • Error: 'rbind' is not an exported object from 'namespace:data.table' ?
      • rbind 现在已被调整为直接调度到 data.table 方法并直接工作,请参阅上面的编辑和 GitHub
      • @DanielKrizian Groovy!
      • @DanielKrizian 我注意到rbind 在加载data.table 的情况下单独工作,但我仍然很惊讶它会抛出该错误。猜猜我仍然没有完全了解命名空间的使用情况。例如,p_load 用作 pacman::p_load() 或在 require(pacman) 之后仅用作 p_load()
      • 这是由于一些自定义重载工作造成的。学习.onLoad on GitHub很有启发性。
      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 2021-05-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-25
        • 2021-11-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多