Respostas:
rbind.fill
do pacote plyr
pode ser o que você está procurando.
Uma solução mais recente é usar dplyr
a bind_rows
funçã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
ABC
não pode ser convertida de caractere para numérico. Existe uma maneira de converter as colunas primeiro?
Você pode usar smartbind
o gtools
pacote.
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
smartbind
com 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)
rbind
também funcionará data.table
enquanto os objetos forem convertidos em data.table
objetos, 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.
intersect
abordagem, 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.frame
converte 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 join
ou right join
semelhante a uma consulta SQL. R possui a merge
funçã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 Stack
biblioteca e sua função Stack
no 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.fill
ebind_rows()
ambos silenciosamente eliminam nomes de domínio.