R 使用正则表达式查找 mutate 中的值
R using regular expression to look up values in mutate
我有一个参考 table,它有 2 列,例如:
pattern
name
a.*b
name 1
c\d{2}
name 2
假设我有一个包含列 col 的数据框,如下所示:
col
adb
c12
add
我想使用模式并基于模式创建另一个基于列的列。
使用上面的例子,新列的值应该分别是 c("name 1", "name 2", NA)。我尝试编写一个带有字符串检测的循环,如下所示:
regex_map <- function(in_string){
ref_table <- read_excel("./data/meta_data.xlsx", "mapping_ex") %>% filter(!is.na(pattern))
for(i in 1:nrow(ref_table)){
r <- ref_table[i,]
#print(str(r))
if(str_detect(tolower(in_string), r$pattern)){
return(r$name)
}
}
return("N/A")
}
函数工作正常,但是,如果我将函数作为 mutate 的一部分,它会非常慢,这可能与预期的一样。我想知道如何在 R 中有效地执行此操作?感谢您的帮助!!
该函数非常慢,因为您每次调用它时都在读取 ref_table
。仅在 mutate
之外读取文件一次,并将其作为第二个函数 regex_map
参数传递。
您可以通过在循环外仅使用一次 in_string
全部小写来进一步加快循环。
我正在使用 base::grep
,而不是 stringr::str_detect
。
y <- '
col
adb
c12
add'
df1 <- read.table(textConnection(y), header = TRUE)
suppressPackageStartupMessages({
library(dplyr)
library(readxl)
})
regex_map <- function(in_string, ref_table){
res <- rep("N/A", length(in_string))
in_string <- tolower(in_string)
for(i in seq_len(nrow(ref_table))){
r <- ref_table[i, , drop = FALSE]
found <- grep(r$pattern, in_string)
if(length(found)){
res[found] <- r$name
}
}
res
}
ref_table_file <- file.path("~", "Temp", "meta_data.xlsx")
ref_table <- read_excel(ref_table_file, "mapping_ex") %>% filter(!is.na(pattern))
df1 %>%
mutate(clean = regex_map(col, ref_table))
#> col clean
#> 1 adb name 1
#> 2 c12 name 2
#> 3 add N/A
由 reprex package (v2.0.1)
创建于 2022-05-02
另一种可能的解决方案,基于tidyverse
:
library(tidyverse)
df1 <- data.frame(
pattern = c("a.*b", "c\d{2}"),
name = c("name1", "name2")
)
df2 <- data.frame(
col = c("adb", "c12", "add")
)
df2 %>%
rowid_to_column() %>%
full_join(df1 %>% rowid_to_column()) %>%
mutate(name = if_else(str_detect(col, pattern), name, NA_character_)) %>%
select(col, name)
#> Joining, by = "rowid"
#> col name
#> 1 adb name1
#> 2 c12 name2
#> 3 add <NA>
我有一个参考 table,它有 2 列,例如:
pattern | name |
---|---|
a.*b | name 1 |
c\d{2} | name 2 |
假设我有一个包含列 col 的数据框,如下所示:
col |
---|
adb |
c12 |
add |
我想使用模式并基于模式创建另一个基于列的列。
使用上面的例子,新列的值应该分别是 c("name 1", "name 2", NA)。我尝试编写一个带有字符串检测的循环,如下所示:
regex_map <- function(in_string){
ref_table <- read_excel("./data/meta_data.xlsx", "mapping_ex") %>% filter(!is.na(pattern))
for(i in 1:nrow(ref_table)){
r <- ref_table[i,]
#print(str(r))
if(str_detect(tolower(in_string), r$pattern)){
return(r$name)
}
}
return("N/A")
}
函数工作正常,但是,如果我将函数作为 mutate 的一部分,它会非常慢,这可能与预期的一样。我想知道如何在 R 中有效地执行此操作?感谢您的帮助!!
该函数非常慢,因为您每次调用它时都在读取 ref_table
。仅在 mutate
之外读取文件一次,并将其作为第二个函数 regex_map
参数传递。
您可以通过在循环外仅使用一次 in_string
全部小写来进一步加快循环。
我正在使用 base::grep
,而不是 stringr::str_detect
。
y <- '
col
adb
c12
add'
df1 <- read.table(textConnection(y), header = TRUE)
suppressPackageStartupMessages({
library(dplyr)
library(readxl)
})
regex_map <- function(in_string, ref_table){
res <- rep("N/A", length(in_string))
in_string <- tolower(in_string)
for(i in seq_len(nrow(ref_table))){
r <- ref_table[i, , drop = FALSE]
found <- grep(r$pattern, in_string)
if(length(found)){
res[found] <- r$name
}
}
res
}
ref_table_file <- file.path("~", "Temp", "meta_data.xlsx")
ref_table <- read_excel(ref_table_file, "mapping_ex") %>% filter(!is.na(pattern))
df1 %>%
mutate(clean = regex_map(col, ref_table))
#> col clean
#> 1 adb name 1
#> 2 c12 name 2
#> 3 add N/A
由 reprex package (v2.0.1)
创建于 2022-05-02另一种可能的解决方案,基于tidyverse
:
library(tidyverse)
df1 <- data.frame(
pattern = c("a.*b", "c\d{2}"),
name = c("name1", "name2")
)
df2 <- data.frame(
col = c("adb", "c12", "add")
)
df2 %>%
rowid_to_column() %>%
full_join(df1 %>% rowid_to_column()) %>%
mutate(name = if_else(str_detect(col, pattern), name, NA_character_)) %>%
select(col, name)
#> Joining, by = "rowid"
#> col name
#> 1 adb name1
#> 2 c12 name2
#> 3 add <NA>