2010-11-29 136 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)]