【问题标题】:Replace duplicates with NA after the maximum in a series has been reached达到系列中的最大值后,用 NA 替换重复项
【发布时间】:2021-10-15 20:18:16
【问题描述】:

想象一个看起来像这样的data.frame

id  t1  t2  t3  t4  t5
1   5   10  11  11  11
2   6   7   12  13  16
3   1   2   2   2   2
4   3   3   4   4   4

数字在各行中不断增加,但在某些时候数字系列达到最大值,并且重复相同的数字。我如何使用R 将其转换为data.frame,其中“重复”的数字被NA 替换或简单地留空?即,变成这样:

id  t1  t2  t3  t4  t5
1   5   10  11  NA  NA
2   6   7   12  13  16
3   1   2   NA  NA   NA
4   3   3   4   NA   NA

【问题讨论】:

    标签: r dataframe data-manipulation


    【解决方案1】:
    dat[cbind(FALSE, t(apply(dat[,-1], 1, function(z) duplicated(z) & z >= max(z))))] <- NA
    dat
    #   id t1 t2 t3 t4 t5
    # 1  1  5 10 11 NA NA
    # 2  2  6  7 12 13 16
    # 3  3  1  2 NA NA NA
    # 4  4  3  3  4 NA NA
    

    细分:

    1. 由于我们需要按行工作,我们将使用apply(dat, 1, .)

    2. 在每一行上,我们需要那些大于或等于最大值的那些 并且 重复,因此使用 anon-func

      function(z) duplicated(z) & z >= max(z)
      

      这本身会产生一个转置矩阵(因为 R 的 apply 是如何运作的),然后我们将其 transpose 转换为一个形状正确的逻辑矩阵:

      t(apply(dat[,-1], 1, function(z) duplicated(z) & z >= max(z)))
      #         t1    t2    t3    t4    t5
      # [1,] FALSE FALSE FALSE  TRUE  TRUE
      # [2,] FALSE FALSE FALSE FALSE FALSE
      # [3,] FALSE FALSE  TRUE  TRUE  TRUE
      # [4,] FALSE FALSE FALSE  TRUE  TRUE
      
    3. 我们用dat[,-1] 省略了id 列,但是为了重新分配NA,我们需要cbind(FALSE, .) 以便保留id 列。

    4. 最后,我们使用 dat[.] &lt;- NA 重新分配给这些字段。

    PS:其他答案中使用的替代函数在这里也同样有效:

    # equivalent with this sample data
    function(z) duplicated(z) & z >= max(z)
    function(z) seq_along(z) > which.max(z)
    

    答案的最大差异(到目前为止)是偏爱 R 方言,无论是基本方言还是 dplyr+purrr


    数据

    dat <- structure(list(id = 1:4, t1 = c(5L, 6L, 1L, 3L), t2 = c(10L, 7L, 2L, 3L), t3 = c(11L, 12L, NA, 4L), t4 = c(NA, 13L, NA, NA), t5 = c(NA, 16L, NA, NA)), row.names = c(NA, -4L), class = "data.frame")
    

    【讨论】:

    • 我想到了df[cbind(FALSE, FALSE, (df[,-1] == apply(df[, -1], 1, max))[,-ncol(df)+1])] &lt;- NA,但它与您的答案太相似,无法发布。
    【解决方案2】:

    使用dplyrpurrr 的一个选项可能是:

    df %>%
        mutate(pmap_dfr(across(-id), ~ `[<-`(c(...), seq_along(c(...)) > which.max(c(...)), NA)))
    
      id t1 t2 t3 t4 t5
    1  1  5 10 11 NA NA
    2  2  6  7 12 13 16
    3  3  1  2 NA NA NA
    4  4  3  3  4 NA NA
    

    【讨论】:

      【解决方案3】:

      感谢@Martin Gal 提出如此巧妙的提示。是的,我们确实可以省去else 声明:

      library(dplyr)
      library(purrr)
      
      df %>%
        pmap_df(~ {x <- c(...)[-1]
        ind <- which.max(x)
        if(ind < length(x)) {
          x[(ind + 1):length(x)] <- NA
        } 
        c(c(...)[1], x)
        })
      
      # A tibble: 4 x 6
           id    t1    t2    t3    t4    t5
        <int> <int> <int> <int> <int> <int>
      1     1     5    10    11    NA    NA
      2     2     6     7    12    13    16
      3     3     1     2    NA    NA    NA
      4     4     3     3     4    NA    NA
      

      【讨论】:

      • 完全没有关系,但是你可以简单地将x } else { x }替换为} x。您不需要else-statement,只需在if-construct 之外返回x
      • 谢谢你亲爱的马丁,还有比你更好的人吗? :D 我的代码还需要一个小的编辑,因为我忘记在输出中包含 id 列。
      【解决方案4】:

      我能看到的最简单的方法是将表格融合为长格式(使用 data.table 融合函数),然后编写一个自定义函数,该函数接受一个数字序列并返回预期的输出。然后我只是将表格恢复为原始格式。我敢肯定,有一些非常酷的解决方案要好得多,但至少可以按预期工作。

      library(data.table)
      x <- data.table('id' = c(1,2,3,4),
                      't1' = c(5,6,1,3),
                      't2' = c(10, 7, 2, 3),
                      't3' = c(11, 12, 2, 4),
                      't4' = c(11, 13, 2, 4),
                      't5' = c(11, 16, 2, 4))
      
      x <- melt.data.table(x, id.vars = 'id')
      
      find_max_replace_subsequent <- function(numbers){
        
        max_is <- max(numbers)
        max_found <- 0
      
        for(i in c(1:length(numbers))){
          current_number <- numbers[i]
      
          if(max_found == 1){
            numbers[i] <- NA
      
          }
          if(current_number == max_is & max_found == 0){
            max_found <- 1
          }
          
        }
        
        return(numbers)
      }
      x[, 'value' := find_max_replace_subsequent(numbers = value), by = c(by1 = 'id')]
      
      x <- dcast.data.table(x, formula = id~variable, value.var = 'value')
      x
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-06-27
        • 1970-01-01
        • 2021-06-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-11
        相关资源
        最近更新 更多