2011-02-24 124 views
2

我正在創建一些仿真數據。我需要創建家庭ID(H_ID)和個人ID(P_ID,在每個家庭中)。向量化循環

我發現了一種如何以矢量化的方式創建H_ID的方法。

N <- 50 

### Household ID 
# loop-for 
set.seed(20110224) 
H_ID <- vector("integer", N) 
H_ID[1] <- 1 
for (i in 2:N) if (runif(1) < .5) H_ID[i] <- H_ID[i-1]+1 else H_ID[i] <- H_ID[i-1] 
print(H_ID) 

# vectorised form 
set.seed(20110224) 
r <- c(0, runif(N-1)) 
H_ID <- cumsum(r < .5) 
print(H_ID) 

但我無法弄清楚如何以矢量化的方式創建P_ID。

### Person ID 
# loop-for 
P_ID <- vector("integer", N) 
P_ID[1] <- 1 
for (i in 2:N) if (H_ID[i] > H_ID[i-1]) P_ID[i] <- 1 else P_ID[i] <- P_ID[i-1]+1 
print(cbind(H_ID, P_ID)) 

# vectorised form 
# ??? 

回答

1

通過Martin Morgan's solution啓發密切相關的問題,下面是一個使用cummax函數生成P_ID一個真正的量化方式。一旦你注意到P_ID密切相關,它變得清晰了cumsum!(r < 0.5)的:

set.seed(1) 
N <- 10 
r <- c(0, runif(N-1)) 
H_ID <- cumsum(r < .5) 
r_ <- r >= .5 # flip the coins that generated H_ID. 
z <- cumsum(r_) # this is almost P_ID; just need to subtract the right amount... 
# ... and the right amount to subtract is obtained via cummax 
P_ID <- 1 + z - cummax(z * (!r_)) 
> cbind(H_ID, P_ID) 
     H_ID P_ID 
[1,] 1 1 
[2,] 1 2 
[3,] 2 1 
[4,] 3 1 
[5,] 3 2 
[6,] 3 3 
[7,] 3 4 
[8,] 4 1 
[9,] 5 1 
[10,] 5 2 

我沒有做過詳細的計時測試,但它可能是邪惡的快,因爲這些都是內部的,量化的功能

+0

我做了時間測試('N < - 2e6')。你的解決方案肯定是最快的。它比'lapply'解決方案快了約34倍。謝謝! – djhurio 2011-02-28 19:26:26

2
P_ID <- unname(unlist(tapply(H_ID, H_ID, function(x)c(1:length(x))))) 
4

又如:

P_ID <- ave(rep(1, N), H_ID, FUN=cumsum) 

我前幾天(在這裏)發現了關於ave功能,並發現它在很多情況下,一個非常有用和有效的捷徑。

0

seq_along()這裏是一個有用的工具。本示例通過本身分裂H_ID到含有戶的列表:

> head(split(H_ID, H_ID)) 
$`1` 
[1] 1 1 

$`2` 
[1] 2 

$`3` 
[1] 3 3 3 3 
.... 

的溶液到Q然後是lapply()seq_along()函數應用於每個列表元素; seq_along()創建了一個向量1:length(foo)。最後兩個看家步驟,選擇不公開的結果,然後取出names

> unname(unlist(lapply(split(H_ID, H_ID), seq_along))) 
[1] 1 2 1 1 2 3 4 1 1 2 3 1 1 1 1 1 2 3 4 5 1 2 3 4 1 1 2 1 2 1 
[31] 1 2 1 2 3 4 1 2 1 2 1 2 1 1 2 1 2 1 2 3 
0

這是一個合理的緊湊和富有表現力的解決方案。有點類似於辛普森在中間值方面:

cbind(H_ID, unlist(sapply(table(H_ID), seq))) 

核心,它的策略是使用表() - ED值作爲參數SEQ(),它在默認情況下將採取單一數值和返回從1的序列。