如果任何剩余值为0,则将值设置为0

时间:2020-07-17 10:54:49

标签: r dataframe dplyr

我有这样的data.frame

dat <- data.frame("ID"=c(rep(1,13),rep(2,5)), "time"=c(seq(1,13),c(seq(1,5))), "value"=c(rep(0,5), rep(1,3), 2, 0, 1, 5, 20, rep(0,2), seq(1:3)))
   ID time value
1   1    1     0
2   1    2     0
3   1    3     0
4   1    4     0
5   1    5     0
6   1    6     1
7   1    7     1
8   1    8     1
9   1    9     2
10  1   10     0
11  1   11     1
12  1   12     5
13  1   13    20
14  2    1     0
15  2    2     0
16  2    3     1
17  2    4     2
18  2    5     3

我的目标是将所有值设置为0,如果其余值中还有其他0(对于每个唯一的ID并按time排序)。这意味着在示例数据中,我想在6:9行中插入0

我尝试了dat %>% group_by(ID) %>% mutate(value2 = ifelse(lead(value, order_by=time)==0, 0, value)),但是我不得不运行几次,因为它一次只能更改一行(即首先更改第9行,然后更改第8行,等等)。

dplyr解决方案将是首选,但我会采取一切可行的方法:)

简短的解释:值是肿瘤的大小。如果肿瘤没有长大,但实际上在以后完全消失,则很可能是无关的包囊,因此应将其编码为“零肿瘤”。

4 个答案:

答案 0 :(得分:5)

我不确定这是否是您想要的输出,但也许对您有用

dat %>%
  group_by(ID) %>%
  arrange(-time) %>%
  mutate(value = if_else(cumsum(value == 0) > 0, 0, value)) %>%
  arrange(ID, time)

     ID  time value
   <dbl> <int> <dbl>
 1     1     1     0
 2     1     2     0
 3     1     3     0
 4     1     4     0
 5     1     5     0
 6     1     6     0
 7     1     7     0
 8     1     8     0
 9     1     9     0
10     1    10     0
11     1    11     1
12     1    12     5
13     1    13    20
14     2     1     0
15     2     2     0
16     2     3     1
17     2     4     2
18     2     5     3

Basicalyl,我首先将观察值降序排列。然后,检查值是否为零(cumsum(value == 0) > 0))。如果是,我将所有剩余值设置为零。 最后,我再次按照正确的顺序放置观察结果。

如果您不想对数据进行排序和重新排序,则可以使用以下代码,该代码依赖于相同的逻辑,但难于读取:

dat %>%
  group_by(ID) %>%
  arrange(ID, time) %>%
  mutate(value = if_else(cumsum(value == 0) < sum(value == 0), 0, value))

或者在没有if_else的情况下效率更高:

dat %>%
  group_by(ID) %>%
  arrange(ID, time) %>%
  mutate(value = value * (cumsum(value == 0) >= sum(value == 0)))

答案 1 :(得分:2)

一种方法可能是找到0的第一个和最后一个出现的索引,并替换之间的所有内容。

library(dplyr)

dat %>%
  group_by(ID) %>%
  mutate(value = replace(value, between(row_number(), which.max(value == 0), tail(which(value == 0), 1)), 0))

# A tibble: 18 x 3
# Groups:   ID [2]
      ID  time value
   <dbl> <int> <dbl>
 1     1     1     0
 2     1     2     0
 3     1     3     0
 4     1     4     0
 5     1     5     0
 6     1     6     0
 7     1     7     0
 8     1     8     0
 9     1     9     0
10     1    10     0
11     1    11     1
12     1    12     5
13     1    13    20
14     2     1     0
15     2     2     0
16     2     3     1
17     2     4     2
18     2     5     3

答案 2 :(得分:1)

使用data.table,您可以按一定顺序计算数据字段,而无需实际重新排列数据框。在这里有用

library(data.table)
setDT(dat)

dat[order(-time), value := fifelse(cumsum(value == 0) > 0, 0, value), ID]

dat
#     ID time value
#  1:  1    1     0
#  2:  1    2     0
#  3:  1    3     0
#  4:  1    4     0
#  5:  1    5     0
#  6:  1    6     0
#  7:  1    7     0
#  8:  1    8     0
#  9:  1    9     0
# 10:  1   10     0
# 11:  1   11     1
# 12:  1   12     5
# 13:  1   13    20
# 14:  2    1     0
# 15:  2    2     0
# 16:  2    3     1
# 17:  2    4     2
# 18:  2    5     3

答案 3 :(得分:0)

您可以在accumulate(..., .dir = "backward")中使用purrr

library(dplyr)
library(purrr)

dat %>%
  group_by(ID) %>%
  arrange(time, .by_group = T) %>%
  mutate(value2 = accumulate(value, ~ if(.y == 0) 0 else .x, .dir = "backward")) %>%
  ungroup()

# A tibble: 18 x 4
      ID  time value value2
   <dbl> <int> <dbl>  <dbl>
 1     1     1     0      0
 2     1     2     0      0
 3     1     3     0      0
 4     1     4     0      0
 5     1     5     0      0
 6     1     6     1      0
 7     1     7     1      0
 8     1     8     1      0
 9     1     9     2      0
10     1    10     0      0
11     1    11     1      1
12     1    12     5      5
13     1    13    20     20
14     2     1     0      0
15     2     2     0      0
16     2     3     1      1
17     2     4     2      2
18     2     5     3      3