Risposte:
rbind.fill
dal pacchetto plyr
potrebbe essere quello che stai cercando.
Una soluzione più recente è quella di utilizzare dplyr
la bind_rows
funzione che presumo sia più efficiente di 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
ABC
non può essere convertita da carattere a numerico. C'è un modo per convertire prima le colonne?
Puoi usare smartbind
dal gtools
pacchetto.
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
smartbind
con due grandi frame di dati (in totale circa 3 * 10 ^ 6 righe) e l'ho interrotto dopo 10 minuti.
Se le colonne in df1 sono un sottoinsieme di quelle in df2 (in base ai nomi delle colonne):
df3 <- rbind(df1, df2[, names(df1)])
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
funzionerà anche data.table
finché gli oggetti vengono convertiti in data.table
oggetti, quindi
rbind(setDT(df1), setDT(df2), fill=TRUE)
funzionerà anche in questa situazione. Questo può essere preferibile quando hai un paio di data.tables e non vuoi costruire un elenco.
intersect
approccio, funzionano solo con 2 frame di dati e non si generalizzano facilmente.
La maggior parte delle risposte di base R affronta la situazione in cui solo un data.frame ha colonne aggiuntive o che il data.frame risultante avrebbe l'intersezione delle colonne. Poiché l'OP scrive spero di conservare le colonne che non corrispondono dopo il bind , probabilmente vale la pena pubblicare una risposta utilizzando i metodi di base R per risolvere questo problema.
Di seguito, presento due metodi R di base: uno che altera i data.frames originali e uno che non lo fa. Inoltre, offro un metodo che generalizza il metodo non distruttivo a più di due data.frames.
Innanzitutto, otteniamo 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 gli originali
Per conservare tutte le colonne di entrambi i data.frames in un rbind
(e consentire alla funzione di funzionare senza generare un errore), aggiungi colonne NA a ciascun data.frame con i nomi mancanti appropriati compilati usando 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 modificano i data.frames originali, df1 e df2, aggiungendo il set completo di colonne ad entrambi.
Due data.frames, non alterano gli originali
Per lasciare intatti i data.frames originali, scorrere prima i nomi che differiscono, restituendo un vettore denominato di NA che sono concatenati in un elenco con data.frame usando c
. Quindi, data.frame
converte il risultato in un data.frame appropriato per il file 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 alterano gli originali Nell'istanza in
cui hai più di due data.frames, puoi 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 dei 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))
mydflist <- list(as, dr, kr, hyt, ed1, of)
. Questo dovrebbe costruire un oggetto elenco che non aumenti le dimensioni del tuo ambiente, ma punti solo a ciascun elemento dell'elenco (purché non modifichi in seguito alcun contenuto). Dopo l'operazione, rimuovere l'oggetto elenco, solo per sicurezza.
Potresti anche estrarre i nomi delle colonne comuni.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
Ho scritto una funzione per farlo perché mi piace il mio codice per dirmi se qualcosa non va. Questa funzione ti dirà esplicitamente quali nomi di colonna non corrispondono e se hai una mancata corrispondenza del tipo. Quindi farà del suo meglio per combinare i data.frames comunque. Il limite è 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)
}
Forse ho letto male la tua domanda, ma "Spero di conservare le colonne che non corrispondono dopo il bind" mi fa pensare che stai cercando una left join
o right join
simile a una query SQL. R ha la merge
funzione che consente di specificare join sinistro, destro o interno simili alle tabelle di join in SQL.
C'è già una grande domanda e risposta su questo argomento qui: Come unire (unire) i frame di dati (interno, esterno, sinistro, destro)?
A gtools / smartbind non piaceva lavorare con 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)
}
Solo per la documentazione. Puoi provare la Stack
libreria e la sua funzione Stack
nel seguente formato:
Stack(df_1, df_2)
Ho anche l'impressione che sia più veloce di altri metodi per set di dati di grandi dimensioni.
È inoltre possibile utilizzare sjmisc::add_rows()
, che utilizza dplyr::bind_rows()
, ma a differenza bind_rows()
, add_rows()
conserva gli attributi e quindi è utile per i dati etichettati .
Vedere l'esempio seguente con un set di dati con etichetta. La funzione frq()
stampa le tabelle di frequenza con etichette dei valori, se i dati sono etichettati.
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
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)]))
}
rbind.fill
edbind_rows()
entrambi rilasciano silenziosamente i rownames.