Verschmelzen Sie gleichzeitig mehrere data.frames in einer Liste


Answers

Reduzieren macht das ziemlich einfach:

merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)

Hier ist ein vollständiges Beispiel mit einigen Scheindaten:

set.seed(1)
list.of.data.frames = list(data.frame(x=1:10, a=1:10), data.frame(x=5:14, b=11:20), data.frame(x=sample(20, 10), y=runif(10)))
merged.data.frame = Reduce(function(...) merge(..., all=T), list.of.data.frames)
tail(merged.data.frame)
#    x  a  b         y
#12 12 NA 18        NA
#13 13 NA 19        NA
#14 14 NA 20 0.4976992
#15 15 NA NA 0.7176185
#16 16 NA NA 0.3841037
#17 19 NA NA 0.3800352

Und hier ein Beispiel, das diese Daten verwendet, um my.list zu replizieren:

merged.data.frame = Reduce(function(...) merge(..., by=match.by, all=T), my.list)
merged.data.frame[, 1:12]

#  matchname party st district chamber senate1993 name.x v2.x v3.x v4.x senate1994 name.y
#1   ALGIERE   200 RI      026       S         NA   <NA>   NA   NA   NA         NA   <NA>
#2     ALVES   100 RI      019       S         NA   <NA>   NA   NA   NA         NA   <NA>
#3    BADEAU   100 RI      032       S         NA   <NA>   NA   NA   NA         NA   <NA>

Hinweis: Es scheint, dass dies ein Fehler beim merge . Das Problem besteht darin, dass nicht geprüft wird, ob das Hinzufügen der Suffixe (um überlappende nicht übereinstimmende Namen zu behandeln) tatsächlich einzigartig ist. An einem bestimmten Punkt verwendet er [.data.frame das die Namen [.data.frame macht , wodurch der rbind fehlschlägt.

# first merge will end up with 'name.x' & 'name.y'
merge(my.list[[1]], my.list[[2]], by=match.by, all=T)
# [1] matchname    party        st           district     chamber      senate1993   name.x      
# [8] votes.year.x senate1994   name.y       votes.year.y
#<0 rows> (or 0-length row.names)
# as there is no clash, we retain 'name.x' & 'name.y' and get 'name' again
merge(merge(my.list[[1]], my.list[[2]], by=match.by, all=T), my.list[[3]], by=match.by, all=T)
# [1] matchname    party        st           district     chamber      senate1993   name.x      
# [8] votes.year.x senate1994   name.y       votes.year.y senate1995   name         votes.year  
#<0 rows> (or 0-length row.names)
# the next merge will fail as 'name' will get renamed to a pre-existing field.

Der einfachste Weg zu beheben ist, die Feldumbenennung für Duplikatfelder (von denen es hier viele gibt) nicht bis zum merge zu merge . Z.B:

my.list2 = Map(function(x, i) setNames(x, ifelse(names(x) %in% match.by,
      names(x), sprintf('%s.%d', names(x), i))), my.list, seq_along(my.list))

Die merge / Reduce funktioniert dann gut.

Question

Ich habe eine Liste mit vielen data.frames, die ich zusammenführen möchte. Das Problem hierbei ist, dass sich jedes data.frame hinsichtlich der Anzahl der Zeilen und Spalten unterscheidet, aber alle teilen sich die Schlüsselvariablen (die ich im folgenden Code "var1" und "var2" ). Wenn die data.frames in Bezug auf die Spalten identisch rbind , könnte ich einfach rbind , für welche rbind.fill von rbind.fill die Aufgabe rbind.fill würde, aber das ist bei diesen Daten nicht der Fall.

Da der merge nur für 2 data.frames funktioniert, wandte ich mich dem Internet für Ideen zu. Ich habe dieses here , was perfekt in R 2.7.2 funktioniert hat, was ich damals hatte:

merge.rec <- function(.list, ...){
    if(length(.list)==1) return(.list[[1]])
    Recall(c(list(merge(.list[[1]], .list[[2]], ...)), .list[-(1:2)]), ...)
}

Und ich würde die Funktion so nennen:

df <- merge.rec(my.list, by.x = c("var1", "var2"), 
                by.y = c("var1", "var2"), all = T, suffixes=c("", ""))

Aber in jeder R-Version nach 2.7.2, einschließlich 2.11 und 2.12, schlägt dieser Code mit dem folgenden Fehler fehl:

Error in match.names(clabs, names(xi)) : 
  names do not match previous names

(Übrigens sehe ich andere Verweise auf diesen Fehler an elsewhere ohne Auflösung).

Gibt es eine Möglichkeit, dies zu lösen?




Sie können dazu die Rekursion verwenden. Ich habe das Folgende nicht verifiziert, aber es sollte Ihnen die richtige Idee geben:

MergeListOfDf = function( data , ... )
{
    if ( length( data ) == 2 ) 
    {
        return( merge( data[[ 1 ]] , data[[ 2 ]] , ... ) )
    }    
    return( merge( MergeListOfDf( data[ -1 ] , ... ) , data[[ 1 ]] , ... ) )
}



Eine neuere Lösung könnte im purrr Paket verfügbar sein. Für Ihre genaue Frage können Sie reduce() beachten Sie die kleine r im Vergleich zu base::Reduce , aber Sie können möglicherweise das Problem vollständig vermeiden, indem Sie map_dfr() oder map_dfc die das Problem durch die Karte und die Reduzierung verhindern könnte Schritt in eins.