2011-05-25 22 views
6

Tengo dos trama de datos en R.fusionar dos trama de datos basada en la coincidencia de dos columnas intercambiables en cada trama de datos

trama de datos 1

A B C D E F G 
1 2 a a a a a 
2 3 b b b c c 
4 1 e e f f e 

trama de datos 2

X Y Z 
1 2 g 
2 1 h 
3 4 i 
1 4 j 

Quiero coincidir con la columna de dataframe1 A y B con las columnas X e Y del dataframe2 NO son comparaciones por pares, es decir, la fila 1 (A = 1 B = 2) se considera igual que la fila 1 (X = 1, Y = 2) y la fila 2 (X = 2, Y = 1) del dataframe 2.

Cuando se puede encontrar la coincidencia, me gustaría agregar las columnas C, D, E, F de dataframe1 a la fila coincidente de dataframe2, de la siguiente manera: sin concordancia como na.

trama de datos final

X Y Z C D E F G 
1 2 g a a a a a 
2 1 h a a a a a 
3 4 i na na na na na 
1 4 j e e f f e 

sólo puedo saber cómo hacer a juego para una sola columna, sin embargo, cómo hacer juego para dos columnas intercambiables y la fusión de dos tramas de datos en base a los resultados de coincidencias es difícil para mí. Pls amablemente ayuda a ofrecer una forma inteligente de hacer esto.

Para facilitar la discusión (gracias por los comentarios de Vincent y Dwin (mi quesiton anterior) que deba probar la cita.) Hay la cuota de trama de datos de carga 1 y 2 a R.

df1 <- data.frame(A = c(1,2,4), B=c(2,3,1), C=c('a','b','e'), 
           D=c('a','b','e'), E=c('a','b','f'), 
           F=c('a','c','f'), G=c('a','c', 'e')) 

df2 <- data.frame(X = c(1,2,3,1), Y=c(2,1,4,4), Z=letters[7:10]) 
+1

¿Cómo manejas las colisiones? Es decir, ¿cuándo df1 tiene X = 1 e Y = 2 y X = 2 e Y = 1 en filas? ¿Estás garantizado que eso no sucederá? – mcpeterson

Respuesta

6

Lo siguiente funciona, pero sin duda se puede mejorar.

Primero creo una pequeña función de ayuda que realiza una clasificación por filas en A y B (y la renombra a V1 y V2).

replace_index <- function(dat){ 
    x <- as.data.frame(t(sapply(seq_len(nrow(dat)), 
    function(i)sort(unlist(dat[i, 1:2]))))) 
    names(x) <- paste("V", seq_len(ncol(x)), sep="") 
    data.frame(x, dat[, -(1:2), drop=FALSE]) 
} 

replace_index(df1) 

    V1 V2 C D E F G 
1 1 2 a a a a a 
2 2 3 b b b c c 
3 1 4 e e f f e 

Esto significa que puede utilizar una recta de avance merge para combinar los datos.

merge(replace_index(df1), replace_index(df2), all.y=TRUE) 

    V1 V2 C D E F G Z 
1 1 2 a a a a a g 
2 1 2 a a a a a h 
3 1 4 e e f f e j 
4 3 4 <NA> <NA> <NA> <NA> <NA> i 
1

Esto es ligeramente torpe, y tiene algunos posibles problemas de colisión y el orden, pero funciona con su ejemplo

df1a <- df1; df1a$A <- df1$B; df1a$B <- df1$A #reverse A and B 
merge(df2, rbind(df1,df1a), by.x=c("X","Y"), by.y=c("A","B"), all.x=TRUE) 

para producir

X Y Z C D E F G 
1 1 2 g a a a a a 
2 1 4 j e e f f e 
3 2 1 h a a a a a 
4 3 4 i <NA> <NA> <NA> <NA> <NA> 
1

Un enfoque podría ser la creación de una clave id para emparejar eso es invariante de orden.

# create id key to match 
require(plyr) 
df1 = adply(df1, 1, transform, id = paste(min(A, B), "-", max(A, B))) 
df2 = adply(df2, 1, transform, id = paste(min(X, Y), "-", max(X, Y))) 

# combine data frames using `match` 
cbind(df2, df1[match(df2$id, df1$id),3:7]) 

Esto produce la salida

X Y Z id C D E F G 
1 1 2 g 1 - 2 a a a a a 
1.1 2 1 h 1 - 2 a a a a a 
NA 3 4 i 3 - 4 <NA> <NA> <NA> <NA> <NA> 
3 1 4 j 1 - 4 e e f f e 
0

También puede unirse a las tablas en ambos sentidos (X == A y Y == B, entonces X == B y Y == A) y rbind ellos. Esto producirá pares duplicados donde una forma produjo una coincidencia y la otra proporcionó NA, por lo que luego reduciría los duplicados cortando solo una fila por cada combinación X-Y, la que no tiene NA, si existe.

library(dplyr) 
m <- left_join(df2,df1,by = c("X" = "A","Y" = "B")) 
n <- left_join(df2,df1,by = c("Y" = "A","X" = "B")) 

rbind(m,n) %>% 
    group_by(X,Y) %>% 
    arrange(C,D,E,F,G) %>% # sort to put NA rows on bottom of pairs 
    slice(1) # take top row from combination 

Produce:

Source: local data frame [4 x 8] 
Groups: X, Y 

    X Y Z C D E F G 
1 1 2 g a a a a a 
2 1 4 j e e f f e 
3 2 1 h a a a a a 
4 3 4 i NA NA NA NA NA 
0

Aquí hay otra solución posible en base de R. Esta solución cbind() s nuevas columnas clave (K1 y K2) a ambos data.frames utilizando los vectorizados pmin() y pmax() funciones para derivar el orden canónico de las columnas clave, y se funde en aquellas:

merge(cbind(df2,K1=pmin(df2$X,df2$Y),K2=pmax(df2$X,df2$Y)),cbind(df1,K1=pmin(df1$A,df1$B),K2=pmax(df1$A,df1$B)),all.x=T)[,-c(1:2,6:7)]; 
## X Y Z C D E F G 
## 1 1 2 g a a a a a 
## 2 2 1 h a a a a a 
## 3 1 4 j e e f f e 
## 4 3 4 i <NA> <NA> <NA> <NA> <NA> 

Tenga en cuenta que el uso de pmin() y pmax() solo es posible para este problema porque solo tiene dos columnas de clave; si tuviera más, entonces tendría que usar algún tipo de solución aplicar + ordenar para lograr el orden de la clave canónica para la fusión, similar a lo que hace @Andrie en su función auxiliar, que funcionaría para cualquier número de columnas clave, pero sería menos eficiente.