首页 > 解决方案 > 根据R中不同的运行长度替换连续的重复值

问题描述

考虑以下数据集:

dat<-data.frame(id = c(1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3),
                var1 = c("A","NA","B","A","NA","NA","B","A","NA","NA","NA","C","A","NA","B","A","NA","NA","D","A","NA","NA","B"))

dat

首先,我需要用 NA 两侧的值填写所有 NA,这在 dplyr 中是成功的:

mutate(value = ifelse(is.na(value), paste0(na.locf(value), "-", na.locf(value, fromLast=TRUE)), 
                        value))

这导致:

   id var1
1   1    A
2   1  A-B
3   1    B
4   1    A
5   1  A-B
6   1  A-B
7   1    B
8   2    A
9   2  A-C
10  2  A-C
11  2  A-C
12  2    C
13  2    A
14  2  A-B
15  2    B
16  3    A
17  3  A-D
18  3  A-D
19  3    D
20  3    A
21  3  A-B
22  3  A-B
23  3    B

但是,我现在需要根据重复的连续运行长度(按 id col 分组)保留一些值,同时将其他值返回 NA。如果 AB 的连续重复长于 1 则将所有值返回给 NA,如果 AC 的连续重复长于 2 则将所有值返回给 NA,如果 AD 的连续重复长于 3 则返回所有值到北美。

我想要的结果是:

   id var1
1   1    A
2   1  A-B
3   1    B
4   1    A
5   1   NA
6   1   NA
7   1    B
8   2    A
9   2   NA
10  2   NA
11  2   NA
12  2    C
13  2    A
14  2  A-B
15  2    B
16  3    A
17  3  A-D
18  3  A-D
19  3    D
20  3    A
21  3   NA
22  3   NA
23  3    B

我认为这可以通过group_by(id), thenrle()或 data.table's的一些组合来完成,然后rleid()根据值和使用 case_when 的运行长度有条件地将值转回 NA (我考虑过ifelse(),但我的条件比示例中提供的要多得多,并且已阅读 case_when 将是一个更好的选择),但我无法弄清楚如何编写精确的代码来执行此操作。我遇到的一个类似问题是将 NA 替换为具有限制的先前值,但是,它是我需要做的更简单的版本。

任何建议将不胜感激。我觉得我很接近,但我需要帮助才能达到预期的结果。

标签: rgroup-bydplyrrle

解决方案


你可以做什么:

myfun <- function(x){
  y <- rle(x)
  z <- match(y$values, LETTERS)
  ind <- which(is.na(z))
  m <- z[ind + 1] - z[ind - 1] >= y$lengths[ind]
  y$values[ind[m]] <- paste(y$values[ind[m] - 1], y$values[ind[m] + 1], sep = "-")
  inverse.rle(y)
}


transform(dat, var1 = ave(var1, id, FUN = myfun))

   id var1
1   1    A
2   1  A-B
3   1    B
4   1    A
5   1   NA
6   1   NA
7   1    B
8   2    A
9   2   NA
10  2   NA
11  2   NA
12  2    C
13  2    A
14  2  A-B
15  2    B
16  3    A
17  3  A-D
18  3  A-D
19  3    D
20  3    A
21  3   NA
22  3   NA
23  3    B

推荐阅读