Respostas:
rbind.filldo pacote plyrpode ser o que você está procurando.
Uma solução mais recente é usar dplyra bind_rowsfunção de que suponho ser mais eficiente que 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
ABCnão pode ser convertida de caractere para numérico. Existe uma maneira de converter as colunas primeiro?
Você pode usar smartbindo gtoolspacote.
Exemplo:
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
smartbindcom dois grandes quadros de dados (no total aproximadamente 3 * 10 ^ 6 linhas) e abortei-o após 10 minutos.
Se as colunas no df1 forem um subconjunto daquelas no df2 (pelos nomes das colunas):
df3 <- rbind(df1, df2[, names(df1)])
Uma alternativa com 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)
rbindtambém funcionará data.tableenquanto os objetos forem convertidos em data.tableobjetos, portanto
rbind(setDT(df1), setDT(df2), fill=TRUE)
também funcionará nessa situação. Isso pode ser preferível quando você tem algumas tabelas de dados e não deseja construir uma lista.
intersectabordagem, funcionam apenas para dois quadros de dados e não são facilmente generalizadas.
A maioria das respostas R básicas aborda a situação em que apenas um data.frame possui colunas adicionais ou que o data.frame resultante teria a interseção das colunas. Como o OP escreve , espero manter as colunas que não correspondem após a ligação , provavelmente vale a pena postar uma resposta usando os métodos base R para resolver esse problema.
A seguir, apresento dois métodos R básicos: um que altera o data.frames original e outro que não. Além disso, ofereço um método que generaliza o método não destrutivo para mais de dois data.frames.
Primeiro, vamos obter alguns dados de amostra.
# 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])
Dois data.frames, altere originais
Para manter todas as colunas de ambos data.frames em um rbind(e permitir que a função funcione sem resultar em erro), adicione colunas NA a cada data.frame com os nomes ausentes apropriados preenchidos usando setdiff.
# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA
Agora, 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
Observe que as duas primeiras linhas alteram os data.frames originais, df1 e df2, adicionando o conjunto completo de colunas a ambos.
Dois data.frames, não alteram os originais
Para deixar os data.frames originais intactos, primeiro faça um loop entre os nomes que diferem, retorne um vetor nomeado de NAs que são concatenados em uma lista com o data.frame usando c. Em seguida, data.frameconverte o resultado em um data.frame apropriado para o 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)))
)
Muitos data.frames, não alteram os originais
Caso você tenha mais de dois data.frames, faça o seguinte.
# 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)))))
Talvez um pouco melhor não ver os nomes das linhas dos data.frames originais? Então faça isso.
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). Isso deve criar um objeto de lista que não aumente o tamanho do seu ambiente, mas apenas aponte para cada elemento da lista (desde que você não altere nenhum conteúdo posteriormente). Após a operação, remova o objeto de lista, apenas para garantir a segurança.
Você também pode simplesmente retirar os nomes das colunas comuns.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
Eu escrevi uma função para fazer isso porque eu gosto do meu código para me dizer se algo está errado. Essa função informará explicitamente quais nomes de colunas não correspondem e se você tem uma incompatibilidade de tipo. Em seguida, fará o possível para combinar os data.frames de qualquer maneira. A limitação é que você pode combinar apenas dois data.frames por vez.
### 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)
}
Talvez eu tenha interpretado mal sua pergunta completamente, mas o "Espero manter as colunas que não coincidem após a ligação" me faz pensar que você está procurando por uma consulta left joinou right joinsemelhante a uma consulta SQL. R possui a mergefunção que permite especificar junções esquerda, direita ou interna semelhantes às junções de tabelas no SQL.
Já existe uma ótima pergunta e resposta sobre este tópico aqui: Como unir (mesclar) quadros de dados (interno, externo, esquerdo, direito)?
O gtools / smartbind não gostava de trabalhar com o Datas, provavelmente porque era como.vector. Então aqui está a minha solução ...
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)
}
Apenas para a documentação. Você pode tentar a Stackbiblioteca e sua função Stackno seguinte formato:
Stack(df_1, df_2)
Também tenho a impressão de que é mais rápido que outros métodos para grandes conjuntos de dados.
Você também pode usar sjmisc::add_rows(), que usa dplyr::bind_rows(), mas diferentemente bind_rows(), add_rows()preserva atributos e, portanto, é útil para dados rotulados .
Veja o exemplo a seguir com um conjunto de dados rotulado. A frq()função-imprime tabelas de frequência com etiquetas de valor, se os dados estiverem rotulados.
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.fillebind_rows()ambos silenciosamente eliminam nomes de domínio.