※ 引述《clansoda (小笨)》之銘言:
: 我自己來回我自己的問題 我採用的解法是andrew大的解法
: C版的解法看起來應該是最快的 但是小弟無法理解在幹嘛
: 所以選擇了一個看起來比我的快很多又能理解的方法
: kk <- Sys.time()
: klist <- lapply(1 : NROW(target), function(k){
: target[k,] %>% as.numeric %>% .[!is.na(.)]
: })
: test <- lapply(klist, function(k) {
: m <- logical(20)
: m[k] <- TRUE
: return(m)
: }) %>% do.call(rbind, .)
: Sys.time() - kk
: Time difference of 53.88025 secs
: 我稍微修正過andrew大在提取每個row裡的值成為list的這一段碼
: 這樣可以將速度從180幾秒提到50秒左右
: 以我目前這個50萬row的資料等級來說這個速度我個人可以接受了
: 等到C大提點他的程式碼的邏輯以後可能會再修改我的寫法
: 目前先到這樣 感謝各位先進的提供的作法 受益良多
結果應該是一樣的,程式:
library(data.table)
target <- fread('
a b c
2 5 NA
1 NA NA
1 2 3
3 NA NA
2 4 NA
1 4 5
')
mat <- as.matrix(target)
library(magrittr)
system.time({
klist <- lapply(1 : NROW(target), function(k){
target[k,] %>% as.numeric %>% .[!is.na(.)]
})
test <- lapply(klist, function(k) {
m <- logical(5)
m[k] <- TRUE
return(m)
}) %>% do.call(rbind, .)
})
system.time({
idx <- nrow(mat) * (mat - 1L)
idx <- idx[which(!is.na(idx))] + which(!is.na(mat), arr.ind = TRUE)[, 1]
out <- matrix(FALSE, nrow(mat), 5L)
out[idx] <- TRUE
dim(out) <- c(nrow(mat), 5L)
})
all.equal(test, out) # TRUE
我程式有點偷懶,是因為假設level數跟input的coloumn數會一樣
我這裡解釋一下我的程式邏輯:
我們先看一下輸出的結果
[,1] [,2] [,3] [,4] [,5]
[1,] FALSE TRUE FALSE FALSE TRUE
[2,] TRUE FALSE FALSE FALSE FALSE
[3,] TRUE TRUE TRUE FALSE FALSE
[4,] FALSE FALSE TRUE FALSE FALSE
[5,] FALSE TRUE FALSE TRUE FALSE
[6,] TRUE FALSE FALSE TRUE TRUE
第一列是2, 5要為TRUE,對應到input的第一列 2, 5, NA
第二列是1是TRUE,對應到input的第一列 1, NA, NA
所以我只要有(1, 2), (1, 5), (2, 1), ... 的位置向量
就可以把TRUE位置都描述出來
而且(1, 2), (1, 5), ...這些位置也可以用一個index表示
(這裡計算是根據coloumn-major的矩陣,row-major則會有一點不同)
矩陣中 (1, 2)位置其實可以用 1 + nrow(matrix) * (2 - 1) = 7 (這列有6個row)
(1, 5)位置可以用 1 + nrow(matrix) * (5 - 1) = 25
(2, 1)位置可以用 2 + nrow(matrix) * (1 - 1) = 2 ...
來表示
所以我們可以得到一個通式:
(i, j) => i + nrow(matrix) * (j - 1)
換到我的程式上來看
這行 idx <- nrow(mat) * (mat - 1L) 是把後面那個部分算出來
可是因為mat裡面充滿了NA,所以要滿NA先移除掉就有了下一行的前半段:
idx[which(!is.na(idx))]
那i要怎麼辦,就利用which + !is.na去把對應的列位置取出
於是我們就得到了TRUE位置的index:
idx <- idx[which(!is.na(idx))] + which(!is.na(mat), arr.ind = TRUE)[, 1]
那最後我只要把output的矩陣弄出來:
out <- matrix(FALSE, nrow(mat), 5L)
# 這裡的5是指target中最大的數字,可以用max(mat[!is.na(mat)])取得
然後再把TRUE位置補上,改一下dim:
out[idx] <- TRUE
dim(out) <- c(nrow(mat), 5L) # 這個5同前面的5意思
如此一來就可以得到正確答案了
這個方法比較tricky一點,但是向量化的精神就在這裡
向量化的程式需要一點的數學 跟 邏輯推演,不是那麼直覺就寫得出來
但是它的performance會真的很好~~~~