Неожиданное поведение с str_replace NA

Я пытаюсь преобразовать строку символов в числовую и столкнулся с неожиданным поведением с str_replace. Вот минимальный рабочий пример:

library(stringr)
x <- c("0", "NULL", "0")

# This works, i.e. 0 NA 0
as.numeric(str_replace(x, "NULL", ""))

# This doesn't, i.e. NA NA NA
as.numeric(str_replace(x, "NULL", NA))

На мой взгляд, второй пример должен работать, поскольку он должен заменить вторую запись в векторе только на NA (что является допустимым значением в векторе символов). Но это не так: внутренний str_replace преобразует все три записи в NA.

Что тут происходит? Я просмотрел документацию для str_replace и stri_replace_all, но не нашел очевидного объяснения.

EDIT: Чтобы уточнить, это с stringr_1.0.0 и stringi_1.0-1 на R 3.1.3, Windows 7.


person jkeirstead    schedule 17.12.2015    source источник
comment
Конечно, неожиданное поведение в исходном коде, которое нуждается в исправлении, вам нужно предоставить NA строку, чтобы она работала: as.numeric(str_replace(x, "NULL", "NA"))   -  person Colonel Beauvel    schedule 17.12.2015
comment
Возможный обходной путь? x <- c("0", "NULL", "0"); y <- x; y[y=="NULL"] <- NA; as.numeric(y)   -  person bubble    schedule 17.12.2015
comment
Я что-то упускаю, у меня работает второй пример as.numeric(str_replace(x, "NULL", NA)) [1] 0 NA 0   -  person Pierre L    schedule 17.12.2015
comment
@PierreLafortune stringr используется для переноса base функций; теперь он оборачивает stringi функции. У вас, наверное, старая версия stringr. gsub ведет себя здесь корректно.   -  person nicola    schedule 17.12.2015
comment
как @PierreLafortune, я получаю правильный/ожидаемый результат (с обеими строками в моем случае)   -  person Cath    schedule 17.12.2015
comment
@nicola CathG имеет последнюю версию и получает правильный вывод, поэтому не уверен, как OP получает это   -  person Pierre L    schedule 17.12.2015
comment
У меня было загружено несколько пакетов, поэтому я повторил попытку в новом сеансе только с stringr (stringr_1.0.0), и он все еще работает...   -  person Cath    schedule 17.12.2015
comment
@CathG Я наблюдаю такое же поведение в операционной системе и со строкой 1.0.0 в чистом сеансе. Различия в платформах...? Я на ОСХ.   -  person joran    schedule 17.12.2015
comment
Странный. У меня есть stringr 1.0.0 и stringi 1.0-1 (похоже, это последние версии), и я могу воспроизвести результаты OP. Я на Убунте. зависит от ОС?   -  person nicola    schedule 17.12.2015
comment
Также иметь stringr 1.0.0 и stringi 1.0-1 и получить то же самое, что и OP, в чистом сеансе (на OSX)   -  person Jaap    schedule 17.12.2015
comment
одинаковые версии stringr и stringi, R 3.2.1 в Windows 7   -  person Cath    schedule 17.12.2015
comment
Я также получаю результат OP с stringr 1.0.0, stringi 1.0-1 и R 3.2.3 в Windows 7. Сейчас я пытаюсь отследить источник.   -  person TARehman    schedule 17.12.2015
comment
так больше похоже на версию R?   -  person Cath    schedule 17.12.2015
comment
Странный. Просматривая исходный код, сначала NA преобразуется в NA_character_, а затем оказывается здесь: x <- c("123", "NULL", "456"); stringi:::stri_replace_first_regex(x, "NULL", NA_character_). (Я изменил числа, чтобы устранить возможные проблемы с 0). После этого он спускается в код C...   -  person user2802241    schedule 17.12.2015
comment
Наконец-то мне удалось воспроизвести ошибку, обновив оба пакета. Возможно, подать запрос функции.   -  person Pierre L    schedule 17.12.2015
comment
Я рекомендую запрос функции для stringi для этого :)   -  person gagolews    schedule 19.12.2015
comment
Не беспокойтесь, я уже зарегистрировал проблему github.com/Rexamine/stringi/issues/210< /а>   -  person gagolews    schedule 20.12.2015


Ответы (4)


Посмотрите на исходный код str_replace.

function (string, pattern, replacement) 
{
    replacement <- fix_replacement(replacement)
    switch(type(pattern), empty = , bound = stop("Not implemented", 
        call. = FALSE), fixed = stri_replace_first_fixed(string, 
        pattern, replacement, opts_fixed = attr(pattern, "options")), 
        coll = stri_replace_first_coll(string, pattern, replacement, 
            opts_collator = attr(pattern, "options")), regex = stri_replace_first_regex(string, 
            pattern, replacement, opts_regex = attr(pattern, 
                "options")), )
}
<environment: namespace:stringr>

Это приводит к поиску fix_replacement, который находится на Github, и я тоже поставил ниже. Если вы запустите его в своей основной среде, вы обнаружите, что fix_replacement(NA) возвращает NA. Вы можете видеть, что он зависит от stri_replace_all_regex из пакета stringi.

fix_replacement <- function(x) {
    stri_replace_all_regex(
        stri_replace_all_fixed(x, "$", "\\$"),
        "(?<!\\\\)\\\\(\\d)",
        "\\$$1")
}

Интересно то, что stri_replace_first_fixed и stri_replace_first_regex возвращают c(NA,NA,NA) при запуске с вашими параметрами (ваши string, pattern и replacement). Проблема в том, что stri_replace_first_fixed и stri_replace_first_regex — это код C++, поэтому понять, что происходит, становится немного сложнее.

stri_replace_first_fixed можно найти здесь.

stri_replace_first_regex можно найти здесь.

Насколько я могу понять с ограниченным временем и моим относительно ржавым знанием C++, функция stri__replace_allfirstlast_fixed проверяет аргумент replacement, используя stri_prepare_arg_string. Согласно документации для этого, он выдаст ошибку, если обнаружит NA . У меня нет времени, чтобы полностью отследить это, но я подозреваю, что эта ошибка может быть причиной странного возврата всех NA.

person TARehman    schedule 17.12.2015

Это была ошибка в пакете stringi, но теперь она исправлена (напомним, что stringr основан на на stringi - первое тоже пострадает).

С самой последней версией разработки мы получаем:

stri_replace_all_fixed(c("1", "NULL"), "NULL", NA)
## [1] "1" NA
person gagolews    schedule 30.01.2016
comment
Я все еще получаю проблему, используя stringr 1_2_0, который вызывает stringi_1.1.5? Хотя проблема была закрыта на github, github.com/tidyverse/stringr/issues/110 Есть идеи, что происходит? Спасибо! - person Matifou; 08.06.2017

Есть еще один способ решить эту проблему, как показано здесь, используя NA_character_

Краткий ответ на вопрос:

library(stringr)
x <- c("0", "NULL", "0")
y <- as.numeric(str_replace(x, "NULL", NA_character_))

Производит:

> y
[1]  0 NA  0
> typeof(y)
[1] "double"

Идти дальше

library(dplyr)
library(stringr)
# create a dummy dataset
ex = starwars %>% select(name, hair_color, homeworld) %>% head(6)
print(ex)
# lets say you want to replace all "Tatooine" by NA
# this produce the expected output
ex %>% mutate(homeworld = str_replace_all(homeworld, pattern = "Tatooine", NA_character_))

# HOWEVER,
# From Hadley's comment: "str_replace() has to replace parts of a string and replacing part of a string with NA doesn't make sense."
# then be careful using this method, see the example below:
ex %>% mutate(hair_color = str_replace_all(hair_color, pattern = "brown", NA_character_))
# all air colors with "brown", including "blond, grey" (Owen Lars, line 6) are now NA

Выходы

> print(ex)
# A tibble: 10 x 3
   name               hair_color    homeworld
   <chr>              <chr>         <chr>    
 1 Luke Skywalker     blond         Tatooine 
 2 C-3PO              NA            Tatooine 
 3 R2-D2              NA            Naboo    
 4 Darth Vader        none          Tatooine 
 5 Leia Organa        brown         Alderaan 
 6 Owen Lars          brown, grey   Tatooine  

> ex %>% mutate(homeworld = str_replace_all(homeworld, pattern = "Tatooine", NA_character_))
# A tibble: 10 x 3
   name               hair_color    homeworld
   <chr>              <chr>         <chr>    
 1 Luke Skywalker     blond         NA       
 2 C-3PO              NA            NA       
 3 R2-D2              NA            Naboo    
 4 Darth Vader        none          NA       
 5 Leia Organa        brown         Alderaan 
 6 Owen Lars          brown, grey   NA         

 > ex %>% mutate(hair_color = str_replace_all(hair_color, pattern = "brown", NA_character_))
# A tibble: 10 x 3
   name               hair_color    homeworld
   <chr>              <chr>         <chr>    
 1 Luke Skywalker     blond         Tatooine 
 2 C-3PO              NA            Tatooine 
 3 R2-D2              NA            Naboo    
 4 Darth Vader        none          Tatooine 
 5 Leia Organa        NA            Alderaan 
 6 Owen Lars          NA            Tatooine 
person Paul    schedule 14.08.2020

Вот решение, использующее метод across dplyr и пакет stringr.

df <- data.frame(x=c("a","b","null","e"),
                 y=c("g","null","h","k"))  

df2 <- df %>% 
  mutate(across(everything(),str_replace,"null",NA_character_))
person Zeus    schedule 15.07.2021