Подтвердить что ты не робот

Rbind с новыми столбцами и data.table

Мне нужно добавить много больших таблиц в существующую таблицу, поэтому я использую rbind с отличным пакетом data.table. Но некоторые из более поздних таблиц имеют больше столбцов, чем исходные (которые необходимо включить). Есть ли эквивалент rbind.fill для data.table?

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.

4b9b3361

Ответ 1

Вот подход, который обновит отсутствующие столбцы в

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, так как работает только по два одновременно.

Ответ 2

Так как v1.9.2, data.table функция rbind получила аргумент fill. В документации ?rbind.data.table:

Если TRUE заполняет отсутствующие столбцы с NA. По умолчанию FALSE. когда ИСТИНА, use.names должна быть ИСТИНА, и все элементы входного списка должны имеют ненулевые имена столбцов.

Таким образом, вы можете сделать (до приближения 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

Ответ 3

базовая концепция заключается в том, чтобы добавить отсутствующие столбцы в обоих направлениях: из текущей таблицы master к newTable и обратно в другую сторону.

Как заметил в комментариях @menl, просто назначение NA - проблема, потому что это будет введите весь столбец class logical.

Одним из решений является принудительное выполнение всех столбцов одного типа (т.е. as.numeric(NA)), но это слишком ограничительно.

Вместо этого нам нужно проанализировать каждый новый столбец для своего класса. Затем мы можем использовать as(NA, cc) _ (cc, являющийся классом) как вектор, который мы назначим новому столбцу. Мы завершаем это в инструкции lapply на RHS и используем 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

Ответ 4

Еще один способ вставить недостающие столбцы (с правильным типом и NA) - это merge() первая data.table A с пустой data.table A2[0], которая имеет структуру вторых данных. Таблица. Это позволяет избежать ошибок в пользовательских функциях (я знаю, что merge() более надежный, чем мой собственный код;)). Используя таблицы mnel сверху, сделайте что-то вроде кода ниже.

Кроме того, использование 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)

Теперь A и A2 должны иметь одинаковые столбцы с одинаковыми типами. Задайте порядок столбцов, на всякий случай (возможно, не обязательно, не уверены, что 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: (Поскольку у меня недостаточно репутации для комментариев) Суть вопроса кажется дубликатом этого вопроса. Также важно для бенчмаркинга data.table vs. plyr с большими наборами данных.

Ответ 5

Ответы потрясающие, но похоже, есть некоторые функции, предлагаемые здесь, такие как plyr:: rbind.fill и gtools:: smartbind, которые, казалось, отлично работали для меня.