#r #purrr #stringr
#r #purrr #stringr
Вопрос:
Я пытаюсь подсчитать количество не буквенно-цифровых символов для каждой строки в векторе из 100 000 строк. Я нахожу, что моя текущая реализация медленнее, чем хотелось бы.
Моя текущая реализация использует purrr::map()
для сопоставления пользовательскую функцию, которая использует stringr
пакет для каждой строки в векторе.
library(dplyr)
library(stringr)
library(purrr)
# custom function that accepts string input and counts the number
# of non-alphanum characters
count_non_alnum <- function(x) {
stringr::str_detect(x, "[^[:alnum:] ]") %>% sum()
}
# character vector of length 100K
vec <- rep("Hello. World.", 100000)
# tokenize individual characters for each string
vec_tokens <- purrr::map(vec, function(x) {
stringr::str_split(x, "") %>% unlist()
})
# count non-alphanum characters
purrr::map(vec_tokens, count_non_alnum)
# Time difference of 1.048214 mins
sessionInfo()
# R version 3.4.3 (2017-11-30)
# Platform: x86_64-w64-mingw32/x64 (64-bit)
# Running under: Windows 7 x64 (build 7601) Service Pack 1
Для завершения моих симуляций постоянно требуется около 1 минуты. У меня нет особых оснований для ожидания, но я надеюсь, что есть более быстрая альтернатива. Я открыт для альтернативных пакетов R или интерфейсов (например, reticulate, Rcpp).
Комментарии:
1. Намного быстрее:
count_non_alnum2 <- function(x) {sum(grepl("[^[:alnum:] ]", x))}
.2. Действительно, намного быстрее. От 1 минуты до 1 секунды. Опубликует в качестве ответа, чтобы я мог принять?
3. @RuiBarradas Я пытался сравнить вашу функцию с OP, я не могу воспроизвести эффективность. Я сделал что-то не так в тесте
library(microbenchmark); microbenchmark(OP = count_non_alnum(vec), Rui = count_non_alnum2(vec), times = 20L, unit = "relative")# Unit: relative expr min lq mean median uq max neval cld OP 1.00000 1.00000 1.000000 1.000000 1.000000 1.000000 20 a Rui 1.77505 1.76753 1.870186 1.751732 1.715179 3.362217 20 b
4. @akrun вы должны тестировать каждую функцию после обозначения отдельных символов.
microbenchmark(OP = map(vec_tokens, count_non_alnum), Rui = map(vec_tokens, count_non_alnum2), times = 5L, unit = "relative")
5. @DannyMorris Хорошо, я думаю, тогда проблема в части токенизации. Спасибо за разъяснение
Ответ №1:
Базовые функции R намного быстрее. Вот sum/grepl
решение и 4 разных способа вызова двух функций.
library(microbenchmark)
library(ggplot2)
library(dplyr)
library(stringr)
library(purrr)
# custom function that accepts string input and counts the number
# of non-alphanum characters
count_non_alnum <- function(x) {
stringr::str_detect(x, "[^[:alnum:] ]") %>% sum()
}
count_non_alnum2 <- function(x) {
sum(grepl("[^[:alnum:] ]", x))
}
# character vector of length 100K
vec <- rep("Hello. World.", 100)
# tokenize individual characters for each string
vec_tokens <- purrr::map(vec, function(x) {
stringr::str_split(x, "") %>% unlist()
})
# count non-alphanum characters
mb <- microbenchmark(
Danny_purrr = purrr::map(vec_tokens, count_non_alnum),
Rui_purrr = purrr::map(vec_tokens, count_non_alnum2),
Danny_base = sapply(vec_tokens, count_non_alnum),
Rui_base = sapply(vec_tokens, count_non_alnum2),
unit = "relative"
)
mb
#Unit: relative
# expr min lq mean median uq max neval cld
# Danny_purrr 58.508234 56.440147 52.854162 53.890724 53.464640 25.855456 100 c
# Rui_purrr 1.026362 1.021998 1.011265 1.025648 1.025087 1.558001 100 a
# Danny_base 58.643098 56.398330 52.491478 53.857666 52.821759 27.981780 100 b
# Rui_base 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 100 a
autoplot(mb)