【问题标题】:Replace NA with mean of variable grouped by time and treatment用按时间和治疗分组的变量的平均值替换 NA
【发布时间】:2020-09-01 10:32:33
【问题描述】:

我有一个数据框,类似于下面的数据框(参见 dput),记录变量对治疗的响应:

df <- structure(list( time = c(0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 33, 33, 33, 33, 33, 33, 90, 90, 90, 90, 90, 90),
                      trt = structure(c(2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L),
                      .Label = c("1", "2"), class = "factor"), 
               A1 = c(6.301, 5.426, 5.6021, NA, NA, NA, 6.1663, 6.426, 6.8239, 2.301, 4.7047, 2.301, 5.8062, 4.97, 4.97, 2.301, 2.301, 2.301, 2.301, 2.301, 2.301, 2.301, 2.301, 2.301),
               B1 = c(5.727, 5.727, 5.4472, NA, NA, NA, 6.6021, 7.028, 7.1249, 3.028, 3.1663, 3.6021, 5.727, 5.2711, 5.2389, 3.3554, 3.9031, 4.2389, 3.727, 3.6021, 3.6021, 3.8239, 3.727, 3.426)),
               row.names = c(NA, -24L), class = c("tbl_df", "tbl", "data.frame"))

看起来是这样的:

    time trt      A1    B1
   <dbl> <fct> <dbl> <dbl>
 1     0 2      6.30  5.73
 2     0 2      5.43  5.73
 3     0 2      5.60  5.45
 4     0 1     NA    NA   
 5     0 1     NA    NA   
 6     0 1     NA    NA   
 7    14 2      6.17  6.60
 8    14 2      6.43  7.03
 9    14 2      6.82  7.12
10    14 1      2.30  3.03

在我们的实验中,我们并不总是在时间 == 0 时记录所有处理的值。我想在(且仅在)时间 == 0 时用 trt 的平均值替换任何缺失值 (NA) 2'组在时间== 0。所以A1中的NA都变成5.78,B1中的NA都变成5.63。

使用来自herehere 以及其他一些人的答案,我已经能够提出以下建议:

df %>% 
  mutate_if(is.numeric, funs(if_else(is.na(.),if_else(time == 0, 0, .), .)))

这会将时间 == 0 时的 NA 替换为 0(这对于我的一些变量很有用,其中在时间 == 0 的任何处理中都没有数据,但不是我在这里所追求的)。我也试过这个:

df %>% 
  mutate_if(is.numeric, funs(if_else(is.na(.),if_else(time == 0, mean(., na.rm = TRUE), .), .)))

这更接近我想要的,但是是对整个列/变量的值进行平均。当时间 == 0 时,我可以让它只平均来自治疗“2”的那些值吗?

【问题讨论】:

    标签: r dplyr na


    【解决方案1】:

    由于我无法访问 dplyr 的开发版本以使用新的 cross() 函数,因此我结合了上述两个答案的元素以给出我想要的结果:

    df %>%
      mutate_if(is.numeric, funs(if_else(is.na(.) & time == 0, mean(.[trt == "2" & time == 0]), .)))
    

    从长远来看,cross() 似乎旨在替换 _if 函数 (see here),但此解决方案同时有效。

    【讨论】:

      【解决方案2】:

      如果我们添加group_by(time),我们可以将缺失的列重新编码为time == 0 的观测值的特定时间平均值,如下所示。

      df <- structure(list( time = c(0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 33, 33, 33, 33, 33, 33, 90, 90, 90, 90, 90, 90),
                            trt = structure(c(2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 1L, 1L),
                                            .Label = c("1", "2"), class = "factor"), 
                            A1 = c(6.301, 5.426, 5.6021, NA, NA, NA, 6.1663, 6.426, 6.8239, 2.301, 4.7047, 2.301, 5.8062, 4.97, 4.97, 2.301, 2.301, 2.301, 2.301, 2.301, 2.301, 2.301, 2.301, 2.301),
                            B1 = c(5.727, 5.727, 5.4472, NA, NA, NA, 6.6021, 7.028, 7.1249, 3.028, 3.1663, 3.6021, 5.727, 5.2711, 5.2389, 3.3554, 3.9031, 4.2389, 3.727, 3.6021, 3.6021, 3.8239, 3.727, 3.426)),
                      row.names = c(NA, -24L), class = c("tbl_df", "tbl", "data.frame"))
      
      library(dplyr)
      df %>% group_by(time)  %>%
           mutate(A1 = if_else(is.na(A1) & time == 0,mean(A1,na.rm=TRUE),A1),
                  B1 = if_else(is.na(B1) & time == 0,mean(B1,na.rm=TRUE),B1))
      

      ...和输出:

      # A tibble: 24 x 4
      # Groups:   time [4]
          time trt      A1    B1
         <dbl> <fct> <dbl> <dbl>
       1     0 2      6.30  5.73
       2     0 2      5.43  5.73
       3     0 2      5.60  5.45
       4     0 1      5.78  5.63
       5     0 1      5.78  5.63
       6     0 1      5.78  5.63
       7    14 2      6.17  6.60
       8    14 2      6.43  7.03
       9    14 2      6.82  7.12
      10    14 1      2.30  3.03
      # ... with 14 more rows
      > 
      

      更新:跨多列的通用解决方案

      根据我回答中的 cmets,这是一个使用 dplyr 的开发版本访问新的 across() 函数的解决方案。

      devtools::install_github("tidyverse/dplyr") # needed for across()
      # get all columns except time and trt
      theColumns <- colnames(df)[!(colnames(df) %in% c("time","trt"))]
      df %>% group_by(time)  %>%
           mutate(across(theColumns,~if_else(is.na(.) & time == 0,mean(.,na.rm=TRUE),.)))
      

      ...和输出:

      # Groups:   time [4]
          time trt      A1    B1
         <dbl> <fct> <dbl> <dbl>
       1     0 2      6.30  5.73
       2     0 2      5.43  5.73
       3     0 2      5.60  5.45
       4     0 1      5.78  5.63
       5     0 1      5.78  5.63
       6     0 1      5.78  5.63
       7    14 2      6.17  6.60
       8    14 2      6.43  7.03
       9    14 2      6.82  7.12
      10    14 1      2.30  3.03
      # … with 14 more rows
      > 
      

      【讨论】:

      • 谢谢你 - 我更喜欢像你这样的 tidyverse / dplyr 解决方案,但这个也取代了除 time == 0 以外的组中的 NA,我不想要...
      • @Mark - 鉴于测试数据,不清楚您是否希望将其他时间值保留为 NA。这很容易解决(请参阅我的回答中的细微调整)。
      • 感谢您的更新。与 Alen 相同的问题(对问题不够清晰表示歉意),这是否可以应用于一组变量而不像你一样明确命名它们,也就是说 - 对 A1、B1、C1 等做同样的事情?
      • @Mark - 是的,将在美国时间晚上晚些时候发布更新。
      • @Mark -- 查看更新的答案,使用dplyr 1.0.0 及其新的across() 函数。
      【解决方案3】:

      我想我会为此使用基础 R 中的索引:

      within(df, {A1[is.na(A1) & time == 0] <- mean(A1[trt == "2" & time == 0])
                  B1[is.na(B1) & time == 0] <- mean(B1[trt == "2" & time == 0])})
      #> # A tibble: 24 x 4
      #>     time trt      A1    B1
      #>    <dbl> <fct> <dbl> <dbl>
      #>  1     0 2      6.30  5.73
      #>  2     0 2      5.43  5.73
      #>  3     0 2      5.60  5.45
      #>  4     0 1      5.78  5.63
      #>  5     0 1      5.78  5.63
      #>  6     0 1      5.78  5.63
      #>  7    14 2      6.17  6.60
      #>  8    14 2      6.43  7.03
      #>  9    14 2      6.82  7.12
      #> 10    14 1      2.30  3.03
      #> # ... with 14 more rows
      

      reprex package (v0.3.0) 于 2020-05-15 创建

      【讨论】:

      • 谢谢,这完全符合我的要求。由于我有两个以上的变量要应用它,是否可以在不明确说明每个变量的情况下做到这一点?例如我可以在一个循环中尝试这个,并循环通过 A1、B1、C1 等吗?
      • 类似这样的东西:for (i in c("A1", "B1")){ within(df, {i[is.na(i) &amp; time == 0] &lt;- mean(i[trt == "2" &amp; time == 0])})} 但它给出的错误:In mean.default(i[trt == "2" &amp; time == 0]) : argument is not numeric or logical: returning NA
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-29
      • 2021-03-10
      • 2021-01-15
      • 2019-02-03
      • 1970-01-01
      • 1970-01-01
      • 2019-11-26
      相关资源
      最近更新 更多