Неожиданное поведение dplyr :: filter () с NA - PullRequest
1 голос
/ 01 августа 2020

У меня есть данные со следующими двумя столбцами:

  • location_country: код альфа-2 (например, «США»)
  • location_admin_level_1: административный уровень 1 ( для 'США' должно быть 2 символа)

За один шаг I filter() извлекает location_admin_level_1, где количество символов больше 2:

librayr(dplyr)

data %>%
    filter(location_country == "US",
           nchar(location_admin_level_1) > 2)

... который работает правильно.

Однако, когда я пытаюсь отфильтровать эти out (т.е. получить остальные данные), он возвращает меньшее количество строк, чем следовало бы:

# Does not behave as expected
data %>%
    filter(!(location_country == "US" & nchar(location_admin_level_1) > 2))

Это тоже не работает

# Does not behave as expected
data %>%
    filter(location_country != "US" | (location_country == "US" & nchar(location_admin_level_1) == 2))

Но когда я использую базовый R для фильтрации этих строк, он работает должным образом:

# Works
data[data$location_country != "US" | (data$location_country == "US" & nchar(data$location_admin_level_1) == 2), ]

# Also works
data[!(data$location_country == "US" & nchar(data$location_admin_level_1) > 2), ]

Почему базовое решение R работает должным образом, а решение dplyr - нет?

(Обратите внимание: я не могу создать MWE, потому что не знаю, как фильтрация работает, на чем и сосредоточен мой вопрос)

1 Ответ

1 голос
/ 01 августа 2020

Одна возможность - наличие NA элементов в этих строках. Base R вернет строку NA, потому что == with NA возвращает NA, а filter удаляет NA в логическом векторе по умолчанию

data[!(data$location_country == "US" & nchar(data$location_admin_level_1) > 2), ]

Теперь проверьте с помощью filter from dplyr

library(dplyr)
data %>%
    filter(!(location_country == "US" & nchar(location_admin_level_1) > 2))

Если мы хотим получить строки NA в filter, используйте is.na

data %>% 
   filter((!(location_country == "US" & !is.na(location_country) &
        nchar(location_admin_level_1) > 2 &
           !is.na(location_admin_level_1)))|
           is.na(location_country))

Проблема в том, что == возвращает NA, когда есть какой-либо NA

with(data, location_country == "US")
#[1]  TRUE  TRUE FALSE FALSE    NA

В base R NA в логическом векторе просто возвращает строку NA, потому что это не ИСТИНА или ЛОЖЬ, в то время как в filter это удаляется по умолчанию, оставляя только 2 строки на шаге filter (учитывая только последнее выражение). Чтобы сделать это ИСТИНА или ЛОЖЬ, просто добавьте is.na

with(data, location_country == "US" & !is.na(location_country))
#[1]  TRUE  TRUE FALSE FALSE FALSE

Это приведет к удалению NA строк. Но предположим, что если нам нужна строка NA, тогда последний элемент должен иметь значение ИСТИНА. Для этого нам потребуется |

with(data, location_country == "US"|is.na(location_country))
#[1]  TRUE  TRUE FALSE FALSE  TRUE

данные

data <- data.frame(location_country = c('US', 'US', 'China', 'Canada', NA), location_admin_level_1 = c('hello', 'l', 'w', '321', '2443'))
...