在数据框中按组应用for循环

时间:2018-01-18 14:29:29

标签: r

我有一个名为(df)的数据框,其结构如下:

day colA colB
1   4    8
2   9    2
3   8    1
4   9    4 
1   5    8
2   2    4

问题:

我想创建一个名为'colC'的新列,其逻辑如下:

if day is equal to 1, then colC = colA+colB
if day not equal to 1, then colC = (colA+colB) - previous ColC 

预期解决方案:

day colA colB colC
1   4    8    12
2   9    2    -1
3   8    1    10
4   9    4    3 
1   5    8    13
2   2    4    -7

当前解决方案:

for(i in 1:NROW(df)
{
if(day[i] == 1) {colC[i] <- colA[i] + colB[i]} 
else {colC[i] <- colA[i] + colB[i] - colC[i-1]}  
}

问题:

  • 数据集的大小太大,因此循环非常大 慢
  • 在我的数据集中,日期列为第7天的值
  • 基本上我正在寻找一个解决方案,可以分割数据集,并且可以为每个子集独立应用for循环(假设每个连续的第1-4天是一组)

3 个答案:

答案 0 :(得分:0)

我有一个使用移位的解决方案,并在两行之间的最大距离上循环== 1

df$colC = df$colA + df$colB

for (i in 1:max(diff(which(df$day == 1))))
{
df$colCshift = c(NA,df$colC[1:(length(df$colC)-1)]) # creating a shifted version of colC
df$colC[df$day != 1] = df$colA[df$day != 1] + df$colB[df$day != 1] - df$colCshift[df$day != 1]
# here colC[i-1] is the shifted version of ColC.
}

  day colA colB colC colCshift
1   1    4    8   12        NA
2   2    9    2   -1        12
3   3    8    1   10        -1
4   4    9    4    3        10
5   1    5    8   13         3
6   2    2    4   -7        13

第一次迭代正确计算数据框中各处的所有行。然后我更新了colC的移位版本,并在day = 1之后计算所有行两行等等。

此处max(diff(which(df$day == 1)))应该是您所说的

答案 1 :(得分:0)

这应该很快。您需要包dplyr

df$colC <- df$colA+df$colB

df$colD <- dplyr::lag(df$colC,1)

df$colC <- ifelse(df$day != 1, df$colC-df$colD, df$colC)

> df[, 1:4]
  day colA colB colC
1   1    4    8   12
2   2    9    2   -1
3   3    8    1   -2
4   4    9    4    4
5   1    5    8   13
6   2    2    4   -7

顺便说一下,你的预期输出似乎是假的。

答案 2 :(得分:0)

您的数据:

df <- structure(list(day = c(1L, 2L, 3L, 4L, 1L, 2L), colA = c(4L, 
9L, 8L, 9L, 5L, 2L), colB = c(8L, 2L, 1L, 4L, 8L, 4L)), .Names = c("day", 
"colA", "colB"), class = "data.frame", row.names = c(NA, -6L))

起始案例:

df$colC <- df$colA + df$colB

我建议不要使用for进行循环,而是使用cumsum(df$day == 1)声明每个天数。从那里,我们可以使用Reduce的技巧来进行滚动应用:

df <- do.call(rbind, by(df, cumsum(df$day == 1), function(d) {
  d$colC <- Reduce(function(a,b) b-a, d$colC[-1], d$colC[1], accumulate=T)
  d
}))

我认为您在第5行的预期输出不正确,因为它5+8只是day==1

df
#     day colA colB colC
# 1.1   1    4    8   12
# 1.2   2    9    2   -1
# 1.3   3    8    1   10
# 1.4   4    9    4    3
# 2.5   1    5    8   13
# 2.6   2    2    4   -7

编辑:我认为您应该将代码更新为以群组为中心而不是以行为中心,但由于您认为需要使用for,请从原始{开始{1}}:

df

或许(稍微更快):

df$colC <- df$colA + df$colB
for (i in seq_len(nrow(df))) {
  df$colC[i] <- df$colC[i] - ifelse(i < 2 | df$day[i] == 1, 0, df$colC[i-1])
}