【问题标题】:How to mutate NA on multiple rows (rowwise) in tibble如何在 tibble 中的多行(按行)上改变 NA
【发布时间】:2025-11-30 13:30:01
【问题描述】:

我花了一些时间试图弄清楚如何在tibble 的行透视图中改变多行上的NA 值,tibble 有 3 个观察值和 6 个变量,生成如下:

df <- data.frame(ID = c(1, 2, 3),
                 Score1 = c(90, 80, 70),
                 Score2 = c(66, 78, 86),
                 Score3 = c(NA, 86, 96),
                 Score4 = c(84, 76, 72),
                 Score5 = c(92, NA, 74))
sample_tibble <- as_tibble(df)

tibble 看起来像

# A tibble: 3 x 6
     ID Score1 Score2 Score3 Score4 Score5
  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1     1     90     66     NA     84     92
2     2     80     78     86     76     NA
3     3     70     86     96     72     74

我必须使用来自tidyverse 的函数(例如mutatemutate_atrowwise.. 等),目标是替换第1 行的NA(在Score3 列中)和第 2 行(在Score5 列中)分别与第 1 行和第 2 行的meanmean 使用行上的其他值而不是NA 计算),所以理想的结果应该是在变异之后

# A tibble: 3 x 6
     ID Score1 Score2 Score3 Score4 Score5
  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1     1     90     66     83     84     92
2     2     80     78     86     76     80
3     3     70     86     96     72     74

第一个NA 替换为mean(c(90, 66, NA, 84, 92), na.rm = TRUE)83
第二个NA 替换为mean(c(80, 78, 86, 76, NA), na.rm = TRUE)80

尝试了一些类似下面的代码,并且还检查了以前的文档为Apply a function to every row of a matrix or a data framedplyr - using mutate() like rowmeans(),但是由于我能够弄清楚mutate 函数的主体,所以代码永远不会工作

sample_tibble[, -1] %>% rowwise() %>% mutate(...)

不限于rowwisemutate(如mutate_at 也不错),有没有任何解决方案能够改变第1 行和第2 行以达到目标格式(其非常适合同时变异,而不是使用for loop 变异两次),感谢任何解决方案!

【问题讨论】:

    标签: r tidyverse dplyr tibble


    【解决方案1】:

    一个稍微低效的方法是gathergroup_by它:

    sample_tibble %>%
      tidyr::gather(k, v, -ID) %>%
      group_by(ID) %>%
      mutate(v = if_else(is.na(v), mean(v, na.rm = TRUE), v)) %>%
      ungroup() %>%
      tidyr::spread(k, v)
    # # A tibble: 3 x 6
    #      ID Score1 Score2 Score3 Score4 Score5
    #   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
    # 1     1     90     66     83     84     92
    # 2     2     80     78     86     76     80
    # 3     3     70     86     96     72     74
    

    正如 RonakShah 也提醒我的那样,gather/spread 可以替换为更新(且功能更多)的表亲:pivot_longer/pivot_wider

    另一种技术使用apply

    sample_tibble %>%
      mutate(mu = apply(.[,-1], 1, mean, na.rm = TRUE)) %>%
      ### similarly, and faster, thanks RonakShah
      # mutate(mu = rowMeans(.[,-1], na.rm = TRUE)) %>%
      mutate_at(vars(starts_with("Score")), ~ if_else(is.na(.), mu, .)) %>%
      select(-mu)
    

    需要注意的是:.[,-1] 明确使用除第一列之外的每一列;如果您有问题中未提及的其他列,那么这肯定会使用比您预期更多的数据。不幸的是,我不知道在这个解决方案中使用:-ranging 的方法,因为这样会更清楚。

    【讨论】:

      【解决方案2】:

      一种利用一点数学的方法可能是:

      df %>%
       mutate_at(vars(-1), 
                 ~ pmax(is.na(.)*rowMeans(select(df, -1), na.rm = TRUE), 
                        (!is.na(.))*., 
                        na.rm = TRUE))
      
      
        ID Score1 Score2 Score3 Score4 Score5
      1  1     90     66     83     84     92
      2  2     80     78     86     76     80
      3  3     70     86     96     72     74
      

      【讨论】:

        最近更新 更多