Fusionnez deux blocs de données tout en conservant l’ordre des lignes d’origine

Je souhaite fusionner deux trames de données en conservant l’ordre des lignes d’origine ( df.2 dans l’exemple ci-dessous).

Voici quelques exemples de données (toutes les valeurs de class colonne de class sont définies dans les deux trames de données):

 df.1 <- data.frame(class = c(1, 2, 3), prob = c(0.5, 0.7, 0.3)) df.2 <- data.frame(object = c('A', 'B', 'D', 'F', 'C'), class = c(2, 1, 2, 3, 1)) 

Si je fais:

 merge(df.2, df.1) 

Le résultat est:

  class object prob 1 1 B 0.5 2 1 C 0.5 3 2 A 0.7 4 2 D 0.7 5 3 F 0.3 

Si j’ajoute sort = FALSE :

 merge(df.2, df.1, sort = F) 

Le résultat est:

  class object prob 1 2 A 0.7 2 2 D 0.7 3 1 B 0.5 4 1 C 0.5 5 3 F 0.3 

Ce que j’aimerais, c’est:

  class object prob 1 2 A 0.7 2 1 B 0.5 3 2 D 0.7 4 3 F 0.3 5 1 C 0.5 

Découvrez la fonction de jointure dans le package plyr. C’est comme la fusion, mais cela vous permet de conserver l’ordre des lignes de l’un des ensembles de données. Dans l’ensemble, c’est plus souple que la fusion.

En utilisant vos exemples de données, nous utiliserions la join comme ceci:

 > join(df.2,df.1) Joining by: class object class prob 1 A 2 0.7 2 B 1 0.5 3 D 2 0.7 4 F 3 0.3 5 C 1 0.5 

Voici quelques liens décrivant les correctifs à la fonction de fusion pour conserver l’ordre des lignes:

http://www.r-statistics.com/2012/01/merging-two-data-frame-objects-while-preserving-the-rows-order/

http://r.789695.n4.nabble.com/patching-merge-tow-the-user-to-keep-the-order-of-one-of-the-two-data-frame-objects- fusion-td4296561.html

Il suffit de créer une variable qui donne le numéro de ligne dans df.2. Ensuite, une fois vos données fusionnées, vous sortingez le nouvel dataset en fonction de cette variable. Voici un exemple :

 df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3)) df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1)) df.2$id <- 1:nrow(df.2) out <- merge(df.2,df.1, by = "class") out[order(out$id), ] 

De data.table v1.9.5 + , vous pouvez faire:

 require(data.table) # v1.9.5+ setDT(df.1)[df.2, on="class"] 

La class effectue une jointure sur la colonne en recherchant des lignes correspondantes dans df.1 pour chaque ligne de df.2 et en extrayant les colonnes correspondantes.

Vous pouvez également vérifier la fonction inner_join dans le package dplyr de Hadley (prochaine itération de plyr ). Il conserve l’ordre des lignes du premier dataset. La différence mineure par rapport à la solution souhaitée est qu’elle préserve également l’ordre des colonnes d’origine du premier dataset. Donc, cela ne met pas nécessairement la colonne que nous avons utilisée pour la fusion à la première position.

En utilisant votre exemple ci-dessus, le résultat inner_join ressemble à ceci:

 inner_join(df.2,df.1) Joining by: "class" object class prob 1 A 2 0.7 2 B 1 0.5 3 D 2 0.7 4 F 3 0.3 5 C 1 0.5 

Par souci d’exhaustivité, la mise à jour dans une jointure préserve également l’ordre des lignes d’origine. Cela pourrait être une alternative à la réponse data.table d’ Arun s’il n’y a que quelques colonnes à append:

 library(data.table) setDT(df.2)[df.1, on = "class", prob := i.prob][] 
  object class prob 1: A 2 0.7 2: B 1 0.5 3: D 2 0.7 4: F 3 0.3 5: C 1 0.5 

Ici, df.2 est à droite de df.1 et gagne une nouvelle colonne prob qui est copiée à partir des lignes correspondantes de df.1 .

La réponse acceptée propose un moyen manuel de garder l’ordre lors de l’utilisation de la merge , qui fonctionne la plupart du temps mais nécessite un travail manuel inutile. Cette solution vient à la suite de Comment ddply () sans sortinger? , qui traite de la question du maintien de l’ordre mais dans un contexte de split-apply-combine:

Cela est apparu sur la liste de diffusion de plyr (soulevé par @kohske pas moins) et c’est une solution proposée par Peter Meilstrup pour des cas limités:

 #Peter's version used a function gensym to # create the col name, but I couldn't track down # what package it was in. keeping.order <- function(data, fn, ...) { col <- ".sortColumn" data[,col] <- 1:nrow(data) out <- fn(data, ...) if (!col %in% colnames(out)) stop("Ordering column not preserved by function") out <- out[order(out[,col]),] out[,col] <- NULL out } 

Donc, maintenant, vous pouvez utiliser cette fonction générique keeping.order pour conserver l'ordre des lignes d'origine d'un appel de merge :

 df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3)) df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1)) keeping.order(df.2, merge, y=df.1, by = "class") 

Qui donnera, comme demandé:

 > keeping.order(df.2, merge, y=df.1, by = "class") class object id prob 3 2 A 1 0.7 1 1 B 2 0.5 4 2 D 3 0.7 5 3 F 4 0.3 2 1 C 5 0.5 

Donc, keeping.order automatise efficacement l'approche dans la réponse acceptée.

Grâce à @PAC, j’ai trouvé quelque chose comme ceci:

 merge_sameord = function(x, y, ...) { UseMethod('merge_sameord') } merge_sameord.data.frame = function(x, y, ...) { rstr = paste(sample(c(0:9, letters, LETTERS), 12, replace=TRUE), collapse='') x[, rstr] = 1:nrow(x) res = merge(x, y, all.x=TRUE, sort=FALSE, ...) res = res[order(res[, rstr]), ] res[, rstr] = NULL res } 

Cela suppose que vous souhaitiez conserver la commande du premier bloc de données et que le bloc de données fusionné ait le même nombre de lignes que le premier bloc de données. Il vous donnera le bloc de données propre sans colonnes supplémentaires.

Dans ce cas précis, vous pourriez nous demander une solution de base compacte:

 df.2$prob = factor(df.2$class,labels=df.1$prob) df.2 # object class prob # 1 A 2 0.7 # 2 B 1 0.5 # 3 D 2 0.7 # 4 F 3 0.3 # 5 C 1 0.5 

Pas une solution générale cependant, cela fonctionne si:

  1. Vous avez une table de recherche contenant des valeurs uniques
  2. Vous voulez mettre à jour une table, pas en créer une nouvelle
  3. la table de recherche est sortingée par la colonne de fusion
  4. La table de consultation n’a pas de niveaux supplémentaires
  5. Vous voulez un left_join
  6. Si vous êtes d’accord avec les facteurs

1 n’est pas négociable, pour le rest nous pouvons faire:

 df.3 <- df.2 # deal with 2. df.1b <- df.1[order(df.1$class),] # deal with 3 df.1b <- df.1b[df.1$class %in% df.2$class,] # deal with 4. df.3$prob = factor(df.3$class,labels=df.1b$prob) df.3 <- df3[!is.na(df.3$prob),] # deal with 5. if you want an `inner join` df.3$prob <- as.numeric(as.character(df.3$prob)) # deal with 6. 

Il peut y avoir un moyen plus efficace dans la base. Ce serait assez simple à faire dans une fonction.

 varorder <- names(mydata) # --- Merge mydata <- merge(mydata, otherData, by="commonVar") restOfvars <- names(mydata[!(names(mydata) %in% varorder)]) mydata[c(varorder,restOfvars)]