Объедините два фрейма данных по строкам (rbind), когда они имеют разные наборы столбцов - PullRequest
190 голосов
/ 04 августа 2010

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

Ответы [ 13 ]

192 голосов
/ 04 августа 2010

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

94 голосов
/ 07 января 2015

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

46 голосов
/ 04 августа 2010

Вы можете использовать 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
36 голосов
/ 04 августа 2010

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

df3 <- rbind(df1, df2[, names(df1)])
31 голосов
/ 22 февраля 2016

Альтернатива с 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 и вы не хотите создавать список.

22 голосов
/ 08 октября 2017

Большинство ответов base 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 к каждому данным.frame с соответствующими пропущенными именами, заполненными с помощью setdiff.

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

Now, 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

Обратите внимание, что первые две строки изменяют исходные данные.кадры, 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))
18 голосов
/ 04 августа 2010

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

> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
6 голосов
/ 03 февраля 2011

Я написал функцию для этого, потому что мне нравится, когда мой код сообщает мне, если что-то не так.Эта функция явно сообщит вам, какие имена столбцов не совпадают, и если у вас есть несоответствие типов.Тогда он сделает все возможное, чтобы объединить 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)
}
2 голосов
/ 15 августа 2017

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

Stack(df_1, df_2)

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

1 голос
/ 13 ноября 2013

gtools / smartbind не любил работать с датами, вероятно, потому что это было как .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)
}
...