2014-04-10 12 views
6

假設我有矢量vec <- c("D","B","B","C","C")用於查找與矢量中唯一值關聯的索引的高效R代碼

我的目標是與尺寸length(unique(vec)),其中該列表中的每個i返回一個表示在vecunique(vec)[i]位置索引的向量的列表結束。

例如,這個列表vec將返回:

exampleList <- list() 
exampleList[[1]] <- c(1) #Since "D" is the first element 
exampleList[[2]] <- c(2,3) #Since "B" is the 2nd/3rd element. 
exampleList[[3]] <- c(4,5) #Since "C" is the 4th/5th element. 

我嘗試以下方法,但它的速度太慢。我的例子是大的,所以我需要更快的代碼:

vec <- c("D","B","B","C","C") 
uniques <- unique(vec) 
exampleList <- lapply(1:3,function(i) { 
    which(vec==uniques[i]) 
}) 
exampleList 

回答

6

更新:行爲DT[, list(list(.)), by=.]有時會導致作爲R版本錯誤的結果> = 3.1.0 。現在在data.table v1.9.3的當前開發版本中現在修復了commit #1280。從NEWS

  • DT[, list(list(.)), by=.]回報中的R正確結果> = 3.1.0爲好。該錯誤是由於R v3.1.0中最近(歡迎)的更改導致的,其中list(.)未導致副本。關閉#481

使用data.table大約是15倍比tapply快:

library(data.table) 

vec <- c("D","B","B","C","C") 

dt = as.data.table(vec)[, list(list(.I)), by = vec] 
dt 
# vec V1 
#1: D 1 
#2: B 2,3 
#3: C 4,5 

# to get it in the desired format 
# (perhaps in the future data.table's setnames will work for lists instead) 
setattr(dt$V1, 'names', dt$vec) 
dt$V1 
#$D 
#[1] 1 
# 
#$B 
#[1] 2 3 
# 
#$C 
#[1] 4 5 

速度測試:

vec = sample(letters, 1e7, T) 

system.time(tapply(seq_along(vec), vec, identity)[unique(vec)]) 
# user system elapsed 
# 7.92 0.35 8.50 

system.time({dt = as.data.table(vec)[, list(list(.I)), by = vec]; setattr(dt$V1, 'names', dt$vec); dt$V1}) 
# user system elapsed 
# 0.39 0.09 0.49 
+0

@阿倫好點,謝謝!有些令我驚訝的是它並沒有影響計時 - 我猜這只是太便宜而無法複製 – eddi

+0

有趣...我用你的data.table代碼得到了不同的結果:在我的電腦中dt變成> dt vec V1 1:D 4,5 2:B 4,5 3:C 4,5( - 也就是說,V1的所有元素均相等於4:5)。 – lebatsnok

+0

@lebatsnok你正在運行什麼版本的'data.table'? (我使用1.9.3) – eddi

4

你可以用tapply做到這一點:

vec <- c("D", "B", "B", "C", "C") 
tapply(seq_along(vec), vec, identity)[unique(vec)] 
# $D 
# [1] 1 
# 
# $B 
# [1] 2 3 
# 
# $C 
# [1] 4 5 

identity函數返回它的參數作爲它的結果,和索引的unique(vec)確保你找回來按照原始矢量中元素的相同順序排列。

+0

這是一種荒謬的這種功能有多快。 – user2763361

+0

我如何獲得它,因此它專注於元素的排序而不是按字母順序排列?我的用例需要排序元素,而不是按字母排序(例如'vec < - c(「C」,「B」)'應該返回'$ C [1] 1 $ B [1] 2',而不是反過來)。 – user2763361

+0

我在原始文章中添加了一個我正在尋找的示例。 – user2763361

1

爲了保持josilber的答案的順序,只要指數由uniques矢量結果創建:

vec <- c("D","B","B","C","C") 

uniques <- unique(vec) 

tapply(seq_along(vec), vec, identity)[uniques] 

# $D 
# [1] 1 
# 
# $B 
# [1] 2 3 
# 
# $C 
# [1] 4 5 
4
split(seq_along(vec), vec) 

這是更快,比tapply短解決方案:

vec = sample(letters, 1e7, T) 
system.time(res1 <- tapply(seq_along(vec), vec, identity)[unique(vec)]) 
# user system elapsed 
# 1.808 0.364 2.176 
system.time(res2 <- split(seq_along(vec), vec)) 
# user system elapsed 
# 0.876 0.152 1.029 
+0

bah ..我沒有注意到這已經在上面的評論中被提出了'類似的方法可以是:split(seq_along(vec),vec) - alexis_laz 3小時前' – lebatsnok

+1

唯一可以使用數字而不是字母的方法。 – marsei