2010-11-29 23 views
53

He tres o más variables independientes representados como vectores R, así:trama de datos producto cartesiano en I

A <- c(1,2,3) 
B <- factor(c('x','y')) 
C <- c(0.1,0.5) 

y quiero tomar el producto cartesiano de todos ellos y poner el resultado en un conjunto de datos marco, de esta manera:

A B C 
1 x 0.1 
1 x 0.5 
1 y 0.1 
1 y 0.5 
2 x 0.1 
2 x 0.5 
2 y 0.1 
2 y 0.5 
3 x 0.1 
3 x 0.5 
3 y 0.1 
3 y 0.5 

me puede hacer esto escribiendo manualmente llamadas a rep:

d <- data.frame(A = rep(A, times=length(B)*length(C)), 
       B = rep(B, times=length(A), each=length(C)), 
       C = rep(C, each=length(A)*length(B)) 

pero debe haber una forma más elegante de hacerlo, ¿sí? product en itertools hace parte del trabajo, pero no puedo encontrar ninguna manera de absorber la salida de un iterador y ponerlo en un marco de datos. ¿Alguna sugerencia?

p.s. El siguiente paso en este cálculo parece

d$D <- f(d$A, d$B, d$C) 

así que si conoces una manera de hacer las dos pasos a la vez, que también serían útiles.

+0

sería útil si especificas qué hace la función f. – Ramnath

+0

'f' es un marcador de posición para uno de varios cálculos matemáticos diferentes, pero a los efectos de esta pregunta, creo * que lo que necesita saber es que todos toman N vectores del tipo apropiado y producen un vector; todas las entradas deben tener la misma longitud y la salida también es de esa longitud. – zwol

+0

Yo recomendaría cambiar el título de esta pregunta ... "tabla de datos" ahora significa algo diferente en R. –

Respuesta

57

puede utilizar

EDIT: una alternativa al uso do.call para lograr la segunda parte, es la función mdply. aquí está el código

d = expand.grid(x = A, y = B, z = C) 
d = mdply(d, f) 

para ilustrar su uso mediante una función trivial 'pegar', puede intentar

d = mdply(d, 'paste', sep = '+'); 
+0

Aha! Sabía que tenía que haber una rutina de biblioteca estándar que hiciera esto, pero no podía encontrar cómo se llamaba. Sin embargo, voy a dejar la pregunta abierta en caso de que alguien tenga una respuesta para la segunda parte. – zwol

+0

si f es una función personalizada, entonces puede modificarla para aceptar un marco de datos como argumento y dejar que la función maneje la división en vectores componentes – Ramnath

+0

Estaba mirando la documentación de plyr, pero no entendió que esto era lo que 'mdply 'era para. Gracias. – zwol

0

nunca puedo recordar que la función estándar expand.grid. Así que aquí hay otra versión.

crossproduct <- function(...,FUN='data.frame') { 
    args <- list(...) 
    n1 <- names(args) 
    n2 <- sapply(match.call()[1+1:length(args)], as.character) 
    nn <- if (is.null(n1)) n2 else ifelse(n1!='',n1,n2) 
    dims <- sapply(args,length) 
    dimtot <- prod(dims) 
    reps <- rev(cumprod(c(1,rev(dims))))[-1] 
    cols <- lapply(1:length(dims), function(j) 
       args[[j]][1+((1:dimtot-1) %/% reps[j]) %% dims[j]]) 
    names(cols) <- nn 
    do.call(match.fun(FUN),cols) 
} 

A <- c(1,2,3) 
B <- factor(c('x','y')) 
C <- c(.1,.5) 

crossproduct(A,B,C) 

crossproduct(A,B,C, FUN=function(...) paste(...,sep='_')) 
5

Aquí está una manera de hacer ambas cosas, mediante la sugerencia de Ramnath de expand.grid:

f <- function(x,y,z) paste(x,y,z,sep="+") 
d <- expand.grid(x=A, y=B, z=C) 
d$D <- do.call(f, d) 

Tenga en cuenta que do.call trabajos sobre d "tal cual" porque un data.frame es una list. Pero do.call espera que los nombres de columna de d coincidan con los nombres de argumento de f.

+0

'd' solo está definido por la llamada' expand.grid' ... – zwol

+0

@Zack: Gracias; He actualizado mi respuesta. No es de una sola línea, pero evaluar 'f' es aún más fácil con' do.call' que escribir en cada argumento. –

+0

el buen viejo truco de do.call. ¡Buena esa! – Ramnath

13

Hay una función que manipula el marco de datos, que es útil en este caso.

Puede producir varios join (en terminología SQL), mientras que el producto cartesiano es un caso especial.

Primero tiene que convertir las varibles en marcos de datos, ya que toma el marco de datos como parámetros.

así que algo como esto va a hacer:

A.B=merge(data.frame(A=A), data.frame(B=B),by=NULL); 
A.B.C=merge(A.B, data.frame(C=C),by=NULL); 

La única cosa a tener en cuenta es que las filas no se ordenan a medida que se representa. Puede ordenarlos manualmente como lo desee.

merge(x, y, by = intersect(names(x), names(y)), by.x = by, by.y = by, all = FALSE, all.x = all, all.y = all, sort = TRUE, suffixes = c(".x",".y"), incomparables = NULL, ...)

"Si por o ambos by.x y por.y son de longitud 0 (un vector de longitud cero o NULL), el resultado, r, es el producto cartesiano de X e Y"

ver este URL para el detalle: http://stat.ethz.ch/R-manual/R-patched/library/base/html/merge.html

3

considerar el uso de la maravillosa data.table biblioteca para la expresividad y la velocidad. Se ocupa de muchos casos de uso plyr (grupo relacional), junto con la transformada, de subconjuntos y unión relacional utilizando una sintaxis uniforme bastante simple.

library(data.table) 
d <- CJ(x=A, y=B, z=C) # Cross join 
d[, w:=f(x,y,z)] # Mutates the data.table 

o en una línea

d <- CJ(x=A, y=B, z=C)[, w:=f(x,y,z)] 
Cuestiones relacionadas