5 votes

Fusionner les cadres de données par une correspondance dans au moins une de deux colonnes

J'ai cherché une solution et j'ai expérimenté, mais je ne parviens pas à effectuer ce qui devrait être une tâche simple.

J'ai deux cadres de données formatés de la même manière que les exemples ci-dessous.

DF1 = data.frame(A=c("cats","dogs",NA,"dogs"), B=c("kittens","puppies","kittens",NA), C=c(88,99,101,110))

    A       B           C
1   cats    kittens     88
2   dogs    puppies     99
3   NA      kittens     101
4   dogs    NA          110

DF2 = data.frame(D=c(1,2), A=c("cats","dogs"), B=c("kittens","puppies"))

    D   A       B
1   1   cats    kittens
2   2   dogs    puppies

Je souhaite fusionner les deux ensembles de données de telle sorte que la sortie soit :

      A     B         C     D
1   cats    kittens   88    1
2   dogs    puppies   99    2
3   dogs    NA        110   2
4     NA    kittens   101   1

En d'autres termes, toutes les lignes dont l'étiquette est A=="chats" ou B=="chatons" seront affectées à 1 dans la colonne D, toutes les lignes dont l'étiquette est A=="chiens" ou B=="chiots" seront affectées à 2.

J'ai utilisé la commande

merge(DF1, DF2, by=c("A","B"), all.x=TRUE)

Cependant, cela ne correspond pas aux rangées 3 et 4 correctement, seulement aux rangées 1 et 2. J'obtiens le résultat suivant

      A     B         C     D
1   cats    kittens   88    1
2   dogs    puppies   99    2
3   dogs    NA        110   NA
4     NA    kittens   101   NA

Veuillez noter que les ensembles de données sur lesquels je travaille sont très longs. En réalité, DF1 compte plus de 1 000 000 de lignes et DF2 plus de 300 000 lignes, soit des milliers de lignes chacun. J'ai donc vraiment besoin d'une solution qui puisse être mise à l'échelle.

3voto

Ananda Mahto Points 67213

Vous pouvez peut-être essayer quelque chose de ce genre :

temp <- merge(DF1, DF2, by=c("A","B"), all.x=TRUE)

within(temp, {
  M1 <- c("cats", "kittens")
  D <- ifelse(A %in% M1 | B %in% M1, 1, 2)
  rm(M1)
})
#      A       B   C D
# 1 cats kittens  88 1
# 2 dogs puppies  99 2
# 3 dogs    <NA> 110 2
# 4 <NA> kittens 101 1

Vous pouvez nicher ifelse déclarations si vous avez besoin de plus que ces deux options.

2voto

BondedDust Points 105234
DF1[which(DF1$A=="cats"|DF1$B=="kittens"), "D"] <- DF2[which(DF2$A=="cats"|DF2$B=="kittens"), "D"]
DF1[which(DF1$A=="dogs"|DF1$B=="puppies"), "D"] <- DF2[which(DF2$A=="dogs"|DF2$B=="puppies"), "D"]
DF1
#-------
     A       B   C D
1 cats kittens  88 1
2 dogs puppies  99 2
3 <NA> kittens 101 1
4 dogs    <NA> 110 2

Fonctionnalisé :

idxpick <- function(a,b) DF1[which(DF1$A==a|DF1$B==b), "D"] <<- # Yes, I feel guilty.
                                   DF2[which(DF2$A==a|DF2$B==b), "D"]
DF1 = data.frame(A=c("cats","dogs",NA,"dogs"), 
                 B=c("kittens","puppies","kittens",NA), 
                 C=c(88,99,101,110))
DF2 = data.frame(D=c(1,2), A=c("cats","dogs"), B=c("kittens","puppies"))
apply(DF2, 1, function(rr) idxpick(rr["A"], rr["B"]) )
#------------
[1] 1 2

DF1
     A       B   C D
1 cats kittens  88 1
2 dogs puppies  99 2
3 <NA> kittens 101 1
4 dogs    <NA> 110 2

2voto

Matthew Lundberg Points 21747

Voici une approche différente :

library(functional)

partial.merge <- function(DF1, DF2) {
  common.cols <- intersect(names(DF1), names(DF2))
  result.col <- names(DF2)[!(names(DF2) %in% common.cols)]

  # This can only handle one result column:
  stopifnot(length(result.col) == 1)

  # Merge in each common column, one at a time.
  # The identical operation is done for each common column, so Reduce is useful:
  r <- Reduce(function(D, C) merge(D, DF2[c(C, result.col)], by=c(C), all.x=TRUE), x=common.cols, init=DF1)

  # The merge created cols like c('D.x', 'D.y').  These are the columns:
  merge.cols <- paste(result.col, c('x', 'y'), sep='.')

  # The .x and .y columns are partial, put them together:
  r[[result.col]] <- rowMeans(r[merge.cols], na.rm=TRUE)

  # Remove the temporaries:
  for (i in merge.cols) {
    r[[i]] <- NULL
  }
  return(r)
}

partial.merge(DF1, DF2)
##         B    A   C D
## 1 kittens cats  88 1
## 2 kittens <NA> 101 1
## 3 puppies dogs  99 2
## 4    <NA> dogs 110 2

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X