wake-up-neo.com

Kombinieren Sie zwei Datenrahmen nach Zeilen (rbind), wenn sie unterschiedliche Spaltengruppen haben

Ist es möglich, zwei Datenrahmen, die nicht über dieselbe Spaltengruppe verfügen, in Zeilen zu binden? Ich hoffe, dass die Spalten, die nach dem Bind nicht übereinstimmen, beibehalten werden. 

170
Btibert3

Möglicherweise suchen Sie nach rbind.fill aus dem Paket plyr.

185

Eine neuere Lösung ist die Verwendung der bind_rows-Funktion von dplyr, von der ich annehme, dass sie effizienter ist als smartbind.

89
xiaodai

Sie können smartbind aus dem Paket gtools verwenden.

Beispiel:

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
45
neilfws

Wenn die Spalten in df1 eine Teilmenge der in df2 sind (nach Spaltennamen):

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

Eine Alternative mit 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 funktioniert auch in data.table, solange die Objekte in data.table-Objekte konvertiert werden 

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

wird auch in dieser Situation funktionieren. Dies kann vorzuziehen sein, wenn Sie über einige Datentabellen verfügen und keine Liste erstellen möchten.

22
kdauria

Sie können auch einfach die üblichen Spaltennamen herausziehen. 

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

Die meisten Basis-R-Antworten beziehen sich auf die Situation, in der nur ein data.frame zusätzliche Spalten hat oder dass das resultierende data.frame den Schnittpunkt der Spalten hätte. Da das OP schreibt Ich hoffe, die Spalten, die nach dem bind nicht übereinstimmen, beizubehalten, ist es wahrscheinlich wert, eine Antwort mit Basis-R-Methoden zur Lösung dieses Problems zu posten.

Im Folgenden stelle ich zwei grundlegende R-Methoden vor: eine, die die ursprünglichen data.frames ändert, und eine, die dies nicht tut. Zusätzlich biete ich eine Methode an, die die zerstörungsfreie Methode auf mehr als zwei data.frames verallgemeinert.

Lassen Sie uns zunächst einige Beispieldaten erhalten.

# 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])

Zwei Datenrahmen, Originale ändern
Um alle Spalten aus beiden data.frames in einer rbind beizubehalten (und die Funktion ohne Fehler ausführen zu lassen), fügen Sie jedem data.frame NA-Spalten hinzu, wobei die entsprechenden fehlenden Namen mit setdiff ausgefüllt werden.

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

Nun 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

Beachten Sie, dass die ersten beiden Zeilen die ursprünglichen data.frames (df1 und df2) ändern und den vollständigen Satz von Spalten zu beiden hinzufügen.


Zwei data.frames, Originale nicht ändern
Um die ursprünglichen data.frames intakt zu lassen, durchlaufen Sie zunächst die Namen, die sich unterscheiden, und geben Sie einen benannten Vektor von NAs zurück, die mit c in einer Liste mit dem data.frame verkettet werden. Dann konvertiert data.frame das Ergebnis in einen geeigneten data.frame für die 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)))
)

Viele Datenrahmen ändern Originale nicht
Wenn Sie mehr als zwei data.frames haben, können Sie Folgendes tun.

# 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)))))

Vielleicht ein bisschen schöner, wenn Sie die Zeilennamen der ursprünglichen data.frames nicht sehen? Dann mach das.

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

Ich habe dazu eine Funktion geschrieben, weil mir mein Code gefällt, wenn etwas nicht stimmt. Diese Funktion teilt Ihnen explizit mit, welche Spaltennamen nicht übereinstimmen und ob ein Typenkonflikt vorliegt. Dann versucht es trotzdem, die data.frames trotzdem zu kombinieren. Die Einschränkung ist, dass Sie nur zwei data.frames gleichzeitig kombinieren können.

### 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)
}
6
user399470

Nur für die Dokumentation. Sie können die Bibliothek Stack und ihre Funktion Stack in der folgenden Form testen:

Stack(df_1, df_2)

Ich habe auch den Eindruck, dass es für große Datensätze schneller ist als andere Methoden.

2
Cro-Magnon

Vielleicht habe ich Ihre Frage völlig falsch verstanden, aber das "Ich hoffe, die Spalten zu behalten, die nach dem Bindungsvorgang nicht übereinstimmen" lässt mich denken, dass Sie nach einem left join oder right join suchen, der einer SQL-Abfrage ähnelt. R hat die Funktion merge, mit der Sie Links, Rechts oder Inner-Joins festlegen können, ähnlich wie in SQL-Tabellen verbundene Tabellen.

Zu diesem Thema gibt es bereits eine große Frage und Antwort: Wie werden Datenrahmen (innere, äußere, linke, rechte) zusammengefügt (zusammengeführt)?

1
Chase

gtools/smartbind arbeitete nicht gern mit Dates zusammen, wahrscheinlich weil es as.vectoring war. Also hier ist meine Lösung ...

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)
}
1
aaron

Sie können auch sjmisc::add_rows() verwenden, das dplyr::bind_rows() verwendet. Anders als bind_rows() behält add_rows() jedoch die Attribute und ist daher nützlich für beschriftete Daten .

Siehe folgendes Beispiel mit einem beschrifteten Datensatz. Die Funktion frq()- druckt Frequenztabellen mit Wertelabels, wenn die Daten beschriftet sind.

library(sjmisc)
library(dplyr)

data(efc)
# select two subsets, with some identical and else different columns
x1 <- efc %>% select(1:5) %>% slice(1:10)
x2 <- efc %>% select(3:7) %>% slice(11:20)

str(x1)
#> 'data.frame':    10 obs. of  5 variables:
#>  $ c12hour : num  16 148 70 168 168 16 161 110 28 40
#>   ..- attr(*, "label")= chr "average number of hours of care per week"
#>  $ e15relat: num  2 2 1 1 2 2 1 4 2 2
#>   ..- attr(*, "label")= chr "relationship to elder"
#>   ..- attr(*, "labels")= Named num  1 2 3 4 5 6 7 8
#>   .. ..- attr(*, "names")= chr  "spouse/partner" "child" "sibling" "daughter or son -in-law" ...
#>  $ e16sex  : num  2 2 2 2 2 2 1 2 2 2
#>   ..- attr(*, "label")= chr "elder's gender"
#>   ..- attr(*, "labels")= Named num  1 2
#>   .. ..- attr(*, "names")= chr  "male" "female"
#>  $ e17age  : num  83 88 82 67 84 85 74 87 79 83
#>   ..- attr(*, "label")= chr "elder' age"
#>  $ e42dep  : num  3 3 3 4 4 4 4 4 4 4
#>   ..- attr(*, "label")= chr "elder's dependency"
#>   ..- attr(*, "labels")= Named num  1 2 3 4
#>   .. ..- attr(*, "names")= chr  "independent" "slightly dependent" "moderately dependent" "severely dependent"

bind_rows(x1, x1) %>% frq(e42dep)
#> 
#> # e42dep <numeric> 
#> # total N=20  valid N=20  mean=3.70  sd=0.47
#>  
#>   val frq raw.prc valid.prc cum.prc
#>     3   6      30        30      30
#>     4  14      70        70     100
#>  <NA>   0       0        NA      NA

add_rows(x1, x1) %>% frq(e42dep)
#> 
#> # elder's dependency (e42dep) <numeric> 
#> # total N=20  valid N=20  mean=3.70  sd=0.47
#>  
#>  val                label frq raw.prc valid.prc cum.prc
#>    1          independent   0       0         0       0
#>    2   slightly dependent   0       0         0       0
#>    3 moderately dependent   6      30        30      30
#>    4   severely dependent  14      70        70     100
#>   NA                   NA   0       0        NA      NA
0
Daniel