Est-il possible de lier deux trames de données ne contenant pas le même ensemble de colonnes? J'espère conserver les colonnes qui ne correspondent pas après la liaison.
rbind.fill
du paquet plyr
pourrait être ce que vous cherchez.
Une solution plus récente consiste à utiliser la fonction bind_rows
de dplyr
qui, je suppose, est plus efficace que smartbind
.
Vous pouvez utiliser smartbind
à partir du package gtools
.
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
Si les colonnes dans df1 est un sous-ensemble de celles dans df2 (par noms de colonnes):
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 dans data.table
tant que les objets sont convertis en objets data.table
, donc
rbind(setDT(df1), setDT(df2), fill=TRUE)
travaillera également dans cette situation. Cela peut être préférable lorsque vous avez quelques tables de données et que vous ne voulez pas créer de liste.
Vous pouvez également extraire les noms de colonnes communs.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
La plupart des réponses de base R concernent le cas où un seul fichier data.frame a des colonnes supplémentaires ou que le fichier data.frame résultant aurait l'intersection des colonnes. Comme le PO écrit j'espère conserver les colonnes qui ne correspondent pas après le bind, 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 de base R: une méthode qui modifie les données.frames d'origine et une méthode qui ne modifie pas. De plus, je propose une méthode qui généralise la méthode non destructive à plus de deux data.frames.
Premièrement, obtenons des 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 images. Modifier les originaux
Afin de conserver toutes les colonnes des deux data.frames dans une rbind
(et de permettre à la fonction de fonctionner sans entraîner d'erreur), vous ajoutez des colonnes NA à chaque data.frame avec les noms manquants appropriés renseignés avec 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 données d'origine data.frames, df1 et df2, en ajoutant l'ensemble complet des colonnes aux deux.
Deux images.frames, ne modifie pas les originaux
Pour conserver les données.frames d'origine intactes, commencez par parcourir les noms qui diffèrent, renvoyez un vecteur nommé d'AN concaténé dans une liste contenant le nom data.frame à l'aide de c
. Ensuite, data.frame
convertit le résultat en un fichier data.frame approprié pour la variable 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)))
)
Plusieurs images.frames, ne modifie pas les originaux
Dans le cas où vous avez plus de deux data.frames, 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 de 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))
J'ai écrit une fonction pour le faire parce que j'aime bien que mon code me dise si quelque chose ne va pas. Cette fonction vous indiquera explicitement quels noms de colonne 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 données.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)
}
Juste pour la documentation. Vous pouvez essayer la bibliothèque Stack
et sa fonction Stack
sous la forme suivante:
Stack(df_1, df_2)
J'ai aussi l'impression que c'est plus rapide que les autres méthodes pour les grands ensembles de données.
J'ai peut-être mal interprété 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 un left join
ou un right join
semblable à une requête SQL. R possède la fonction merge
qui vous permet de spécifier des jointures à gauche, à droite ou internes similaires à des jointures de tables en SQL.
Il existe déjà une excellente question et une réponse à ce sujet ici: Comment joindre (fusionner) des trames de données (intérieure, extérieure, gauche, droite)?
gtools/smartbind n'aimait pas travailler avec Dates, probablement parce que c'était as.vectoring. Alors voici 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)
}
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 label data .
Voir l'exemple suivant avec un jeu de données étiqueté. La fonction frq()
- imprime les tables de fréquences avec les étiquettes de valeur, if 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