Saltar al contenido

¿Cómo realizar una unión cruzada en R?

Solución:

Si la velocidad es un problema, sugiero que revise el excelente data.table paquete. En el ejemplo al final, es ~ 90 veces más rápido que merge.

No proporcionaste datos de ejemplo. Si solo desea obtener todas las combinaciones de dos (o más columnas individuales), puede usar CJ (unión cruzada):

library(data.table)
CJ(x=1:2,y=letters[1:3])
#   x y
#1: 1 a
#2: 1 b
#3: 1 c
#4: 2 a
#5: 2 b
#6: 2 c

Si desea hacer una combinación cruzada en dos tablas, no he encontrado una forma de usar CJ (). Pero aún puedes usar data.table:

x2<-data.table(id1=letters[1:3],vals1=1:3)
y2<-data.table(id2=letters[4:7],vals2=4:7)

res<-setkey(x2[,c(k=1,.SD)],k)[y2[,c(k=1,.SD)],allow.cartesian=TRUE][,k:=NULL]
res
#    id1 vals1 id2 vals2
# 1:   a     1   d     4
# 2:   b     2   d     4
# 3:   c     3   d     4
# 4:   a     1   e     5
# 5:   b     2   e     5
# 6:   c     3   e     5
# 7:   a     1   f     6
# 8:   b     2   f     6
# 9:   c     3   f     6
#10:   a     1   g     7
#11:   b     2   g     7
#12:   c     3   g     7

Explicación del res línea:

  • Básicamente, agrega una columna ficticia (k en este ejemplo) a una tabla y la establece como clave (setkey(tablename,keycolumns)), agregue la columna ficticia a la otra tabla y luego únalas.
  • La estructura data.table usa posiciones de columna y no nombres en la combinación, por lo que debe colocar la columna ficticia al principio. los c(k=1,.SD) parte es una forma que he encontrado para agregar columnas al principio (el valor predeterminado es agregarlas al final).
  • Una combinación de data.table estándar tiene un formato de X[Y]. La X en este caso es setkey(x2[,c(k=1,.SD)],k), y la Y es y2[,c(k=1,.SD)].
  • allow.cartesian=TRUE dice data.table para ignorar los valores clave duplicados y realizar una unión cartesiana (las versiones anteriores no requerían esto)
  • los [,k:=NULL] al final, simplemente elimina la clave ficticia del resultado.

También puede convertir esto en una función, por lo que es más limpio de usar:

# Version 1; easier to write:
CJ.table.1 <- function(X,Y)
  setkey(X[,c(k=1,.SD)],k)[Y[,c(k=1,.SD)],allow.cartesian=TRUE][,k:=NULL]

CJ.table.1(x2,y2)
#    id1 vals1 id2 vals2
# 1:   a     1   d     4
# 2:   b     2   d     4
# 3:   c     3   d     4
# 4:   a     1   e     5
# 5:   b     2   e     5
# 6:   c     3   e     5
# 7:   a     1   f     6
# 8:   b     2   f     6
# 9:   c     3   f     6
#10:   a     1   g     7
#11:   b     2   g     7
#12:   c     3   g     7

# Version 2; faster but messier:
CJ.table.2 <- function(X,Y) {
  eval(parse(text=paste0("setkey(X[,c(k=1,.SD)],k)[Y[,c(k=1,.SD)],list(",paste0(unique(c(names(X),names(Y))),collapse=","),")][,k:=NULL]")))
}

Aquí hay algunos puntos de referencia de velocidad:

# Create a bigger (but still very small) example:
n<-1e3
x3<-data.table(id1=1L:n,vals1=sample(letters,n,replace=T))
y3<-data.table(id2=1L:n,vals2=sample(LETTERS,n,replace=T))

library(microbenchmark)
microbenchmark(merge=merge.data.frame(x3,y3,all=TRUE),
               CJ.table.1=CJ.table.1(x3,y3),
               CJ.table.2=CJ.table.2(x3,y3),
               times=3, unit="s")
#Unit: seconds
#       expr        min         lq     median         uq        max neval
#      merge 4.03710225 4.23233688 4.42757152 5.57854711 6.72952271     3
# CJ.table.1 0.06227603 0.06264222 0.06300842 0.06701880 0.07102917     3
# CJ.table.2 0.04740142 0.04812997 0.04885853 0.05433146 0.05980440     3

Tenga en cuenta que estos data.table Los métodos son mucho más rápidos que los merge método sugerido por @ danas.zuokas. Las dos tablas con 1000 filas en este ejemplo dan como resultado una tabla cruzada con 1 millón de filas. Entonces, incluso si sus tablas originales son pequeñas, el resultado puede crecer rápidamente y la velocidad se vuelve importante.

Por último, las versiones recientes de data.table requiere que agregue el allow.cartesian=TRUE (como en CJ.table.1) o especifique los nombres de las columnas que deben devolverse (CJ.table.2). El segundo método (CJ.table.2) parece ser más rápido, pero requiere un código más complicado si desea especificar automáticamente todos los nombres de las columnas. Y puede que no funcione con nombres de columna duplicados. (No dude en sugerir una versión más simple de CJ.table.2)

Es solo all=TRUE?

x<-data.frame(id1=c("a","b","c"),vals1=1:3)
y<-data.frame(id2=c("d","e","f"),vals2=4:6)
merge(x,y,all=TRUE)

De la documentación de merge:

Si by o ambos by.xy by.y son de longitud 0 (un vector de longitud cero o NULL), el resultado, r, es el producto cartesiano de xey, es decir, dim (r) = c (nrow (x ) * nrow (y), ncol (x) + ncol (y)).

Esto se preguntó hace años, pero puede usar tidyr::crossing() para hacer una unión cruzada. Definitivamente la solución más simple del grupo.

library(tidyr)

league <- c("MLB", "NHL", "NFL", "NBA")
season <- c("2018", "2017")

tidyr::crossing(league, season)
#> # A tibble: 8 x 2
#>   league season
#>   <chr>  <chr> 
#> 1 MLB    2017  
#> 2 MLB    2018  
#> 3 NBA    2017  
#> 4 NBA    2018  
#> 5 NFL    2017  
#> 6 NFL    2018  
#> 7 NHL    2017  
#> 8 NHL    2018

Creado el 2018-12-08 por el paquete reprex (v0.2.0).

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *