我有一个一百万个名字的列表,我想在具有15万行的列的每个单元格中查找它们。我正在使用Grep逐一查找名称,如果在任何单元格中找到,请将单元格留空。我正在运行此循环1百万次,但是会花费很多时间。 我如何加快循环速度?
install.packages("babynames")
install.packages("randomNames")
names = babynames::babynames ###creating a random dataset for this example
temp_new2= data.frame(names$name) ##temp_new2 is a single column name dataframe
random_names<-strsplit((randomNames(n=1000,
which.names="first",
name.sep=" ",
sample.with.replacement=TRUE,
return.complete.data=FALSE
)
),"\n")
count = 0
t=0
list_of_names = list()
for (i in random_names)
{
if (length(grep(paste0("\\b",i,"\\b"),temp_new2$cleaned_names,ignore.case = TRUE)) != 0)
{
p = length(grep(paste0("\\b",i,"\\b"),temp_new2$cleaned_names,ignore.case = TRUE))
print(i)
list_of_names = append(list_of_names,i)
}
else
{t=0
p=0
}
count = count + p
temp_new2[grep(paste0("\\b",i,"\\b"),temp_new2$cleaned_names,ignore.case = TRUE),]<- ""
}
运行1000个名称的循环大约需要4分钟,因此运行100万个名称的循环大约需要4000分钟
答案 0 :(得分:1)
我玩了一些,并用微基准测试获得了以下结果:
microbenchmark::microbenchmark(your_fun(), fun_initialize_list(), fun_list_one_grep(), fun_lapply())
Unit: milliseconds
expr min lq mean median uq max neval
your_fun() 51.02420 52.61047 55.19147 54.20093 55.98069 77.55637 100
fun_initialize_list() 50.86644 52.81099 55.52799 54.23134 56.37564 102.21945 100
fun_list_one_grep() 25.68943 26.31398 28.51748 27.73832 28.46759 56.01566 100
fun_lapply() 25.22339 26.02261 27.83738 27.26183 27.90310 43.80443 100
这些函数在下面定义,它们只是不同过程的包装。正如@RuiBarradas指出的那样,grep
调用将执行3次。
减少这种情况,就我而言,将执行时间减少了50%。
您的方法
your_fun <- function() {
count <- 0
t <- 0
list_of_names <- list()
for (i in random_names) {
if (length(grep(paste0("\\b",i,"\\b"), temp_new2$cleaned_names,ignore.case = TRUE)) != 0) {
p <- length(grep(paste0("\\b",i,"\\b"), temp_new2$cleaned_names,ignore.case = TRUE))
list_of_names <- append(list_of_names,i)
} else {
t <- 0
p <- 0
}
count <- count + p
temp_new2[grep(paste0("\\b",i,"\\b"),temp_new2$cleaned_names,ignore.case = TRUE),] <- ""
}
}
在for循环之前初始化列表
没错,这并没有极大地提高速度,可能是因为grep
花费了很多时间。
fun_initialize_list <- function() {
count <- 0
t <- 0
list_of_names <- logical(length(random_names))
k <- 0
for (i in random_names) {
k <- k + 1
if (length(grep(paste0("\\b",i,"\\b"), temp_new2$cleaned_names,ignore.case = TRUE)) != 0) {
p <- length(grep(paste0("\\b",i,"\\b"), temp_new2$cleaned_names,ignore.case = TRUE))
list_of_names[k] <- TRUE
} else {
t <- 0
p <- 0
list_of_names[k] <- FALSE
}
count <- count + p
temp_new2[grep(paste0("\\b",i,"\\b"),temp_new2$cleaned_names,ignore.case = TRUE),] <- ""
}
list_of_names <- random_names[list_of_names]
}
仅使用一次呼叫grep
fun_list_one_grep <- function() {
count <- 0
t <- 0
list_of_names <- logical(length(random_names))
k <- 0
for (i in random_names) {
k <- k + 1
name_match <- grep(paste0("\\b",i,"\\b"), temp_new2$cleaned_names, ignore.case = TRUE)
len_match <- length(name_match)
if (len_match != 0) {
p <- len_match
list_of_names[k] <- TRUE
} else {
t <- 0
p <- 0
list_of_names[k] <- FALSE
}
count <- count + p
temp_new2[name_match, ] <- ""
}
list_of_names <- random_names[list_of_names]
}
强加于人的方法
fun_lapply <- function() {
random_matches <- lapply(random_names, function(i) {
grep(paste0("\\b",i,"\\b"), temp_new2$cleaned_names, ignore.case = TRUE)
})
temp_new2[unlist(random_matches), ] <- ""
count <- length(unique(unlist(random_matches)))
list_of_names <- random_names[!sapply(random_matches, is.null)]
}
数据
names = babynames::babynames ###creating a random dataset for this example
temp_new2 = data.frame(cleaned_names = names$name[1:1000],
stringsAsFactors = FALSE) ##temp_new2 is a single column name dataframe
set.seed(23)
random_names <- strsplit((
randomNames::randomNames(
n = 100,
which.names = "first",
name.sep = " ",
sample.with.replacement = TRUE,
return.complete.data = FALSE
)), "\n")