R:按组删除特定字符出现后的后续行

问题描述 投票:0回答:2

我有一个类似于以下数据集的大型数据集。

身份证 日期 改变 工资
201 2023年1月4日 B-AL 900
201 2023年3月1日 AL-AL 900
201 2023年10月1日 AL-AL 900
201 2023年11月10日 AL-B 1900
201 2023年11月28日 B-AL 900
201 2023年12月10日 AL-B 1905
301 2023年1月3日 B-AL 1100
301 2023年2月11日 AL-B 2500
301 2023年4月15日 B-AL 1110
301 2023年9月27日 AL-B 2506
401 2023年1月2日 B-AL 700
401 2023年1月28日 AL-X 0
401 2023年2月10日 AL-AL 700
401 2023年4月2日 AL-AL 700
401 2023年5月10日 AL-B 2200
401 2023年8月13日 B-AL 800

我想计算每个状态的平均工资,但有两个限制: 首先,我只想考虑每个人的状态,直到第二次达到“B-AL”状态。每个 ID 第二次出现“B-AL”之后的所有行不应在计算中考虑。

(我认为)我通过创建两个帮助变量解决了这个问题,然后用 cumsum() 进行过滤

data <- data %>%
  arrange(date) %>%
  mutate(num = row_number(),
         num2 = ifelse(change == "B-AL" & num == 2, 1, 0),
         .by = c(ID, change))

data <- data %>%
  group_by(ID) %>%
  filter(cumsum(num2) == 0) %>% 
  ungroup()

现在第二个限制:一旦一个 ID 的状态为“AL-X”,所有后续行就不应该被考虑。

如果我使用与上面相同的方法,我也会失去第一次出现“AL-X”的观察结果。但是,我希望包含此一项,并排除所有后续项。两次操作后,表格应如下所示

身份证 日期 改变 工资
201 2023年1月4日 B-AL 900
201 2023年3月1日 AL-AL 900
201 2023年10月1日 AL-AL 900
201 2023年11月10日 AL-B 1900
301 2023年1月3日 B-AL 1100
301 2023年2月11日 AL-B 2500
401 2023年1月2日 B-AL 700
401 2023年1月28日 AL-X 0

之后我可以通过summary计算平均工资(先按状态,然后按ID)

如有任何帮助,我们将不胜感激。我也不确定第一次手术是否合理有效。

r dplyr grouping cumsum
2个回答
0
投票

根据您想要的输出来判断

  • 第二次出现“B-AL”时删除所有内容,包括该行
  • 第一次出现“AL-X”后所有内容都被删除
library(dplyr)

df %>% 
  filter(cumsum(Change == c("B-AL")) < 2, 
         lag(cumsum(Change == c("AL-X")), default=0) < 1, .by = ID)
   ID       Date Change wage
1 201 04.01.2023   B-AL  900
2 201 01.03.2023  AL-AL  900
3 201 01.10.2023  AL-AL  900
4 201 10.11.2023   AL-B 1900
5 301 03.01.2023   B-AL 1100
6 301 11.02.2023   AL-B 2500
7 401 02.01.2023   B-AL  700
8 401 28.01.2023   AL-X    0

0
投票
library(dplyr)

foo <- function(x, vc1, vc2, vc1_limit = 2L, vc2_limit = 1) {
  vc1_count <- 0L; vc2_count <- 0L
  helper <- function(v) {
    if (v == vc1) vc1_count <<- vc1_count + 1L else if (v == vc2) vc2_count <<- vc2_count + 1L
    if (vc1_count == vc1_limit || vc2_count == vc2_limit) TRUE else FALSE
  }
  pos <- Position(helper, x)
  if (is.na(pos)) length(x) else pos
}

df |> 
  group_by(ID) |> 
  filter(row_number() <= foo(Change, vc1 = "B-AL", vc2 = "AL-X"))

#       ID Date       Change  wage
#  1   201 04.01.2023 B-AL     900
#  2   201 01.03.2023 AL-AL    900
#  3   201 01.10.2023 AL-AL    900
#  4   201 10.11.2023 AL-B    1900
#  5   201 28.11.2023 B-AL     900
#  6   301 03.01.2023 B-AL    1100
#  7   301 11.02.2023 AL-B    2500
#  8   301 15.04.2023 B-AL    1110
#  9   401 02.01.2023 B-AL     700
# 10   401 28.01.2023 AL-X       0
© www.soinside.com 2019 - 2024. All rights reserved.