【问题标题】:Problem using rowwise() to count the number of NA's in each row of a dataframe使用 rowwise() 计算数据帧每行中 NA 的数量的问题
【发布时间】:2021-07-12 23:50:14
【问题描述】:

我在使用 rowwise() 计算每行中的 NA 数量时遇到问题。我的最小示例:

df <- data.frame(Q1 = c(rep(1, 1), rep(NA, 9)),
                 Q2 = c(rep(2, 2), rep(NA, 8)),
                 Q3 = c(rep(3, 3), rep(NA, 7))
)
df
   Q1 Q2 Q3
1   1  2  3
2  NA  2  3
3  NA NA  3
4  NA NA NA
5  NA NA NA
6  NA NA NA
7  NA NA NA
8  NA NA NA
9  NA NA NA
10 NA NA NA

我想创建一个新列来计算每行中的 NA 数量。我可以通过编写非常简单地做到这一点

 df$Count_NA <- rowSums(is.na(df))
 df
   Q1 Q2 Q3 Count_NA
1   1  2  3        0
2  NA  2  3        1
3  NA NA  3        2
4  NA NA NA        3
5  NA NA NA        3
6  NA NA NA        3
7  NA NA NA        3
8  NA NA NA        3
9  NA NA NA        3
10 NA NA NA        3

但如果我尝试使用 rowwise() 通过 dplyr 执行此操作,我会得到错误的答案 - 列 Count_NA 在每一行中具有相同的数字:

df %>%
   rowwise() %>%
   mutate(Count_NA = sum(is.na(.)))
# A tibble: 10 x 4
# Rowwise: 
      Q1    Q2    Q3 Count_NA
   <dbl> <dbl> <dbl>    <int>
 1     1     2     3       24
 2    NA     2     3       24
 3    NA    NA     3       24
 4    NA    NA    NA       24
 5    NA    NA    NA       24
 6    NA    NA    NA       24
 7    NA    NA    NA       24
 8    NA    NA    NA       24
 9    NA    NA    NA       24
10    NA    NA    NA       24

我做错了什么,我该如何解决?

在此先感谢

托马斯·飞利浦

【问题讨论】:

标签: r dplyr rowwise


【解决方案1】:

使用cur_data() 而不是点。 .[cur_group_id(), ]c(Q1, Q2, Q3)across()c_across()(或 c_across 与其他答案的参数相同)也可以。

请注意,最好在之后使用ungroup,否则它会保留按行的内存,以后可能会得到意想不到的结果。

df %>%
   rowwise() %>%
   mutate(Count_NA = sum(is.na(cur_data()))) %>%
   ungroup

给予:

# A tibble: 10 x 4
      Q1    Q2    Q3 Count_NA
   <dbl> <dbl> <dbl>    <int>
 1     1     2     3        0
 2    NA     2     3        1
 3    NA    NA     3        2
 4    NA    NA    NA        3
 5    NA    NA    NA        3
 6    NA    NA    NA        3
 7    NA    NA    NA        3
 8    NA    NA    NA        3
 9    NA    NA    NA        3
10    NA    NA    NA        3

【讨论】:

【解决方案2】:

一个问题是. 在这里解析为整个框架,而不仅仅是整行。 另一种dplyr方法,使用c_across

df %>%
    rowwise() %>%
    mutate(a=sum(is.na(c_across(everything()))))
# # A tibble: 10 x 4
# # Rowwise: 
#       Q1    Q2    Q3     a
#    <dbl> <dbl> <dbl> <int>
#  1     1     2     3     0
#  2    NA     2     3     1
#  3    NA    NA     3     2
#  4    NA    NA    NA     3
#  5    NA    NA    NA     3
#  6    NA    NA    NA     3
#  7    NA    NA    NA     3
#  8    NA    NA    NA     3
#  9    NA    NA    NA     3
# 10    NA    NA    NA     3

我可以看到使用它和cur_data() 之间的最大区别是c_across 允许更直接地进行变量选择,如c_across(starts_with("Q"))。当然,人们总是可以select(cur_data(),...),所以这是一个弱论点。

【讨论】:

  • 谢谢 - 如何确定 .解析为?
  • 坦率地说,我先是func &lt;- function(z, ...) {browser();0;},然后是df %&gt;% rowwise() %&gt;% mutate(Count_NA = func(.)),然后又看了看z。它无助于解决问题,只是为了识别它。
  • @G.Grothendieck 的答案是规范的,因为该答案使用cur_data()。这个答案是规范的,因为它使用c_across。挑选。我倾向于相信c_across 是针对这类事情的,但我相信也有反例。
  • @r2evans,仅供参考,speed comparison
  • @r2evans,我其实很喜欢你的回答,但我对速度比较感到惊讶。我还使用超过 100k 的表,并将 dplyr 留给 data.table
【解决方案3】:

baseR 答案

df$Count_NA <- apply(df, 1, function(x) sum(is.na(x)))                 

df
   Q1 Q2 Q3 Count_NA
1   1  2  3        0
2  NA  2  3        1
3  NA NA  3        2
4  NA NA NA        3
5  NA NA NA        3
6  NA NA NA        3
7  NA NA NA        3
8  NA NA NA        3
9  NA NA NA        3
10 NA NA NA        3

所以可以集成到 dplyr 管道中

df %>% mutate(count_NA = apply(., 1, function(x) sum(is.na(x))))

   Q1 Q2 Q3 count_NA
1   1  2  3        0
2  NA  2  3        1
3  NA NA  3        2
4  NA NA NA        3
5  NA NA NA        3
6  NA NA NA        3
7  NA NA NA        3
8  NA NA NA        3
9  NA NA NA        3
10 NA NA NA        3

【讨论】:

【解决方案4】:

rowSums 直接与mutate 一起工作,而没有rowwise

df %>% mutate(count_NA = rowSums(is.na(.)))

   Q1 Q2 Q3 count_NA
1   1  2  3        0
2  NA  2  3        1
3  NA NA  3        2
4  NA NA NA        3
5  NA NA NA        3
6  NA NA NA        3
7  NA NA NA        3
8  NA NA NA        3
9  NA NA NA        3
10 NA NA NA        3

请注意,您的初始解决方案是迄今为止最快的解决方案:

microbenchmark::microbenchmark(
  df$Count_NA <- rowSums(is.na(df)),
  df$Count_NA <- apply(df, 1, function(x) sum(is.na(x))),
  df %>% mutate(count_NA = rowSums(is.na(.))),
  df %>%
    mutate(Count_NA = purrr::pmap(., ~ sum(is.na(c(...))))),
  df %>%
    rowwise() %>%
    mutate(a=sum(is.na(c_across(everything())))),
  df %>%
  rowwise() %>%
  mutate(Count_NA = sum(is.na(cur_data()))) %>%
  ungroup
)

Unit: microseconds
                                                                            expr     min       lq
                                               df$Count_NA <- rowSums(is.na(df))    39.8    64.30
                          df$Count_NA <- apply(df, 1, function(x) sum(is.na(x)))  1661.6  1868.40
                                     df %>% mutate(count_NA = rowSums(is.na(.)))  1181.7  1572.80
                   df %>% mutate(Count_NA = purrr::pmap(., ~sum(is.na(c(...)))))  4749.9  5190.35
             df %>% rowwise() %>% mutate(a = sum(is.na(c_across(everything())))) 29124.1 31148.50
 df %>% rowwise() %>% mutate(Count_NA = sum(is.na(cur_data()))) %>%      ungroup 70473.0 73659.70
      mean   median       uq     max neval   cld
    79.033    76.25    88.75   174.0   100 a    
  2082.960  1966.50  2075.75  8777.3   100  b   
  1722.178  1676.20  1791.60  3112.9   100  b   
  5726.549  5396.40  5745.25 28592.1   100   c  
 33567.825 31983.05 33637.00 54676.9   100    d 
 77902.342 76492.85 81199.15 98942.1   100     e
Unit: microseconds
                                                                            expr     min       lq
                                               df$Count_NA <- rowSums(is.na(df))    38.2    44.95
                          df$Count_NA <- apply(df, 1, function(x) sum(is.na(x)))  1584.8  1765.30
                                     df %>% mutate(count_NA = rowSums(is.na(.)))  1247.9  1496.95
                   df %>% mutate(Count_NA = purrr::pmap(., ~sum(is.na(c(...)))))  4614.0  5110.50
 df %>% rowwise() %>% mutate(Count_NA = sum(is.na(cur_data()))) %>%      ungroup 67413.5 70865.45
      mean   median       uq      max neval cld
    71.159    65.85    84.40    162.2   100 a  
  1967.629  1894.45  2093.30   3436.6   100 ab 
  1814.193  1666.25  1895.35   9031.0   100 a  
  5796.483  5380.70  5665.10  15309.7   100  b 
 78309.807 75275.30 79776.40 286964.3   100   c

【讨论】:

  • 我觉得最让我惊讶的是cur_data() 的效率如此之低。
  • 我开始问这个问题是因为我的 rowSums 和 apply() 解决方案运行良好,并试图将我的解决方案从基础 R 移动到 dplyr。现在我看到了你的速度比较,我坚持使用 base R!也就是说,我从这次讨论中学到了很多东西。谢谢大家。
【解决方案5】:

如果您将来对具有 purrr 包功能的逐行解决方案感兴趣:

library(purrr)

df %>%
  mutate(Count_NA = pmap(., ~ sum(is.na(c(...)))))


   Q1 Q2 Q3 Count_NA
1   1  2  3        0
2  NA  2  3        1
3  NA NA  3        2
4  NA NA NA        3
5  NA NA NA        3
6  NA NA NA        3
7  NA NA NA        3
8  NA NA NA        3
9  NA NA NA        3
10 NA NA NA        3

【讨论】:

  • 您可以使用pmap 进行更复杂的多变量逐行操作:如果您有兴趣,可以查看:stackoverflow.com/questions/66935005/…
  • 仅供参考,speed comparison
  • @Waldi 哦,这是一个有趣的比较。但我通常更喜欢使用pmap 进行更复杂的逐行操作。不可否认rowSums 更简单,也是迄今为止最快的。
  • 我喜欢你的解决方案,我也是purrr 的忠实用户,但microbenchmark 总是有助于找到最有效的方法。
  • @Waldi 非常感谢。你说的对。由于我开始学习 R 还不到一年,所以我更加重视首先获得所需的输出,然后尝试替代解决方案。但是在这种情况下,我试图提出一个与已经发布的不同的解决方案。有趣的是,还有一个名为 bench 的包与 microbenchmark 的功能相同,我还不知道它与后者有何不同。
【解决方案6】:

使用dapply

library(collapse)
dapply(df, function(x) sum(is.na(x)), MARGIN = 1)
#[1] 0 1 2 3 3 3 3 3 3 3

【讨论】:

    【解决方案7】:

    这可能会迟到,但是,解决您的问题的另一种方法是,例如,您需要计算不同列集的 NA,如下所示:

    # You can specify different column sets
    cols_to_check1 <- c("Q1", "Q2", "Q3")
    cols_to_check2 <- c("Q1", "Q2")
    

    然后,将rowSums 函数与mutate() 一起应用

    df %>%
     mutate(count_NA1 = rowSums(across(cols_to_check1, ~ is.na(.))),
            count_NA2 = rowSums(across(cols_to_check2, ~ is.na(.))))
    

    解决方案如下:

       Q1 Q2 Q3 count_NA1  count_NA2
    1   1  2  3        0           0
    2  NA  2  3        1           1
    3  NA NA  3        2           2
    4  NA NA NA        3           2
    5  NA NA NA        3           2
    6  NA NA NA        3           2
    7  NA NA NA        3           2
    8  NA NA NA        3           2
    9  NA NA NA        3           2
    10 NA NA NA        3           2
    

    【讨论】:

      猜你喜欢
      • 2021-04-27
      • 2016-10-14
      • 2018-06-17
      • 1970-01-01
      • 2012-12-31
      • 2020-07-16
      • 1970-01-01
      相关资源
      最近更新 更多