2016-04-26 16 views
2

私はRに大きな行列(行列Matrixとして)を持つ。それは疎である(01のみを含む)。 (Mが行列の場合)私は何大きい行列のインデックスを高速化する

がある

j<-list() 
for(i in 1:dim(M)[1]){ 
    which(M[i,]==1)->j[[i]] 
} 

これは、通常は高速ですが、このような大規模なマトリックス(5000に1.7ミル薄暗い)に、それは非常に遅いです。私はちょうどカントは

+0

あなたができた「マトリックス」を有します'split($ j、$ i)'またはおそらくより効率的な 'split(rep(seq_len(ncol(M))、diff(Mp) i + 1L) ' –

答えて

1

編集コメントの後に....すべての行に1をされているこれらのCOLSのインデックスを取得するために何より高速な方法がないことを信じている:

apply(M, 1, function(i) which(i == 1)) 

# [[1]] 
# [1] 1 2 3 
# 
# [[2]] 
# [1] 1 3 
# 
# [[3]] 
# [1] 2 3 
# 
# [[4]] 
# [1] 2 3 

この例を試してみてください。

#data 
M <- matrix(c(1,1,0,0,1,0,1,1,1,1,1,1), 4) 
#  [,1] [,2] [,3] 
# [1,] 1 1 1 
# [2,] 1 0 1 
# [3,] 0 1 1 
# [4,] 0 1 1 

# index of rows with all ones 
which(rowSums(M == 1) == ncol(M)) 
# [1] 1 

# index of cols with all ones 
which(colSums(M == 1) == nrow(M)) 
# [1] 3 
+0

こんにちは - 申し訳ありませんが、行を見つけることではなく、特定の行の1を見つけることです。すべての値が1である行はありません。 1 – kutyw

+0

それから '(M == 1)'を試してみてください。あなたの例では – zx8754

+0

、2行目は結果1と3、行3は2と3、行4は2と3が必要です。 – kutyw

2

@のzx8754で例を使用して

M <- matrix(c(1,1,0,0,1,0,1,1,1,1,1,1), 4) 
oneMat <- which(M==1, arr.ind=TRUE) 

を我々は各行の1に等しい列番号を含むリストを作成することができ、この補助マトリックスから:我々は、1に等しいエントリの行と列のインデックスが含まれている補助行列を定義することができ行列Mが大きく、スパースである場合

oneList <- lapply(1:nrow(M), function(x) oneMat[oneMat[,1] == x, 2]) 
#[[1]] 
#[1] 1 2 3 
# 
#[[2]] 
#[1] 1 3 
# 
#[[3]] 
#[1] 2 3 
# 
#[[4]] 
#[1] 2 3 

で、行列oneMatMよりもはるかに小さくする必要があります。その場合、私は、第2ステップで使用されたlapply()ループが、OPに記載されているforループに関してスピードアップにつながると思います。


いくつかのテストの後、私は残念ながらこの回答が特に遅いことを認めなければなりません。 @ColonelBeauvelによって溶液が勝者です:

j <- list() 
set.seed(123) 
M <- matrix(rbinom(1e5,1,0.01),ncol=100) 
library(microbenchmark) 
f_which_and_lappy <- function(x) {oneMat <- which(x==1, arr.ind=TRUE); 
      lapply(1:nrow(x), function(i) oneMat[oneMat[,1] == i, 2])} 
f_only_apply <- function(x) {apply(x, 1, function(i) which(i == 1))} 
f_with_data.frame <- function(x) {with(data.frame(which(!!x, arr.ind=T)), split(col, row))} 
f_OP <- function(x) {for(i in 1:dim(x)[1]){which(x[i,]==1)->j[[i]]}} 
res <- microbenchmark(
    f_which_and_lappy(M), 
    f_only_apply(M), 
    f_with_data.frame(M), 
    f_OP(M),times=1000L) 
#> res 
#Unit: microseconds 
#     expr  min  lq  mean  median  uq  max neval cld 
# f_which_and_lappy(M) 11063.170 11254.032 12090.9506 11351.1830 11570.662 31313.48 1000 d 
#  f_only_apply(M) 3204.572 3359.410 4117.4971 3456.3960 3610.945 25352.35 1000 b 
# f_with_data.frame(M) 739.556 811.906 912.4726 918.0315 946.700 18623.77 1000 a 
#    f_OP(M) 5642.639 5854.751 6955.9980 5969.3685 6151.209 148847.22 1000 c 
+0

ベンチマークが可能ですかなりの時間 - それが現在のところでインデックスを印刷する方法はありますか? – kutyw

+0

現在の行を表示するには、oneMat [oneMat [、1]、[ == x、2]}) ' – RHertel

+0

こんにちは、私が上で提案した解決策よりも遅いです。なぜ私は本当に知りませんが、20秒ごとに1行かかります...少し速くなる機会はありませんか? – kutyw

2

私はむしろベクトル化のアプローチを選ぶと、これらの適用/ lapply家族の機能の代わりにsplitを使用します。

M = matrix(c(1,1,0,0,1,0,1,1,1,1,1,1), 4) 

with(data.frame(which(!!M, arr.ind=T)), split(col, row)) 
#$`1` 
#[1] 1 2 3 

#$`2` 
#[1] 1 3 

#$`3` 
#[1] 2 3 

#$`4` 
#[1] 2 3 
+0

エラーを回避する可能性があります(!(!M)、arr.ind = T) : 関数 'which'のメソッドを選択する際に引数 'x'を評価する際にエラーが発生しました:asMethod(オブジェクト)のエラー: Cholmodエラー 'ファイルが大きすぎます' ../Core/cholmod_dense.c、行105 エラーここに?まだそれを速くする?それは普通の行列ではありませんが、単純に 'which(!! M、arr.ind = T)'であるのに対し、 '(!(!M)、arr.ind = T)'を投稿した行列 – kutyw

+0

は –

+0

です(!(!M)、arr.ind = T)のエラー(data.frame(which!(M、arr.ind = T))、split(col、row)) - > j : 関数 'which'のメソッドを選択する際に引数 'x'を評価する際にエラーが発生しました:asMethod(オブジェクト)のエラー: ファイルの../Core/cholmod_dense.c、行105のCholmodエラー 'too too large'エラーの場合: – kutyw

関連する問題