我正在制作一些人工数据。我需要创建家庭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
# ???
答案 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_ID
与cumsum
的{{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返回序列。