【问题标题】:Using dplyr to make row wise conditions amidst missing values使用 dplyr 在缺失值中生成逐行条件
【发布时间】:2021-11-08 22:05:06
【问题描述】:

我有一个大型数据集,其中包含个人、他们的年龄和 5 次测试的分数以及一些缺失的数据。数据集如下所示:

id<-c(1, 2, 3, 4, 5, 6, 7)
age<-c(25, 43, 55, 12, 15, 67, 71)
score1<-c(1, 2, 1, 2, 1, 2, 2)
score2<-c(5, NA, NA, 5, 6, 7, 5)
score3<-c(NA, NA, NA, NA, 6, 7, 6)
score4<-c( 5, NA, NA, NA, NA, 6, NA)
score5<-c(5, 5, 4, 4, 5, 5, NA)
df<-data.frame(id, age, score1, score2, score3, score4, score5)
df
  id age score1 score2 score3 score4 score5
1  1  25      1      5     NA      5      5
2  2  43      2     NA     NA     NA      5
3  3  55      1     NA     NA     NA      4
4  4  12      2      5     NA     NA      4
5  5  15      1      6      6     NA      5
6  6  67      2      7      7      6      5
7  7  71      2      5      6     NA     NA

对于 2-5 分,我想创建一个 Missing 条件,如果每个 id 在 2-5 分之间有超过 30% 的缺失数据 (NA),则 Missing=YES 否则 @987654327 @=否

所需的输出应如下所示:

  id age score1 score2 score3 score4 score5 missing
1  1  25      1      5     NA      5      5      no
2  2  43      2     NA     NA     NA      5     yes
3  3  55      1     NA     NA     NA      4     yes
4  4  12      2      5     NA     NA      4     yes
5  5  15      1      6      6     NA      5      no
6  6  67      2      7      7      6      5      no
7  7  71      2      5      6     NA     NA     yes

在 dplyr 中创建逐行条件的最佳方法是什么?我尝试在df %&gt;% rowwise() %&gt;% missing=ifelse(sum(is.na(.x))/length(.x)* 100 &gt;=30), "yes", "no") 中使用dplyr::mutate 和ifelse,但我认为这是错误的并且似乎不起作用。

【问题讨论】:

标签: r dplyr conditional-statements purrr plyr


【解决方案1】:

我们可以使用rowMeans(condition)。我建议您在这里使用num_range 选择,它非常适合您的列选择语句。

df %>% mutate(missing=ifelse(rowMeans(across(num_range('score', 2:5), is.na))>0.3, 'yes', 'no'))

  id age score1 score2 score3 score4 score5 missing
1  1  25      1      5     NA      5      5      no
2  2  43      2     NA     NA     NA      5     yes
3  3  55      1     NA     NA     NA      4     yes
4  4  12      2      5     NA     NA      4     yes
5  5  15      1      6      6     NA      5      no
6  6  67      2      7      7      6      5      no
7  7  71      2      5      6     NA     NA     yes

【讨论】:

    【解决方案2】:

    (我的建议与 GuedesBF 提供的建议几乎相同,但当我点击“发布”时我没有看到它。为了基准和讨论,我将其保持可见。) em>

    不使用rowwise的替代方案。

    df %>%
      mutate(Missing = rowMeans(is.na(subset(., select=score2:score5))) > 0.3)
    #   id age score1 score2 score3 score4 score5 Missing
    # 1  1  25      1      5     NA      5      5   FALSE
    # 2  2  43      2     NA     NA     NA      5    TRUE
    # 3  3  55      1     NA     NA     NA      4    TRUE
    # 4  4  12      2      5     NA     NA      4    TRUE
    # 5  5  15      1      6      6     NA      5   FALSE
    # 6  6  67      2      7      7      6      5   FALSE
    # 7  7  71      2      5      6     NA     NA    TRUE
    

    优点是性能。一般来说,逐行操作会比较慢,如果您的数据明显大,那么这可能是个问题。

    bench::mark(
      rowwise = df %>% rowwise() %>% mutate(missing=mean(is.na(across(score2:score5)))>0.3) %>% ungroup(), 
      rowMeans = df %>% mutate(Missing = rowMeans(is.na(subset(., select=score2:score5))) > 0.3),
      guedes = df %>% mutate(missing=rowMeans(across(num_range('score', 2:5), is.na))>0.3),
      check=FALSE)
    # # A tibble: 3 x 13
    #   expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory                  time           gc                
    #   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list>                  <list>         <list>            
    # 1 rowwise      5.91ms   6.61ms      146.   10.05KB     0       74     0      507ms <NULL> <Rprofmem[,3] [21 x 3]> <bch:tm [74]>  <tibble [74 x 3]> 
    # 2 rowMeans      1.7ms      2ms      439.    1.64KB     2.30   191     1      435ms <NULL> <Rprofmem[,3] [4 x 3]>  <bch:tm [192]> <tibble [192 x 3]>
    # 3 guedes       2.97ms   3.44ms      272.   13.24KB     0      137     0      503ms <NULL> <Rprofmem[,3] [21 x 3]> <bch:tm [137]> <tibble [137 x 3]>
    

    rowMeans 方法的速度大约是 3 倍。

    【讨论】:

    • 嗨,r2evans,我怀疑 rowMeans 解决方案应该是最有效的,不是吗?
    • 在我的回答中我使用了across 而不是subset(., x),我想知道它是否会有所不同
    • 是的,它确实有所作为......它比rowwise 快,但仍然是subset 速度的一半。
    • 有趣的是across 在性能方面仍然存在不足(尽管我相信它在清晰度和可读性方面表现出色)。这在 dplyr 的早期版本中得到了承认,但 tidyverse 团队声称从 dplyr 1.0.4 开始,性能差距已经缩小(或减轻):tidyverse.org/blog/2021/02/dplyr-1-0-4-if-any
    • 也许吧。我建议对 7 行数据进行基准测试可能会受到基本函数开销的影响;使用更大的数据集,任何惩罚都可能(甚至可能)减少或逆转。
    【解决方案3】:
    df %>%
    rowwise %>%
    mutate(missing=ifelse(mean(is.na(across(score2:score5)))>0.3,'yes','no')) %>% 
    ungroup
    

    输出;

        id   age score1 score2 score3 score4 score5 missing
      <dbl> <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <chr>  
    1     1    25      1      5     NA      5      5 no     
    2     2    43      2     NA     NA     NA      5 yes    
    3     3    55      1     NA     NA     NA      4 yes    
    4     4    12      2      5     NA     NA      4 yes    
    5     5    15      1      6      6     NA      5 no     
    6     6    67      2      7      7      6      5 no     
    7     7    71      2      5      6     NA     NA yes    
    

    【讨论】:

      猜你喜欢
      • 2019-12-28
      • 2017-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-09
      • 1970-01-01
      • 2014-04-15
      相关资源
      最近更新 更多