R cumsum 与 if 条件

问题描述

假设我有这个数据框

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

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

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

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

mutate(
    segment = if_else(type == "NEW",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","NEW","OLD"),segment_expected = c(0,2,3))
,

使用 rle 的基础 R -

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