37

R でクロス結合を実現するにはどうすればよいですか? 「マージ」が内部結合、外部結合を実行できることを私は知っています。しかし、R でクロス結合を実現する方法がわかりません。

ありがとう

4

10 に答える 10

59

data.table速度が問題になる場合は、優れたパッケージをチェックすることをお勧めします. 最後の例では、 よりも ~90 倍高速ですmerge

サンプルデータを提供していません。2 つ (またはそれ以上の個々の) 列のすべての組み合わせを取得したいだけの場合は、CJ(クロス結合)を使用できます。

library(data.table)
CJ(x=1:2,y=letters[1:3])
#   x y
#1: 1 a
#2: 1 b
#3: 1 c
#4: 2 a
#5: 2 b
#6: 2 c

2 つのテーブルでクロス結合を行いたい場合、CJ() を使用する方法が見つかりませんでした。ただし、引き続き使用できますdata.table

x2<-data.table(id1=letters[1:3],vals1=1:3)
y2<-data.table(id2=letters[4:7],vals2=4:7)

res<-setkey(x2[,c(k=1,.SD)],k)[y2[,c(k=1,.SD)],allow.cartesian=TRUE][,k:=NULL]
res
#    id1 vals1 id2 vals2
# 1:   a     1   d     4
# 2:   b     2   d     4
# 3:   c     3   d     4
# 4:   a     1   e     5
# 5:   b     2   e     5
# 6:   c     3   e     5
# 7:   a     1   f     6
# 8:   b     2   f     6
# 9:   c     3   f     6
#10:   a     1   g     7
#11:   b     2   g     7
#12:   c     3   g     7

行の説明res:

  • 基本的には、一方のテーブルにダミー列(この例ではk)を追加してキー(setkey(tablename,keycolumns))に設定し、もう一方のテーブルにダミー列を追加して結合します。
  • data.table 構造は結合で名前ではなく列位置を使用するため、最初にダミー列を配置する必要があります。このc(k=1,.SD)部分は、最初に列を追加する方法の 1 つです (デフォルトでは列を最後に追加します)。
  • 標準の data.table 結合の形式はX[Y]. この場合の X はsetkey(x2[,c(k=1,.SD)],k)、Y はy2[,c(k=1,.SD)]です。
  • allow.cartesian=TRUE重複するキー値を無視し、デカルト結合を実行するように指示data.tableします (以前のバージョンではこれは必要ありませんでした)。
  • 最後の[,k:=NULL]は、結果からダミー キーを削除するだけです。

これを関数に変換することもできるため、よりクリーンに使用できます。

# Version 1; easier to write:
CJ.table.1 <- function(X,Y)
  setkey(X[,c(k=1,.SD)],k)[Y[,c(k=1,.SD)],allow.cartesian=TRUE][,k:=NULL]

CJ.table.1(x2,y2)
#    id1 vals1 id2 vals2
# 1:   a     1   d     4
# 2:   b     2   d     4
# 3:   c     3   d     4
# 4:   a     1   e     5
# 5:   b     2   e     5
# 6:   c     3   e     5
# 7:   a     1   f     6
# 8:   b     2   f     6
# 9:   c     3   f     6
#10:   a     1   g     7
#11:   b     2   g     7
#12:   c     3   g     7

# Version 2; faster but messier:
CJ.table.2 <- function(X,Y) {
  eval(parse(text=paste0("setkey(X[,c(k=1,.SD)],k)[Y[,c(k=1,.SD)],list(",paste0(unique(c(names(X),names(Y))),collapse=","),")][,k:=NULL]")))
}

速度のベンチマークは次のとおりです。

# Create a bigger (but still very small) example:
n<-1e3
x3<-data.table(id1=1L:n,vals1=sample(letters,n,replace=T))
y3<-data.table(id2=1L:n,vals2=sample(LETTERS,n,replace=T))

library(microbenchmark)
microbenchmark(merge=merge.data.frame(x3,y3,all=TRUE),
               CJ.table.1=CJ.table.1(x3,y3),
               CJ.table.2=CJ.table.2(x3,y3),
               times=3, unit="s")
#Unit: seconds
#       expr        min         lq     median         uq        max neval
#      merge 4.03710225 4.23233688 4.42757152 5.57854711 6.72952271     3
# CJ.table.1 0.06227603 0.06264222 0.06300842 0.06701880 0.07102917     3
# CJ.table.2 0.04740142 0.04812997 0.04885853 0.05433146 0.05980440     3

これらの方法は、@danas.zuokas によって提案されdata.tableた方法よりもはるかに高速であることに注意してください。mergeこの例の 1,000 行の 2 つのテーブルは、100 万行のクロス結合テーブルになります。したがって、元のテーブルが小さくても、結果がすぐに大きくなる可能性があるため、速度が重要になります。

最後に、最近のバージョンの では、(CJ.table.1 のように)data.tableを追加するallow.cartesian=TRUEか、返される列の名前を指定する必要があります (CJ.table.2)。2 番目の方法 (CJ.table.2) の方が高速に見えますが、すべての列名を自動的に指定する場合は、より複雑なコードが必要になります。また、列名が重複していると機能しない場合があります。(CJ.table.2 のより単純なバージョンを自由に提案してください)

于 2013-01-04T21:44:09.247 に答える
37

それはただall=TRUEですか?

x<-data.frame(id1=c("a","b","c"),vals1=1:3)
y<-data.frame(id2=c("d","e","f"),vals2=4:6)
merge(x,y,all=TRUE)

のドキュメントからmerge:

by.x と by.y の長さまたは両方が 0 (長さゼロのベクトルまたは NULL) の場合、結果 r は x と y のデカルト積、つまり、dim(r) = c(nrow(x )*nrow(y)、ncol(x) + ncol(y))。

于 2012-05-15T07:30:54.717 に答える
11

data.table 経由で行いたい場合、これが 1 つの方法です。

cjdt <- function(a,b){
  cj = CJ(1:nrow(a),1:nrow(b))
  cbind(a[cj[[1]],],b[cj[[2]],])
}

A = data.table(ida = 1:10)
B = data.table(idb = 1:10)
cjdt(A,B)

上記のように、多くの小さな結合を行っていて、オブジェクトとそれを生成するオーバーヘッドが必要ない場合は、などを使用してコードブロックをdata.table作成することで大幅な速度向上を実現できます。c++Rcpp

// [[Rcpp::export]]
NumericMatrix crossJoin(NumericVector a, NumericVector b){
  int szA = a.size(), 
      szB = b.size();
  int i,j,r;
  NumericMatrix ret(szA*szB,2);
  for(i = 0, r = 0; i < szA; i++){
    for(j = 0; j < szB; j++, r++){
      ret(r,0) = a(i);
      ret(r,1) = b(j);
    }
  }
  return ret;
}

比較するには、まず大規模な結合の場合:

C++

n = 1
a = runif(10000)
b = runif(10000)
system.time({for(i in 1:n){
  crossJoin(a,b)
}})

ユーザーシステム経過時間 1.033 0.424 1.462


データ表

system.time({for(i in 1:n){
  CJ(a,b)
}})

ユーザーシステム経過時間 0.602 0.569 2.452


ここで、多くの小さな結合について:

C++

n = 1e5
a = runif(10)
b = runif(10)
system.time({for(i in 1:n){
  crossJoin(a,b)
}})

ユーザーシステム経過時間 0.660 0.077 0.739


データ表

system.time({for(i in 1:n){
  CJ(a,b)
}})

ユーザーシステム経過 26.164 0.056 26.271

于 2015-08-08T12:23:40.710 に答える
2

でそれを行う組み込みの方法はわかりませんが、data.frame作成するのは難しくありません。

@danas は簡単な組み込みの方法があることを示しましたが、他の目的に役立つ場合に備えて、ここに回答を残します。

cross.join <- function(a, b) {
    idx <- expand.grid(seq(length=nrow(a)), seq(length=nrow(b)))
    cbind(a[idx[,1],], b[idx[,2],])
}

そして、いくつかの組み込みデータセットで動作することを示しています:

> tmp <- cross.join(mtcars, iris)
> dim(mtcars)
[1] 32 11
> dim(iris)
[1] 150   5
> dim(tmp)
[1] 4800   16
> str(tmp)
'data.frame':   4800 obs. of  16 variables:
 $ mpg         : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
 $ cyl         : num  6 6 4 6 8 6 8 4 4 6 ...
 $ disp        : num  160 160 108 258 360 ...
 $ hp          : num  110 110 93 110 175 105 245 62 95 123 ...
 $ drat        : num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
 $ wt          : num  2.62 2.88 2.32 3.21 3.44 ...
 $ qsec        : num  16.5 17 18.6 19.4 17 ...
 $ vs          : num  0 0 1 1 0 1 0 1 1 1 ...
 $ am          : num  1 1 1 0 0 0 0 0 0 0 ...
 $ gear        : num  4 4 4 3 3 3 3 4 4 4 ...
 $ carb        : num  4 4 1 1 2 1 4 2 2 4 ...
 $ Sepal.Length: num  5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 ...
 $ Sepal.Width : num  3.5 3.5 3.5 3.5 3.5 3.5 3.5 3.5 3.5 3.5 ...
 $ Petal.Length: num  1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
于 2012-05-15T03:42:30.367 に答える
2

data.table用_

dt1[, as.list(dt2), by = names(dt1)]

これは、重複する行がない場合にのみ機能することに注意してください。

于 2020-11-18T13:14:44.787 に答える