【问题标题】:Set value to 0 if any of the remaining values is 0如果任何剩余值为 0,则将值设置为 0
【发布时间】:2020-11-07 04:11:10
【问题描述】:

我有一个这样的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 排序)。这意味着在示例数据中,我希望 0 在行 6:9 中。

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

dplyr 解决方案会更受欢迎,但我会接受所有可行的方法:)

简短的解释:值是肿瘤的大小。如果肿瘤没有变大,但后来实际上完全消失了,很可能是不相关的包裹,因此应编码为“零肿瘤”。

【问题讨论】:

    标签: r dataframe dplyr


    【解决方案1】:

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

    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
    

    【讨论】:

      【解决方案2】:

      使用 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】:

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

        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) &gt; 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)))
        

        【讨论】:

          【解决方案4】:

          一种方法是查找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
          

          【讨论】:

            猜你喜欢
            • 2022-01-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-07-30
            • 1970-01-01
            • 2015-05-28
            • 1970-01-01
            相关资源
            最近更新 更多