R cumsum 与 if 条件

R cumsum with if condition

假设我有这个数据框

df <-
  data.frame(
    id = seq(1, 8),
    type = c("NEW", "OLD", "OLD", "NEW", "OLD", "NEW", "NEW", "OLD")
  ) 

我想为每个 TYPE OLD 组创建“段”,因此结果是这样的 - 每个段都用订单号标记,请注意前两个旧类型有 1 作为段,第二段是标记为 2.

df <-
  data.frame(
    id = seq(1, 8),
    type = c("NEW", "OLD", "OLD", "NEW", "OLD", "OLD", "NEW", "OLD"),
    segment = c(0, 1, 1, 0, 2, 2, 0, 3)
  )

但我在 R 中实现这个时遇到了问题。我可以为类型段创建 if else,我想我需要通过 cumsum 函数来完成,但我还没有找到方法。

mutate(
    segment = if_else(type == "NEW", 0, 1)
    )

这是一种使用行号差异方法的方法。这种方法通常用于数据库中的间隙和孤岛问题,这基本上也是这个 R 问题。

df$segment <- ifelse(df$type == "OLD", df$id - cumsum(df$type == "OLD"), 0)
df

  id type y segment
1  1  NEW 1       0
2  2  OLD 1       1
3  3  OLD 1       1
4  4  NEW 2       0
5  5  OLD 2       2
6  6  OLD 2       2
7  7  NEW 3       0
8  8  OLD 3       3

这里的方法是区分整个数据帧上的序列和仅 OLD 条目上的序列。考虑以下差异:

1 2 3 4 5 6 7 8
0 1 2 2 3 4 4 5
---------------
1 1 1 2 2 2 3 3

那么,我们只保留上述差分属于OLD的值。

cumsum的另一种方法:

df %>% mutate(
    segment = cumsum(lag(type, 1, 'NEW') == 'NEW' & type == 'OLD') * (type == 'OLD'))

#   id type segment_expected segment
# 1  1  NEW                0       0
# 2  2  OLD                1       1
# 3  3  OLD                1       1
# 4  4  NEW                0       0
# 5  5  OLD                2       2
# 6  6  OLD                2       2
# 7  7  NEW                0       0
# 8  8  OLD                3       3

这里使用的df是:

df <-data.frame(
        id = seq(1, 8),
        type = c("NEW", "OLD", "OLD", "NEW", "OLD", "OLD", "NEW", "OLD"),
        segment_expected = c(0, 1, 1, 0, 2, 2, 0, 3))

基础 R 使用 rle -

transform(df, segment = with(rle(type == 'NEW'), 
              rep(cumsum(values) * as.integer(!values), lengths)))

#  id type segment
#1  1  NEW       0
#2  2  OLD       1
#3  3  OLD       1
#4  4  NEW       0
#5  5  OLD       2
#6  6  NEW       0
#7  7  NEW       0
#8  8  OLD       3