2010-11-29 110 views
53

我有三个或更多的自变量表示为R矢量,像这样:笛卡尔乘积数据帧

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

和我想利用它们的所有的笛卡尔乘积,并把该结果到数据帧,这样的:

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 

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

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

p.s.在这个计算中的下一个步骤看起来像

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

所以如果你知道一种方法同时做两个步骤,这也将是有益的。

+0

如果你指定了函数f所做的工作,它将会很有用。 – Ramnath 2010-11-30 00:07:00

+0

`f`是一个占位符,用于几个不同的多毛数学计算之一,但为了这个问题的目的,我认为*你需要知道的是他们都需要N个适当类型的向量并产生一个向量;所有的输入必须是相同的长度,输出也是这个长度。 – zwol 2010-11-30 00:10:05

+0

我建议改变这个问题的标题......“数据表”现在意味着R中的不同。 – 2015-11-11 14:44:18

回答

57

可以使用

编辑:使用do.call以实现第二部分的替代,是mdply功能。这里是代码

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

说明使用一个简单的功能“粘贴”它的使用,你可以尝试

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

我永远记得标准功能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='_')) 
5

这里有一个办法做到两者使用的expand.grid Ramnath的建议:

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.call作品上d“原样”因为data.framelist。但do.call预计d的列名称与参数名称f匹配。

13

有一个操作数据框的功能,在这种情况下这很有帮助。

它可以产生各种连接(在SQL术语中),而笛卡儿积是一种特殊情况。

您必须首先将变量转换为数据帧,因为它将数据帧作为参数。

所以这样的事情会做:

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); 

关心的唯一事情是,行不排序为你描绘。 您可以根据需要手动对它们进行排序。

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和。y是长度为0(长度零矢量或NULL),其结果中,r的,是x和y”

看到此URL详细的笛卡尔乘积:http://stat.ethz.ch/R-manual/R-patched/library/base/html/merge.html

3

考虑使用妙data.table库的表现力和速度。它可以处理许多plyr用例(由关系组),以及变换,子集和关系加入用一个相当简单的一致的语法。

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