Объедините два кадра данных по строкам (rbind), когда они имеют разные наборы столбцов

Можно ли связать строки с двумя кадром данных, которые не имеют одинакового набора столбцов? Я надеюсь сохранить столбцы, которые не совпадают после привязки.

Ответ 1

rbind.fill из пакета plyr может быть тем, что вы ищете.

Ответ 2

Более поздним решением является использование функции dplyr bind_rows, которая, как я полагаю, более эффективна, чем smartbind.

Ответ 3

Вы можете использовать smartbind из пакета gtools.

Пример:

library(gtools)
df1 <- data.frame(a = c(1:5), b = c(6:10))
df2 <- data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
smartbind(df1, df2)
# result
     a  b    c
1.1  1  6 <NA>
1.2  2  7 <NA>
1.3  3  8 <NA>
1.4  4  9 <NA>
1.5  5 10 <NA>
2.1 11 16    A
2.2 12 17    B
2.3 13 18    C
2.4 14 19    D
2.5 15 20    E

Ответ 4

Если столбцы в df1 являются подмножествами в df2 (по именам столбцов):

df3 <- rbind(df1, df2[, names(df1)])

Ответ 5

Альтернатива с data.table:

library(data.table)
df1 = data.frame(a = c(1:5), b = c(6:10))
df2 = data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
rbindlist(list(df1, df2), fill = TRUE)

rbind также будет работать в data.table, пока объекты будут преобразованы в объекты data.table, поэтому

rbind(setDT(df1), setDT(df2), fill=TRUE)

также будет работать в этой ситуации. Это может быть предпочтительнее, если у вас есть несколько data.tables и не хотите создавать список.

Ответ 6

Вы также можете просто вытащить общие имена столбцов.

> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])

Ответ 7

Я написал функцию, чтобы сделать это, потому что мне нравится мой код, чтобы сказать мне, если что-то не так. Эта функция явно укажет вам, какие имена столбцов не совпадают, и если у вас есть несоответствие типа. Тогда он сделает все возможное, чтобы объединить data.frames в любом случае. Ограничение состоит в том, что вы можете комбинировать только два кадра данных за один раз.

### combines data frames (like rbind) but by matching column names
# columns without matches in the other data frame are still combined
# but with NA in the rows corresponding to the data frame without
# the variable
# A warning is issued if there is a type mismatch between columns of
# the same name and an attempt is made to combine the columns
combineByName <- function(A,B) {
    a.names <- names(A)
    b.names <- names(B)
    all.names <- union(a.names,b.names)
    print(paste("Number of columns:",length(all.names)))
    a.type <- NULL
    for (i in 1:ncol(A)) {
        a.type[i] <- typeof(A[,i])
    }
    b.type <- NULL
    for (i in 1:ncol(B)) {
        b.type[i] <- typeof(B[,i])
    }
    a_b.names <- names(A)[!names(A)%in%names(B)]
    b_a.names <- names(B)[!names(B)%in%names(A)]
    if (length(a_b.names)>0 | length(b_a.names)>0){
        print("Columns in data frame A but not in data frame B:")
        print(a_b.names)
        print("Columns in data frame B but not in data frame A:")
        print(b_a.names)
    } else if(a.names==b.names & a.type==b.type){
        C <- rbind(A,B)
        return(C)
    }
    C <- list()
    for(i in 1:length(all.names)) {
        l.a <- all.names[i]%in%a.names
        pos.a <- match(all.names[i],a.names)
        typ.a <- a.type[pos.a]
        l.b <- all.names[i]%in%b.names
        pos.b <- match(all.names[i],b.names)
        typ.b <- b.type[pos.b]
        if(l.a & l.b) {
            if(typ.a==typ.b) {
                vec <- c(A[,pos.a],B[,pos.b])
            } else {
                warning(c("Type mismatch in variable named: ",all.names[i],"\n"))
                vec <- try(c(A[,pos.a],B[,pos.b]))
            }
        } else if (l.a) {
            vec <- c(A[,pos.a],rep(NA,nrow(B)))
        } else {
            vec <- c(rep(NA,nrow(A)),B[,pos.b])
        }
        C[[i]] <- vec
    }
    names(C) <- all.names
    C <- as.data.frame(C)
    return(C)
}

Ответ 8

Возможно, я полностью неправильно понял ваш вопрос, но "я надеюсь сохранить столбцы, которые не совпадают после привязки" заставляет меня думать, что вы ищете left join или right join, похожие на SQL-запрос. R имеет функцию merge, которая позволяет указывать левые, правые или внутренние соединения, похожие на объединение таблиц в SQL.

Здесь уже есть большой вопрос и ответ на этот вопрос: Как объединить (объединить) кадры данных (внутренний, внешний, левый, правый)?

Ответ 9

Только для документации. Вы можете попробовать библиотеку Stack и ее функцию Stack в следующей форме:

Stack(df_1, df_2)

У меня также создается впечатление, что он быстрее других методов для больших наборов данных.

Ответ 10

gtools/smartbind не понравилось работать со сроками, вероятно, потому, что это было as.vectoring. Итак, вот мое решение...

sbind = function(x, y, fill=NA) {
    sbind.fill = function(d, cols){ 
        for(c in cols)
            d[[c]] = fill
        d
    }

    x = sbind.fill(x, setdiff(names(y),names(x)))
    y = sbind.fill(y, setdiff(names(x),names(y)))

    rbind(x, y)
}

Ответ 11

Большинство базовых вопросов R касались ситуации, когда только один data.frame имел дополнительные столбцы или что результирующий data.frame имел бы пересечение столбцов. Поскольку OP пишет

Я надеюсь сохранить столбцы, которые не совпадают после привязки,

Ответ с использованием базовых методов R для решения этой проблемы, вероятно, стоит опубликовать. Ниже я представляю два базовых метода R: один, который изменяет исходные кадры данных, а на этом нет. Кроме того, я предлагаю метод, который обобщает неразрушающий метод для многих data.frames.

# sample data, variable c is in df1, variable d is in df2
df1 = data.frame(a=1:5, b=6:10, d=month.name[1:5])
df2 = data.frame(a=6:10, b=16:20, c = letters[8:12])

Два файла data.frames, изменить оригиналы
Чтобы сохранить все столбцы из обоих data.frames в rbind (и позволить функции работать, не приводя к ошибке), вы добавляете столбцы NA в каждый файл data.frame с соответствующими отсутствующими именами, заполненными в моем setdiff.

# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA

Теперь rbind -em

rbind(df1, df2)
    a  b        d    c
1   1  6  January <NA>
2   2  7 February <NA>
3   3  8    March <NA>
4   4  9    April <NA>
5   5 10      May <NA>
6   6 16     <NA>    h
7   7 17     <NA>    i
8   8 18     <NA>    j
9   9 19     <NA>    k
10 10 20     <NA>    l

Обратите внимание, что первые две строки изменяют исходные data.frames, df1 и df2, добавляя полный набор столбцов к обоим.


Два файла data.frames, не изменяют оригиналы
Чтобы оставить исходные data.frames неповрежденными, сначала пропустите имена, которые отличаются, верните именованный вектор NA, которые объединены в список с помощью data.frame, используя c. Затем data.frame преобразует результат в соответствующий файл data.frame для rbind.

rbind(
  data.frame(c(df1, sapply(setdiff(names(df2), names(df1)), function(x) NA))),
  data.frame(c(df2, sapply(setdiff(names(df1), names(df2)), function(x) NA)))
)

Многие кадры data.frames, не изменяют оригиналы
В том случае, если у вас более двух data.frames, вы можете сделать следующее.

# put data.frames into list (dfs named df1, df2, df3, etc)
mydflist <- mget(ls(pattern="df\\d+")
# get all variable names
allNms <- unique(unlist(lapply(mydflist, names)))

# put em all together
do.call(rbind,
        lapply(mydflist,
               function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
                                                  function(y) NA)))))

Может быть, немного лучше не видеть имена строк исходных data.frames? Затем сделайте это.

do.call(rbind,
        c(lapply(mydflist,
                 function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
                                                    function(y) NA)))),
          make.row.names=FALSE))

Ответ 12

rbind.ordered=function(x,y){

  diffCol = setdiff(colnames(x),colnames(y))
  if (length(diffCol)>0){
    cols=colnames(y)
    for (i in 1:length(diffCol)) y=cbind(y,NA)
    colnames(y)=c(cols,diffCol)
  }

  diffCol = setdiff(colnames(y),colnames(x))
  if (length(diffCol)>0){
    cols=colnames(x)
    for (i in 1:length(diffCol)) x=cbind(x,NA)
    colnames(x)=c(cols,diffCol)
  }
  return(rbind(x, y[, colnames(x)]))
}

Ответ 13

Я понимаю вопрос как:

a = data.frame(
  x = c(1,2,3),
  y = c(5,2,3)
)
b = data.frame(
  u = c(6,2,3),
  v = c(19,13,12)
)
dd=cbind(a, b)


str(dd)

'data.frame':   3 obs. of  4 variables:
 $ x: num  1 2 3
 $ y: num  5 2 3
 $ u: num  6 2 3
 $ v: num  19 13 12