如何编写使用dplyr来检查变量没有NA值的函数?

时间:2018-12-17 02:56:04

标签: r dplyr

我有简单的代码可以创建任意示例数据:

library(assertr)
library(tidyverse)
set.seed(1)
df <- tibble(id = 1:10, value = rnorm(10, 0, 1)) %>%
  mutate(value = if_else(abs(value) < 0.5, NA_real_, value))

数据如下:

> df
# A tibble: 10 x 2
      id   value
   <int>   <dbl>
 1     1  -0.626
 2     2  NA    
 3     3  -0.836
 4     4   1.60 
 5     5  NA    
 6     6  -0.820
 7     7  NA    
 8     8   0.738
 9     9   0.576
10    10  NA   

现在,我正在尝试编写一个函数来检查给定列(在这种情况下,value列)中是否有任何行具有NA值,如果存在则抛出错误。如果没有,它将返回未经修改的原始数据,以便管道可以继续。这很简单,没有功能:

df %>% verify(sum(is.na(value)) == 0)

# Outputs "Error: assertr stopped execution"

将其包装在函数中会带来困难。我尝试使用lazyeval

verify_not_missing <- function(.data, v) {
  .data %>% verify(sum(is.na(lazyeval::lazy(v))) == 0)
}
df %>% verify_not_missing(value)

但这不会引发任何错误或停止执行。它默默地继续执行。同样,从dplyr programming vignette,我认为以下方法会起作用:

verify_not_missing <- function(.data, v) {
  .data %>% verify(sum(is.na(!! quo(v))) == 0)
}
df %>% verify_not_missing(value)

但这会引发错误:

Error in is_quosure(e2) : argument "e2" is missing, with no default 

我搜索了一些文档和SO,包括this question,但是一些答案提到了dplyr弃用的部分,并没有太大帮助(例如,调用{{1} }显示该小插图已不存在。

我在这里想念什么?


我正在x64 Linux系统上使用R v3.5.1,dplyr v0.7.7和asserter v2.5

3 个答案:

答案 0 :(得分:1)

有三种方法可以实现这一目标:

第一种方法

eval()substitute()一起使用,如下所示:

verify_not_missing <- function(.data, v) {
  v <- eval(substitute(v), .data)
  .data %>% 
    verify(sum(is.na(v)) == 0)
}

第二种方法

rlang::eval_tidy()enquo()一起使用,如下所示:

verify_not_missing <- function(.data, v) {
  v <- rlang::eval_tidy(enquo(v), .data)
  .data %>% 
    verify(sum(is.na(v)) == 0)
}

第三种方法

!!enquo()内使用select()(您需要colnames(.data)才能获得其他列)

verify_not_missing <- function(.data, v) {
  .data %>% 
    select(colnames(.data), v = !!enquo(v)) %>%
    verify(sum(is.na(v)) == 0)
}

df %>% verify_not_missing(value)

所有这些都产生相同的结果,使用您的数据,结果如下所示:

#verification [sum(is.na(v)) == 0] failed! (1 failure)

#    verb  redux_fn           predicate  column  index  value
#1 verify        NA  sum(is.na(v)) == 0      NA      1     NA

#Error: assertr stopped execution 

希望有帮助。

答案 1 :(得分:0)

如果您不必使用assertr软件包,我认为可以考虑使用此解决方案。

library(tidyverse)

verify_not_missing <- function(.data) {
  col_na <- colSums(is.na(.data)) > 0 # larger than zero, than na value in that column
  if (any(col_na)) stop(gettextf("column %s is missing", 
                                 str_c(names(col_na)[col_na], collapse = ", ")))
}

通过使用colSums(is.na(.)),您可以检测具有NA值的列。如果有这样的列,则使用其列名打印错误消息可能很容易。

此外,对于多列情况,我折叠了names()

应用于您的数据集,我们可以获得结果:

df %>% 
  verify_not_missing()
#> Error in verify_not_missing(.): column value is missing

类似地,对于具有NA值的其他列,

(mydf2 <- tibble(id = 1:10, value = rnorm(10, 0, 1)) %>%
  mutate(value1 = if_else(abs(value) < 0.5, NA_real_, value),
         value2 = if_else(abs(value) < 0.5, NA_real_, value)))
#> # A tibble: 10 x 4
#>       id   value  value1  value2
#>    <int>   <dbl>   <dbl>   <dbl>
#>  1     1  1.51     1.51    1.51 
#>  2     2  0.390   NA      NA    
#>  3     3 -0.621   -0.621  -0.621
#>  4     4 -2.21    -2.21   -2.21 
#>  5     5  1.12     1.12    1.12 
#>  6     6 -0.0449  NA      NA    
#>  7     7 -0.0162  NA      NA    
#>  8     8  0.944    0.944   0.944
#>  9     9  0.821    0.821   0.821
#> 10    10  0.594    0.594   0.594

mydf2 %>% 
  verify_not_missing()
#> Error in verify_not_missing(.): column value1, value2 is missing

它会打印value1, value2,其中包括NA


编辑-添加列参数

您可以先enquo(v),然后再使用%>% select(!!v)。然后,它返回v的列。其余部分相同。

verify_not_missing2 <- function(.data, v) {
  v <- enquo(v)
  col_na <-
    .data %>% 
    select(!!v) %>% # this returns v columns
    is.na() %>%
    colSums()
  col_na <- col_na > 0
  if (any(col_na)) stop(gettextf("column %s is missing", 
                                 str_c(names(col_na)[col_na], collapse = ", ")))
}

将此应用于示例

df %>% 
  verify_not_missing2(value)
#> Error in verify_not_missing2(., value): column value is missing

value指定为参数,可能会出错。另外,对于多个NA列,

mydf2 %>% 
  verify_not_missing2(value)
#---------------------------
mydf2 %>% 
  verify_not_missing2(value1)
#> Error in verify_not_missing2(., value1): column value1 is missing

当您输入既不是value1也不是value2的列时,则不会打印任何内容。另一方面,指定value1会出错。

此外,您可以使用c()指定多个列。

mydf2 %>% 
  verify_not_missing2(v = c("value1", "value2"))
#> Error in verify_not_missing2(., v = c("value1", "value2")): column value1, value2 is missing
#----------------------------
mydf2 %>% 
  verify_not_missing2(v = c(value1, value2))
#> Error in verify_not_missing2(., v = c(value1, value2)): column value1, value2 is missing

Edit2-还原原始数据

verify_not_missing3 <- function(.data, v) {
  v <- enquo(v)
  col_na <-
    .data %>% 
    select(!!v) %>% 
    is.na() %>% 
    colSums()
  col_na <- col_na > 0
  if (any(col_na)) {
    stop(gettextf("column %s is missing", 
                                 str_c(names(col_na)[col_na], collapse = ", ")))
  } else {
    .data
  }
}

在没有错误的情况下,其他else { .data }语句可以返回。

如果您提供value

mydf2 %>% 
  verify_not_missing3(value)
#> # A tibble: 10 x 4
#>       id   value  value1  value2
#>    <int>   <dbl>   <dbl>   <dbl>
#>  1     1  1.51     1.51    1.51 
#>  2     2  0.390   NA      NA    
#>  3     3 -0.621   -0.621  -0.621
#>  4     4 -2.21    -2.21   -2.21 
#>  5     5  1.12     1.12    1.12 
#>  6     6 -0.0449  NA      NA    
#>  7     7 -0.0162  NA      NA    
#>  8     8  0.944    0.944   0.944
#>  9     9  0.821    0.821   0.821
#> 10    10  0.594    0.594   0.594

另一方面,

mydf2 %>% 
  verify_not_missing3(value1)
#> Error in verify_not_missing3(., value1): column value1 is missing

答案 2 :(得分:0)

这是在基数R中如何执行类似的操作:

verify_not_missing <- function(.data, v) {
  !any(
    is.na(
      .data[[deparse(substitute(v))]]
    )
  )
} 

verify_not_missing(df, value)
[1] FALSE