web-dev-qa-db-ja.com

デカルト積データフレーム

次のように、Rベクトルとして表される3つ以上の独立変数があります。

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は仕事の一部ですが、イテレータの出力を吸収してデータフレームに入れる方法が見つかりません。助言がありますか?

追伸この計算の次のステップは次のようになります

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

したがって、両方のステップを一度に実行する方法を知っていれば、それも役立ちます。

56
zwol

expand.grid(A, B, C)を使用できます

編集:do.callを使用して2番目の部分を達成する代わりに、mdply関数があります。ここにコードがあります

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

ささいな関数「貼り付け」を使用してその使用法を説明するには、試すことができます

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

データフレームを操作する関数があり、この場合に役立ちます。

デカルト積は特殊なケースですが、さまざまな結合(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またはby.yが長さ0(長さゼロのベクトルまたはNULL)である場合、結果のrはxとyのデカルト積です」

詳細については、次のURLを参照してください。 http://stat.ethz.ch/R-manual/R-patched/library/base/html/merge.html

16
misssprite

Ramnathの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)

do.calldであるため、data.framelistで「そのまま」動作することに注意してください。ただし、do.callは、dの列名がfの引数名と一致することを想定しています。

5
Joshua Ulrich

表現力と速度のために素晴らしい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

または1行で

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

ライブラリtidyrを使用すると、tidyr::crossing(注文はOPのようになります):

library(tidyr)
crossing(A,B,C)
# A tibble: 12 x 3
#        A B         C
#    <dbl> <fct> <dbl>
#  1     1 x       0.1
#  2     1 x       0.5
#  3     1 y       0.1
#  4     1 y       0.5
#  5     2 x       0.1
#  6     2 x       0.5
#  7     2 y       0.1
#  8     2 y       0.5
#  9     3 x       0.1
# 10     3 x       0.5
# 11     3 y       0.1
# 12     3 y       0.5 

次のステップは、tidyverse、特にpurrr::pmap* 家族:

library(tidyverse)
crossing(A,B,C) %>% mutate(D = pmap_chr(.,paste,sep="_"))
# A tibble: 12 x 4
#        A B         C D      
#    <dbl> <fct> <dbl> <chr>  
#  1     1 x       0.1 1_1_0.1
#  2     1 x       0.5 1_1_0.5
#  3     1 y       0.1 1_2_0.1
#  4     1 y       0.5 1_2_0.5
#  5     2 x       0.1 2_1_0.1
#  6     2 x       0.5 2_1_0.5
#  7     2 y       0.1 2_2_0.1
#  8     2 y       0.5 2_2_0.5
#  9     3 x       0.1 3_1_0.1
# 10     3 x       0.5 3_1_0.5
# 11     3 y       0.1 3_2_0.1
# 12     3 y       0.5 3_2_0.5
4

sqldfでのクロス結合の使用:

library(sqldf)

A <- data.frame(c1 = c(1,2,3))
B <- data.frame(c2 = factor(c('x','y')))
C <- data.frame(c3 = c(0.1,0.5))

result <- sqldf('SELECT * FROM (A CROSS JOIN B) CROSS JOIN C') 
0
OmG

その標準関数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='_'))
0
DamonJW