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

232

Ist es möglich, zwei Datenrahmen, die nicht denselben Spaltensatz haben, zeilenweise zu binden? Ich hoffe, die Spalten beizubehalten, die nach dem Binden nicht übereinstimmen.

Btibert3
quelle

Antworten:

223

rbind.fillaus dem Paket plyrkönnte sein, was Sie suchen.

Jyotirmoy Bhattacharya
quelle
12
rbind.fillund bind_rows()beide lassen lautlos Rownamen fallen.
MERose
3
@MERose Hadley: "Ja, alle dplyr-Methoden ignorieren Rownamen."
zx8754
Hier ist ein Link zur Dokumentation: rdocumentation.org/packages/plyr/versions/1.8.4/topics/…
Gabriel Fair
124

Eine neuere Lösung besteht darin, dplyrdie bind_rowsFunktion zu verwenden , von der ich annehme, dass sie effizienter ist als smartbind.

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])
dplyr::bind_rows(df1, df2)
    a  b    c
1   1  6 <NA>
2   2  7 <NA>
3   3  8 <NA>
4   4  9 <NA>
5   5 10 <NA>
6  11 16    A
7  12 17    B
8  13 18    C
9  14 19    D
10 15 20    E
Xiaodai
quelle
Ich versuche, eine große Anzahl von Datenrahmen (16) mit verschiedenen Spaltennamen zu kombinieren. Wenn ich dies versuche, wird eine Fehlermeldung angezeigt. Fehler: Die Spalte ABCkann nicht von Zeichen in Zahlen umgewandelt werden. Gibt es eine Möglichkeit, die Spalten zuerst zu konvertieren?
Sar
46

Sie können smartbindaus dem gtoolsPaket 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
neilfws
quelle
3
Ich habe es smartbindmit zwei großen Datenrahmen (insgesamt ungefähr 3 * 10 ^ 6 Zeilen) versucht und nach 10 Minuten abgebrochen.
Joe
2
In 9 Jahren ist viel passiert :) Ich könnte Smartbind heute nicht verwenden. Beachten Sie auch, dass in der ursprünglichen Frage keine großen Datenrahmen angegeben wurden.
Neilfws
42

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

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

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)

rbindfunktioniert data.tableauch, solange die Objekte in data.tableObjekte konvertiert werden

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

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

kdauria
quelle
Dies ist die einfachste, sofort einsatzbereite Lösung, die sich leicht auf eine beliebige Anzahl von Datenrahmen verallgemeinern lässt, da Sie sie alle in separaten Listenelementen speichern können. Andere Antworten, wie der intersectAnsatz, funktionieren nur für 2 Datenrahmen und lassen sich nicht leicht verallgemeinern.
Rich Pauloo
35

Die meisten Antworten der Basis R befassen sich mit der Situation, in der nur ein data.frame zusätzliche Spalten enthält oder der resultierende data.frame den Schnittpunkt der Spalten aufweist. Da das OP schreibt, hoffe ich, die Spalten beizubehalten, die nach dem Binden nicht übereinstimmen , ist eine Antwort mit Basis-R-Methoden zur Behebung dieses Problems wahrscheinlich eine Veröffentlichung wert.

Im Folgenden stelle ich zwei Basis-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 abrufen.

# 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 Datenrahmen in einem zu behalten rbind(und die Funktion ohne Fehler arbeiten zu lassen), fügen Sie jedem Datenrahmen NA-Spalten mit den entsprechenden fehlenden Namen hinzu mit setdiff.

# 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 Datenrahmen df1 und df2 ändern und beiden den vollständigen Satz von Spalten hinzufügen.


Zwei data.frames, Originale nicht ändern
Um die ursprünglichen data.frames intakt zu lassen, durchlaufen Sie zunächst die unterschiedlichen Namen und geben Sie einen benannten Vektor von NAs zurück, die mit dem data.frame in einer Liste verkettet sind c. Dann data.framewandelt das Ergebnis in eine entsprechende 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 data.frames ändern Originale nicht
In dem Fall, dass 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, die Zeilennamen der ursprünglichen data.frames nicht zu 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))
lmo
quelle
Ich habe 16 Datenrahmen, einige mit unterschiedlichen Spalten (jeweils ca. 70-90 Spalten insgesamt). Wenn ich das versuche, bleibe ich beim ersten Befehl <- mget (ls (pattern = "df \\ d +")). Meine Datenrahmen haben unterschiedliche Namen. Ich habe versucht, eine Liste mit mydflist <- c (as, dr, kr, hyt, ed1, of) zu erstellen, aber dies gab mir eine enorme Liste.
Sar
Einfach auf @GKi verlinken
sar
1
@sar verwenden mydflist <- list(as, dr, kr, hyt, ed1, of). Dadurch sollte ein Listenobjekt erstellt werden, das nicht die Größe Ihrer Umgebung vergrößert, sondern nur auf jedes Element der Liste verweist (sofern Sie anschließend keinen Inhalt ändern). Entfernen Sie nach dem Vorgang das Listenobjekt, um die Sicherheit zu gewährleisten.
25.
20

Sie können auch einfach die allgemeinen Spaltennamen herausziehen.

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

Ich habe eine Funktion geschrieben, um dies zu tun, weil ich möchte, dass mein Code mir sagt, wenn etwas nicht stimmt. Diese Funktion teilt Ihnen explizit mit, welche Spaltennamen nicht übereinstimmen und ob der Typ nicht übereinstimmt. Dann wird es sein Bestes tun, um die data.frames trotzdem zu kombinieren. Die Einschränkung besteht darin, dass Sie jeweils nur zwei data.frames 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)
}

quelle
2

Vielleicht habe ich Ihre Frage völlig falsch verstanden, aber das "Ich hoffe, die Spalten beizubehalten, die nach dem Binden nicht übereinstimmen" lässt mich denken, dass Sie nach einer left joinoder einer right joinähnlichen SQL-Abfrage suchen . R verfügt über die mergeFunktion, mit der Sie linke, rechte oder innere Verknüpfungen angeben können, ähnlich wie beim Verknüpfen von Tabellen in SQL.

Zu diesem Thema gibt es hier bereits eine gute Frage und Antwort: Wie werden Datenrahmen (innen, außen, links, rechts) verbunden (zusammengeführt)?

Verfolgungsjagd
quelle
2

gtools / smartbind arbeiteten nicht gern mit Dates, 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)
}
Aaron
quelle
Wenn Sie dplyr :: bind_rows (x, y) anstelle von rbind (x, y) verwenden, wird die Spaltenreihenfolge basierend auf dem ersten Datenrahmen beibehalten.
RanonKahn
2

Nur zur Dokumentation. Sie können die StackBibliothek und ihre Funktion Stackin folgender Form ausprobieren :

Stack(df_1, df_2)

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

Cro-Magnon
quelle
1

Sie können auch verwendet werden sjmisc::add_rows(), welche Anwendungen dplyr::bind_rows(), aber im Gegensatz zu bind_rows(), add_rows()Konserven - Attribute und ist daher für sinnvoll markierte Daten .

Siehe folgendes Beispiel mit einem beschrifteten Datensatz. Die Funktion frq()druckt Häufigkeitstabellen 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
Daniel
quelle
-1
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)]))
}
RockScience
quelle