È possibile eseguire il binding di due frame di dati che non hanno lo stesso set di colonne? Spero di mantenere le colonne che non corrispondono dopo il bind.Combina due frame di dati per righe (rbind) quando hanno set di colonne diversi
risposta
rbind.fill
dal pacchetto plyr
potrebbe essere quello che stai cercando.
È possibile utilizzare smartbind
dal pacchetto gtools
.
Esempio:
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
Ho provato 'smartbind' con due grandi frame di dati (in totale circa 3 * 10^6 righe) e l'ho interrotto dopo 10 minuti. – Joe
Si potrebbe anche solo tirare fuori i nomi delle colonne comuni.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
Se le colonne in df1 è un sottoinsieme di quelle in DF2 (dai nomi delle colonne):
df3 <- rbind(df1, df2[, names(df1)])
Forse ho letto male completamente la tua domanda, ma il "spero di mantenere le colonne che non corrispondono dopo il legame "mi fa pensare che stai cercando uno left join
o right join
simile a una query SQL. R ha la funzione merge
che consente di specificare giunzioni a sinistra, a destra o interne simili a quelle di unione in SQL.
Esiste già una grande domanda e risposta su questo argomento qui: How to join (merge) data frames (inner, outer, left, right)?
ho scritto una funzione per fare questo perché mi piace il mio codice di dirmi se qualcosa non va. Questa funzione ti dirà esplicitamente quali nomi di colonne non corrispondono e se hai una mancata corrispondenza di tipo. Quindi farà del suo meglio per combinare comunque i data.frames. La limitazione è che puoi combinare solo due data.frames alla volta.
### 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)
}
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)]))
}
gtools/non ha ancora smartbind come lavorare con le date, probabilmente perché era as.vectoring. Quindi, ecco la mia soluzione ...
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)
}
Una soluzione più recente è quella di utilizzare dplyr
s' bind_rows
funzione che presumo è più efficiente di smartbind
.
Un'alternativa con 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
funzionano anche in data.table
finché gli oggetti vengono convertiti data.table
oggetti, così
rbind(setDT(df1), setDT(df2), fill=TRUE)
funzionano anche in questa situazione . Questo può essere preferibile quando hai un paio di dati.tables e non vuoi costruire un elenco.
aver capito la domanda come:
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
Sfortunatamente, la tua comprensione sembra sbagliata come si può vedere leggendo la risposta accettata. La domanda riguarda * binding di righe * due data.frames nel caso in cui non abbiano lo stesso set di colonne.Il tuo post si occupa di * column * binding usando 'cbind()'. Per favore, considera di rivedere il tuo post con una risposta univoca alla domanda o di eliminarlo. Grazie. – Uwe
Proprio per la documentazione.Si può provare la libreria Stack
e la sua funzione Stack
nella forma seguente:
Stack(df_1, df_2)
Ho anche l'impressione che sia più veloce di altri metodi per grandi insiemi di dati.
La maggior parte delle risposte di base R risolve la situazione in cui solo un data.frame ha colonne aggiuntive o che il data.frame risultante avrebbe l'intersezione delle colonne. Dal momento che l'OP scrive spero di mantenere le colonne che non corrispondono dopo il binding, è probabile che una risposta utilizzando i metodi di base R per risolvere questo problema sia probabilmente utile.
Di seguito, presento due metodi di base R: Uno che altera i file di dati originali e uno che non lo fa. Inoltre, offro un metodo che generalizza il metodo non distruttivo a più di due data.frames.
In primo luogo, prendiamo alcuni dati di esempio.
# 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])
Due data.frames, alterano originali
Per conservare tutte le colonne di entrambi data.frames in rbind
(e consentire la funzione di lavorare senza un conseguente errore), è aggiungere colonne NA a ogni data.frame con i nomi mancanti appropriati compilati utilizzando setdiff
.
# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA
Ora, 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
Si noti che le prime due righe alterano il data.frames originale, df1 e DF2, aggiungendo il set completo di colonne per entrambi.
Due data.frames, non alterare gli originali
Per lasciare le data.frames originali intatti, primo ciclo attraverso i nomi che differiscono, restituisce un nome vettore di AN che sono concatenati in un elenco con il data.frame utilizzando c
. Quindi, data.frame
converte il risultato in un data.frame appropriato per lo 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)))
)
Molti data.frames, non alterare gli originali
nel caso che si dispone di più di due data.frames, si potrebbe fare quanto segue.
# 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)))))
Forse un po 'più bello non vedere i nomi delle righe di data.frames originali? Quindi fai questo.
do.call(rbind,
c(lapply(mydflist,
function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
function(y) NA)))),
make.row.names=FALSE))
'rbind.fill' e' bind_rows() 'abbassano silenziosamente i nomi dei giocatori. – MERose
@MERose Hadley: "Sì, tutti i metodi di dplyr ignorano i nomi dei giocatori." – zx8754