2013-08-17 131 views
3

假設我有一個20×5的矩陣,我想選擇矩陣的子集並且用它們做一些計算。進一步假設每個子矩陣是7×5。我當然可以做選擇組合的子集

ncomb <- combn(20, 7) 

這給我的7個索引所有可能的組合,我可以用這些來獲取子矩陣。但是用一個小的20×5矩陣,已經有77520個可能的組合。所以我想隨機抽樣一些組合,例如5000個組合。

一種可能性是以下內容:

ncomb <- combn(20, 7) 
ncombsub <- ncomb[, sample(77520, 5000)] 

換句話說,我得到所有可能的組合,然後隨機選擇只5000組合。但我想如果我有一個更大的矩陣計算所有可能的組合,將會有問題 - 比方說100 X 7.

所以我想知道是否有辦法在沒有首先獲得所有可能的組合的情況下獲得組合的子集。

+1

是的,我認爲這是可能通過修改'combn '或者編寫自己的函數(這可能會更簡單)。用這個算法來實現它並不難。 – Roland

+1

你可能想看到相關的帖子[這裏](http://stackoverflow.com/questions/4493287/generating-a-very-large-matrix-of-string-combinations-using-combn-and-bigmemor) – Metrics

+0

@按照你的建議,羅蘭我最終修改了'combn()'。效果很好。 – Alex

回答

3

最後我做什麼@Roland建議,通過修改combn()和字節編譯代碼:

combn_sub <- function (x, m, nset = 5000, seed=123, simplify = TRUE, ...) { 
    stopifnot(length(m) == 1L) 
    if (m < 0) 
     stop("m < 0", domain = NA) 
    if (is.numeric(x) && length(x) == 1L && x > 0 && trunc(x) == 
     x) 
     x <- seq_len(x) 
    n <- length(x) 
    if (n < m) 
     stop("n < m", domain = NA) 
    m <- as.integer(m) 
    e <- 0 
    h <- m 
    a <- seq_len(m) 
    len.r <- length(r <- x[a]) 
    count <- as.integer(round(choose(n, m))) 
    if(count < nset) nset <- count 
    dim.use <- c(m, nset)  

    ##-----MOD 1: Change the output matrix size-------------- 
    out <- matrix(r, nrow = len.r, ncol = nset) 

    if (m > 0) { 
     i <- 2L 
     nmmp1 <- n - m + 1L 

     ##----MOD 2: Select a subset of indices 
     set.seed(seed) 
     samp <- sort(c(1, sample(2:count, nset - 1))) 

     ##----MOD 3: Start a counter. 
     counter <- 2L  

     while (a[1L] != nmmp1) { 
      if (e < n - h) { 
       h <- 1L 
       e <- a[m] 
       j <- 1L 
      } 
      else { 
       e <- a[m - h] 
       h <- h + 1L 
       j <- 1L:h 
      } 
      a[m - h + j] <- e + j 

      #-----MOD 4: Whenever the counter matches an index in samp, 
      #a combination of row indices is produced and stored in the matrix `out` 
      if(samp[i] == counter){ 
       out[, i] <- x[a] 
       if(i == nset) break 
       i <- i + 1L 
      } 
      #-----Increase the counter by 1 for each iteration of the while-loop 
      counter <- counter + 1L 
     } 
    } 
    array(out, dim.use) 
} 

library("compiler") 
comb_sub <- cmpfun(comb_sub) 
3

你的方法:

op <- function(){ 
    ncomb <- combn(20, 7) 
    ncombsub <- ncomb[, sample(choose(20,7), 5000)] 
    return(ncombsub) 
} 

了不同的策略,簡單的樣品從原來的矩陣七行5000次(直到5000個唯一的行組合找到了一個新的樣本來替換任何重複的樣品):

me <- function(){ 
    rowsample <- replicate(5000,sort(sample(1:20,7,FALSE)),simplify=FALSE) 
    while(length(unique(rowsample))<5000){ 
    rowsample <- unique(rowsample) 
    rowsample <- c(rowsample, 
        replicate(5000-length(rowsample), 
           sort(sample(1:20,7,FALSE)),simplify=FALSE)) 
    } 
    return(do.call(cbind,rowsample)) 
} 

這應該是更有效率,因爲它可以防止您必須首先計算所有組合,隨着矩陣變大,這會變得昂貴。

然而,一些基準測試顯示情況並非如此。至少在這個矩陣:

library(microbenchmark) 
microbenchmark(op(),me()) 

Unit: milliseconds 
expr  min  lq median  uq  max neval 
op() 184.5998 201.9861 206.3408 241.430 299.9245 100 
me() 411.7213 422.9740 429.4767 474.047 490.3177 100 
+0

幾個問題。爲了您的代碼能夠工作,我認爲您還需要在while循環之前對每列進行排序,即對每個索引樣本進行排序。否則,'unique()'不起作用。我想第二個問題是'unique()'的參數'MARGIN'需要設置爲'2'(默認爲'1')。而不是'length(unique(rowsample))',它需要是'ncol(unique(rowsample))'。由於'length'給出了矩陣中包含的元素總數,而不是列數(在我的情況下,每列都是一個樣本,所以5000列是5000個樣本的索引)。 – Alex

+0

@Alex做了一些改變(想到'replicate'返回一個列表,而不是矩陣)。結果並不像原來的解決方案那麼高效。而且,如果你允許'replicate'簡化爲一個矩陣,它會更慢。 – Thomas

+0

我最終修改了原來的'combn()'函數,並進行了字節編譯。它工作正常。但是,無論如何,感謝這個解決方案,我認爲你的策略對於我正在處理的其他一些事情可能是有用的。 – Alex