Réponses:
rbind.fill
de l'emballage plyr
pourrait être ce que vous recherchez.
Une solution plus récente consiste à utiliser dplyr
la bind_rows
fonction de qui, je suppose, est plus efficace 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
ne peut pas être convertie de caractère en numérique. Existe-t-il un moyen de convertir les colonnes en premier?
Vous pouvez utiliser à smartbind
partir du gtools
package.
Exemple:
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
avec deux grandes trames de données (au total environ 3 * 10 ^ 6 lignes) et je l'ai abandonné après 10 minutes.
Si les colonnes de df1 sont un sous-ensemble de celles de df2 (par nom de colonne):
df3 <- rbind(df1, df2[, names(df1)])
Une alternative avec 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
fonctionnera également data.table
tant que les objets sont convertis en data.table
objets, donc
rbind(setDT(df1), setDT(df2), fill=TRUE)
fonctionnera également dans cette situation. Cela peut être préférable lorsque vous avez quelques data.tables et que vous ne voulez pas construire de liste.
intersect
approche, ne fonctionnent que pour 2 trames de données et ne se généralisent pas facilement.
La plupart des réponses R de base concernent le cas où un seul data.frame a des colonnes supplémentaires ou que le data.frame résultant aurait l'intersection des colonnes. Étant donné que l'OP écrit, j'espère conserver les colonnes qui ne correspondent pas après la liaison , une réponse utilisant des méthodes de base R pour résoudre ce problème vaut probablement la peine d'être publiée.
Ci-dessous, je présente deux méthodes R de base: une qui modifie les data.frames d'origine et une qui ne le fait pas. De plus, je propose une méthode qui généralise la méthode non destructive à plus de deux data.frames.
Tout d'abord, obtenons quelques exemples de données.
# 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])
Deux data.frames, modifier les originaux
Afin de conserver toutes les colonnes des deux data.frames dans un rbind
(et permettre à la fonction de fonctionner sans entraîner d'erreur), vous ajoutez des colonnes NA à chaque data.frame avec les noms manquants appropriés remplis en utilisant setdiff
.
# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA
Maintenant, 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
Notez que les deux premières lignes modifient les data.frames d'origine, df1 et df2, en ajoutant l'ensemble complet de colonnes aux deux.
Deux data.frames, ne modifient pas les originaux
Pour laisser les data.frames d'origine intacts, parcourez d'abord les noms qui diffèrent, renvoyez un vecteur nommé des NA qui sont concaténés dans une liste avec data.frame en utilisant c
. , Puis data.frame
convertit le résultat en un data.frame approprié 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)))
)
De nombreux cadres de données ne modifient pas les originaux
Dans le cas où vous avez plus de deux cadres de données, vous pouvez effectuer les opérations suivantes.
# 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)))))
Peut-être un peu plus agréable de ne pas voir les noms de lignes des data.frames d'origine? Alors fais ça.
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)
. Cela devrait construire un objet liste qui n'augmente pas la taille de votre environnement, mais pointe simplement vers chaque élément de la liste (tant que vous ne modifiez aucun contenu par la suite). Après l'opération, supprimez l'objet liste, juste pour être sûr.
Vous pouvez également simplement extraire les noms de colonnes communs.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
J'ai écrit une fonction pour ce faire parce que j'aime mon code pour me dire si quelque chose ne va pas. Cette fonction vous indiquera explicitement quels noms de colonnes ne correspondent pas et si vous avez une incompatibilité de type. Ensuite, il fera de son mieux pour combiner les data.frames de toute façon. La limitation est que vous ne pouvez combiner que deux data.frames à la fois.
### 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)
}
J'ai peut-être mal lu votre question, mais le message "J'espère conserver les colonnes qui ne correspondent pas après la liaison" me fait penser que vous recherchez une left join
ou right join
similaire à une requête SQL. R a la merge
fonction qui vous permet de spécifier des jointures gauche, droite ou internes similaires à la jointure de tables dans SQL.
Il y a déjà une grande question et réponse sur ce sujet ici: Comment joindre (fusionner) des trames de données (interne, externe, gauche, droite)?
gtools / smartbind n'aimait pas travailler avec Dates, probablement parce que c'était as.vectoring. Voici donc ma solution ...
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)
}
Juste pour la documentation. Vous pouvez essayer la Stack
bibliothèque et sa fonction Stack
sous la forme suivante:
Stack(df_1, df_2)
J'ai également l'impression qu'elle est plus rapide que les autres méthodes pour les grands ensembles de données.
Vous pouvez également utiliser sjmisc::add_rows()
, qui utilise dplyr::bind_rows()
, mais contrairement à bind_rows()
, add_rows()
préserve les attributs et est donc utile pour les données étiquetées .
Voir l'exemple suivant avec un ensemble de données étiqueté. La frq()
fonction-imprime des tables de fréquences avec des étiquettes de valeur, si les données sont étiquetées.
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
et lesbind_rows()
deux suppriment silencieusement les noms de domaine.