Combina dos marcos de datos manteniendo el orden de fila original

Quiero unir dos marcos de datos manteniendo el orden de fila original de uno de ellos ( df.2 en el ejemplo a continuación).

Aquí hay algunos datos de muestra (todos los valores de class columna de class están definidos en ambos marcos de datos):

 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 lo hago:

 merge(df.2, df.1) 

El resultado es:

  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 agrego sort = FALSE :

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

El resultado es:

  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 

Lo que me gustaría es:

  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 

Consulte la función de unión en el paquete plyr. Es como fusionar, pero le permite mantener el orden de fila de uno de los conjuntos de datos. En general, es más flexible que fusionar.

Usando su información de ejemplo, usaríamos join así:

 > 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 

Aquí hay un par de enlaces que describen las correcciones a la función de fusión para mantener el orden de las filas:

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- merged-td4296561.html

Solo necesita crear una variable que proporcione el número de fila en df.2. Luego, una vez que haya combinado sus datos, clasifique el nuevo conjunto de datos según esta variable. Aquí hay un ejemplo :

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

Desde data.table v1.9.5 + , puede hacer:

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

Realiza una combinación en la class columna descubriendo filas coincidentes en df.1 para cada fila en df.2 y extrayendo las columnas correspondientes.

También puede verificar la función inner_join en el paquete dplyr de Hadley (próxima iteración de plyr ). Conserva el orden de fila del primer conjunto de datos. La menor diferencia para la solución deseada es que también conserva el orden de columnas original del primer conjunto de datos. Por lo tanto, no necesariamente coloca la columna que utilizamos para fusionar en la primera posición.

Utilizando su ejemplo anterior, el resultado inner_join tiene este aspecto:

 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 

En aras de la integridad, la actualización en una unión también conserva el orden de la fila original. Esta podría ser una alternativa a la respuesta de data.table de Arun si solo hay unas pocas columnas para agregar:

 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 

Aquí, df.2 está unido a df.1 y gana un nuevo prob columna que se copia de las filas coincidentes de df.1 .

La respuesta aceptada propone una forma manual de mantener el orden cuando se utiliza la merge , que funciona la mayoría de las veces pero requiere un trabajo manual innecesario. Esta solución viene en la parte posterior de Cómo ddply () sin ordenar? , que trata el tema de mantener el orden pero en un contexto de combinación de aplicación dividida:

Esto apareció en la lista de correo plyr hace un tiempo (planteado por @kohske no menos) y esta es una solución ofrecida por Peter Meilstrup para casos limitados:

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

Ahora puede usar esta función genérica keeping.order para mantener el orden de fila original de una llamada 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") 

Que rendirá, según lo solicitado:

 > 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 

Entonces keeping.order automatiza de manera efectiva el enfoque en la respuesta aceptada.

Gracias a @PAC, se me ocurrió algo como esto:

 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 } 

Esto supone que desea conservar el orden del primer dataframe y el dataframe combinado tendrá el mismo número de filas que el primer dataframe. Le dará el dataframe limpio sin columnas adicionales.

En este caso específico, podría considerarnos como una solución base compacta:

 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 

No es una solución general, sin embargo, funciona si:

  1. Tienes una tabla de búsqueda que contiene valores únicos
  2. Desea actualizar una tabla, no crear una nueva
  3. la tabla de búsqueda está ordenada por la columna de fusión
  4. La tabla de búsqueda no tiene niveles adicionales
  5. Quieres un left_join
  6. Si estás bien con los factores

1 no es negociable, por el rest podemos hacer:

 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. 

Puede haber una manera más eficiente en la base. Esto sería bastante simple de convertir en una función.

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