矢量化循环

时间:2011-02-24 22:39:00

标签: r loops for-loop

我正在制作一些人工数据。我需要创建家庭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
# ???

5 个答案:

答案 0 :(得分:4)

另一个例子:

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

我几天前(这里)发现了ave函数,在很多情况下发现它是一个非常有用和有效的快捷方式。

答案 1 :(得分:2)

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

答案 2 :(得分:1)

受到Martin Morgan's solution启发的密切相关的问题,这是使用P_ID函数生成cummax的真正矢量化方式。一旦您注意到P_IDcumsum的{​​{1}}密切相关,就会很清楚:

!(r < 0.5)

我还没有做过详细的计时测试,但它可能很快就会变坏,因为这些都是内部的矢量化函数

答案 3 :(得分: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

答案 4 :(得分:0)

这是一个相当紧凑和富有表现力的解决方案。与Simpson的中间值有些相似:

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

其策略的核心是使用table() - ed值作为seq()的参数,默认情况下,它将采用单个数值并从1返回序列。