str_replace "NA" 的意外行为
Unexpected behaviour with 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
的文档,但没有看到明显的解释。
编辑: 澄清一下,这是 R 3.1.3 上的 stringr_1.0.0
和 stringi_1.0-1
,Windows 7.
查看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)
returns NA
。你可以看到它依赖于 stri_replace_all_regex
,它来自 stringi
包。
fix_replacement <- function(x) {
stri_replace_all_regex(
stri_replace_all_fixed(x, "$", "\$"),
"(?<!\\)\\(\d)",
"\$")
}
有趣的是 stri_replace_first_fixed
和 stri_replace_first_regex
都 return c(NA,NA,NA)
当 运行 使用您的参数(您的 string
, pattern
,和 replacement
)。问题是 stri_replace_first_fixed
和 stri_replace_first_regex
是 C++ 代码,所以弄清楚发生了什么有点棘手。
stri_replace_first_fixed
可以找到here.
stri_replace_first_regex
可以找到here.
就我在有限的时间和我相对生疏的 C++ 知识的认识来看,函数 stri__replace_allfirstlast_fixed
使用 stri_prepare_arg_string
检查 replacement
参数。根据 documentation for that,如果遇到 NA,它会抛出错误。我没有时间对此进行全面追踪,但我怀疑此错误可能导致所有 NA 出现奇数 return。
这是 stringi
包中的一个错误,但现在是 fixed(回想一下 stringr
是基于 stringi
- 前者也会受到影响) .
我们得到最新的开发版本:
stri_replace_all_fixed(c("1", "NULL"), "NULL", NA)
## [1] "1" NA
还有另一种方法可以解决此问题,如 here 所示,使用 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
这是一个使用 dplyr 的 across
方法和 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_))
我正在尝试将字符串转换为数字,但在使用 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
的文档,但没有看到明显的解释。
编辑: 澄清一下,这是 R 3.1.3 上的 stringr_1.0.0
和 stringi_1.0-1
,Windows 7.
查看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)
returns NA
。你可以看到它依赖于 stri_replace_all_regex
,它来自 stringi
包。
fix_replacement <- function(x) {
stri_replace_all_regex(
stri_replace_all_fixed(x, "$", "\$"),
"(?<!\\)\\(\d)",
"\$")
}
有趣的是 stri_replace_first_fixed
和 stri_replace_first_regex
都 return c(NA,NA,NA)
当 运行 使用您的参数(您的 string
, pattern
,和 replacement
)。问题是 stri_replace_first_fixed
和 stri_replace_first_regex
是 C++ 代码,所以弄清楚发生了什么有点棘手。
stri_replace_first_fixed
可以找到here.
stri_replace_first_regex
可以找到here.
就我在有限的时间和我相对生疏的 C++ 知识的认识来看,函数 stri__replace_allfirstlast_fixed
使用 stri_prepare_arg_string
检查 replacement
参数。根据 documentation for that,如果遇到 NA,它会抛出错误。我没有时间对此进行全面追踪,但我怀疑此错误可能导致所有 NA 出现奇数 return。
这是 stringi
包中的一个错误,但现在是 fixed(回想一下 stringr
是基于 stringi
- 前者也会受到影响) .
我们得到最新的开发版本:
stri_replace_all_fixed(c("1", "NULL"), "NULL", NA)
## [1] "1" NA
还有另一种方法可以解决此问题,如 here 所示,使用 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
这是一个使用 dplyr 的 across
方法和 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_))