Unisci due frame di dati mantenendo l’ordine delle righe originale

Voglio unire due frame di dati mantenendo l’ordine di riga originale di uno di essi ( df.2 nell’esempio seguente).

Ecco alcuni dati di esempio (tutti i valori della colonna della class sono definiti in entrambi i frame di dati):

 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)) 

Se lo faccio:

 merge(df.2, df.1) 

Il risultato è:

  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 

Se aggiungo sort = FALSE :

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

Il risultato è:

  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 

Quello che vorrei è:

  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 

Controlla la funzione di join nel pacchetto plyr. È come unire, ma ti consente di mantenere l’ordine delle righe di uno dei set di dati. Nel complesso, è più flessibile dell’unione.

Usando i tuoi dati di esempio, utilizzeremmo join questo modo:

 > 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 

Ecco un paio di link che descrivono le correzioni alla funzione di unione per mantenere l’ordine delle righe:

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-to-allow-the-user-to-keep-the-order-of-one-of-the-two-data-frame-objects- fusione-td4296561.html

Hai solo bisogno di creare una variabile che dia il numero di riga in df.2. Quindi, una volta uniti i dati, si ordina il nuovo set di dati in base a questa variabile. Ecco un esempio:

 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), ] 

Da data.table v1.9.5 + , puoi fare:

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

Esegue un join sulla class colonna individuando le righe corrispondenti in df.1 per ciascuna riga in df.2 ed estraendo le colonne corrispondenti.

Puoi anche controllare la funzione inner_join nel pacchetto dplyr di Hadley (prossima iterazione di plyr ). Conserva l’ordine delle righe del primo set di dati. La piccola differenza rispetto alla soluzione desiderata è che conserva anche l’ordine di colonne originale del primo set di dati. Quindi non mette necessariamente la colonna che abbiamo usato per la fusione nella prima posizione.

Usando il tuo esempio sopra, il risultato inner_join assomiglia a questo:

 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 

Per completezza, l’ aggiornamento in un join mantiene anche l’ordine delle righe originale. Questa potrebbe essere un’alternativa alla risposta data.table di Arun se ci sono solo poche colonne da aggiungere:

 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 

Qui, df.2 è correttamente associato a df.1 e ottiene un nuovo df.1 colonna che viene copiato dalle righe corrispondenti di df.1 .

La risposta accettata propone un modo manuale per mantenere l’ordine quando si utilizza l’ merge , che funziona la maggior parte delle volte ma richiede un lavoro manuale non necessario. Questa soluzione si trova sul retro di How to ddply () senza ordinare? , che si occupa del problema di mantenere l’ordine ma in un contesto di combinazione split-apply-combine:

Questo è comparso sulla mailing list plyr qualche tempo fa (sollevato da @kohske non meno) e questa è una soluzione offerta da Peter Meilstrup per casi limitati:

 #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 } 

Quindi ora puoi usare questa generica funzione keeping.order per mantenere l'ordine di riga originale di una chiamata di 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") 

Quale produrrà, come richiesto:

 > 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 

Quindi keeping.order automatizza efficacemente l'approccio nella risposta accettata.

Grazie a @PAC, ho trovato qualcosa di simile:

 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 } 

Ciò presuppone che si desidera conservare l’ordine nel primo frame di dati e il frame di dati uniti avrà lo stesso numero di righe del primo frame di dati. Ti darà il quadro dati pulito senza colonne aggiuntive.

In questo caso specifico potremmo noi factor per una soluzione di base compatta:

 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 

Tuttavia, non è una soluzione generale, funziona se:

  1. Hai una tabella di ricerca contenente valori unici
  2. Vuoi aggiornare una tabella, non crearne una nuova
  3. la tabella di ricerca è ordinata dalla colonna di unione
  4. La tabella di ricerca non ha livelli aggiuntivi
  5. Vuoi un left_join
  6. Se stai bene con i fattori

1 non è negoziabile, per il resto possiamo fare:

 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. 

Potrebbe esserci un modo più efficiente in base. Questo sarebbe abbastanza semplice da trasformare in una funzione.

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