【发布时间】:2022-01-09 18:59:43
【问题描述】:
我写了一个计算句子中单词(unigrams)个数的函数:
library(ngram)
library(stringi)
library(tidyverse)
set.seed(123)
get_unigrams <- function(text) {
sapply(text, function(text){
unigram<- ngram(text, n = 1) %>% get.ngrams() %>% length()
return(unigram)
}
)
}
为此,我使用了sapply-函数,它将我的get_unigrams-函数应用于数据集中的每一行。
到目前为止,这也有效:
##example dataset:
df<-sample.int(5, 5, replace = T) %>%
map(., ~ stri_rand_strings(.x, 10) %>% paste(collapse = " ")) %>%
unlist() %>%
tibble(text = .)
##applying my function
df %>% mutate(n=get_unigrams((text)))
# A tibble: 5 x 2
text n
<chr> <int>
1 SxSgZ6tF2K xtgdzehXaH 9xtgn1TlDJ 3
2 E8PPM98ESG r2Rn7YC7kt Nf5NHoRoon 3
3 Rkdi0TDNbL 6FfPm6Qzts 2
4 A8eLeJBm5S VbKUxTtubP 2
5 9vI3wi8Yxa PeJJDMz958 gctfjWeomy 3
但是,由于get_unigrams-函数适用于每一行,因此非常耗时。
因此,我想问一下sapply-function 是否有一个快速的替代方案,可以显着加快我的get_unigrams-function。
##dataset with 50.000 rows:
df<-sample.int(50, 50000, replace = T) %>%
map(., ~ stri_rand_strings(.x, 10) %>% paste(collapse = " ")) %>%
unlist() %>%
tibble(text = .)
system.time({
df %>% mutate(n=get_unigrams((text)))
})
# User System verstrichen
# 21.35 0.11 22.06
对于包含 50,000 行的数据集,我的函数需要 22.06 秒(“verstrichen”)。这对我来说显然太多了!
有人可以帮我提高速度吗?也许用矢量化函数?
get_unigrams-function 中的构造必须保持不变:
unigram <- ngram(text, n = 1) %>% get.ngrams() %>% length()
return(unigram)
我只指sapply-函数。
非常感谢!
【问题讨论】:
-
我可能会误解您的上下文,但因为您似乎很乐意加载
stringi并想“计算字数......也许使用矢量化函数”,也许您可以澄清原因你不能使用stri_count_words(你的大样本在我的旧电脑上需要 0.14 秒)。干杯 -
是的,很遗憾我忘记了。我现在添加了
stringi-package。我需要保留这一部分,因为这只是一小部分。在我的实际函数中,代码要多得多。 -
尝试矢量化:
get_unigrams <- Vectorize(get_unigrams)。或者尝试在多个 CPU 内核上同时运行该函数,例如使用mcapply -
Vectorize不起作用:“错误:mutate() column n. in = get_unigrams((text)) 出现问题。x 无法将类型“闭包”强制转换为“any”类型的向量” -
Vectorize永远不会让您的代码本身更快,除非您自己的矢量化实现本质上是低效的。在这里,您的问题不是sapply,而是底层代码块。您可以使用 for-loop 或 lapply 获得相同的速度(两者都稍微快一点,因为 sapply 如果可能的话将输出简化为向量)。我不明白你对 Hernik 的论点,你为什么不能直接使用stri_count_words?如果您的代码比这个小段“大得多”,但这是一个瓶颈(使用分析器发现)使用stri_count_words替换这个小段,这似乎更有效。