R中的笛卡尔乘积数据框

我有三个或更多的独立variables表示为R向量,如下所示:

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

我想把它们全部的笛卡儿积作为一个数据框,如下所示:

 ABC 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 

我可以通过手动写出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)) 

但是必须有一个更优雅的方式来做到这一点,是的? itertools中的product做了一部分工作,但我找不到任何方法来吸收迭代器的输出并将其放入数据框中。 有什么build议么?

ps这个计算的下一步看起来像

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

所以如果你知道一个方法来同时做两个步骤,那也是有帮助的。

你可以使用expand.grid(A, B, C)

编辑:使用do.call来实现第二部分的替代方法是函数mdply。 这里是代码

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

用一个简单的函数“粘贴”来说明它的用法,你可以试试

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

有一个操作数据框的function,在这种情况下是有帮助的。

它可以产生各种连接(使用SQL术语),而笛卡儿积是一个特例。

您必须首先将variables转换为dataframe,因为它将dataframe作为参数。

所以这样的事情会做:

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

唯一要关心的是行不按照你描述的sorting。 您可以根据需要手动对其进行分类。

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

“如果by.x和by.y的长度为0(长度为零的vector或NULL),则结果r是x和y的笛卡尔乘积”

详情请参阅此url: http : //stat.ethz.ch/R-manual/R-patched/library/base/html/merge.html

这里有两种方法可以使用Ramnath对expand.grid的build议:

 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) 

请注意, do.calld “原样”起作用,因为data.frame是一个list 。 但do.call期望d的列名匹配f的参数名。

考虑使用美妙的data.table库performance力和速度。 它使用相当简单的统一语法处理许多plyr用例(关系组by),以及转换,子集和关系连接。

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

或在一行

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

我永远不会记得那个标准函数expand.grid 。 所以这里是另一个版本。

 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='_'))