在对重复标识符进行操作时避免循环

Avoiding looping when operations on repeated identifier

我经常很难在 R 中进行基本操作,因为我必须控制唯一标识符。

我大部分时间都在处理 "long format" 数据。

dt <- data.frame(id = c(rep("A1", 3), rep("B1", 3)),
             activity = c(15,17,12,3,4,15),
             begin = c( 0, 0, 1, 0, 1, 2 ) )

例如,通过标识符计算时间或观察

dt$time <- 1
for(i in 2:nrow(dt)){
  if(dt[i,'id'] == dt[i-1, 'id'])
  {
    dt[i,'time'] <- dt[i-1,'time'] + 1
  }
}

或仔细检查重复数据

dt$zerocheck = 0 
for(i in 2:nrow(dt)){
  if( dt[i,'id'] == dt[i-1, 'id'] & 
        dt[i,'begin'] == dt[i-1, 'begin'] )  
  {
   dt$zerocheck[i] <- 1
  }
}

我想答案应该是按 id 聚合之类的东西,但我不完全确定。

merge(dt, aggregate(time ~ id, dt, "max"), by=c("id"), all.X=T)

有什么避免循环的建议吗?

这些操作可以用data.table来简化:

require(data.table)
setDT(dt)[, `:=`(time = seq_len(.N), zerocheck = begin == shift(begin)), by = id]

要添加到其他示例,您还可以使用 dplyr

library(dplyr)
dt %>% group_by(id) %>% 
  mutate(time = row_number()) %>% # creates the control for identifier
  mutate(zerocheck= ifelse(begin==lag(begin), 1, 0)) # checks for repeated data

或者等效地,您可以只使用如下所示的单个 mutate 函数:

dt %>% 
  group_by(id) %>% 
  mutate(time = row_number(), 
         zerocheck=begin==lag(begin))

第一个查询有输出:

Source: local data frame [6 x 5]
Groups: id

  id activity begin time zerocheck
1 A1       15     0    1        NA
2 A1       17     0    2         1
3 A1       12     1    3         0
4 B1        3     0    1        NA
5 B1        4     1    2         0
6 B1       15     2    3         0

对于 zerocheck 的情况,我只是使用滞后来检查之前的值是否与当前值相同。这模仿了您问题中的代码。当然如果你想检查别的东西,你可以很容易地改变谓词。