Как мне написать функцию, которая использует dplyr, чтобы проверить, что переменная не имеет значений NA? - PullRequest
0 голосов
/ 17 декабря 2018

У меня есть простой код, который создает произвольные данные примера:

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 я думал, что будет работать следующее:

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, включая этот вопрос , но в некоторых ответах упоминаются устаревшие части dplyr, которые не сильно помогают (в данном случае, вызов vignette("nse") показывает, что виньетка больше не существует).

Что мне здесь не хватает?


Я использую R v3.5.1, dplyr v0.7.7 и assertr v2.5 в системе x64 Linux

Ответы [ 3 ]

0 голосов
/ 17 декабря 2018

Если вам не нужно использовать пакет 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
0 голосов
/ 19 декабря 2018

Вот как вы можете сделать нечто подобное в базе R:

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

verify_not_missing(df, value)
[1] FALSE
0 голосов
/ 17 декабря 2018

Для этого есть три возможных способа:

Первый подход

Использование 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 

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...