Увеличьте производительность, удалив из цикла for
Суть аргумента такова:
Функция, которую я написал, учитывает один аргумент, буквенно-цифровую строку и выводит строку, в которой значения каждого элемента этой буквенно-цифровой строки переключаются для некоторого "сопоставления". MRE следующим образом:
#This is the original and switches value map
map = data.table(mapped = c(0:35), original = c(0:9,LETTERS))
#the function that I'm using:
as_numbers <- function(string) {
#split string unlisted
vector_unlisted <- unlist(strsplit(string,""))
#match the string in vector
for (i in 1:length(vector_unlisted)) {
vector_unlisted[i] <- subset(map, map$original==vector_unlisted[i])[[1]][1]
}
vector_unlisted <- paste0(vector_unlisted, collapse = "")
return(vector_unlisted)
}
Я пытаюсь отойти от for loop
для чего-то, что повышает производительность, поскольку функция работает, но она довольно медленная для количества элементов, которые я предоставил в этой форме:
unlist(lapply(dat$alphanum, function(x) as_numbers(x)))
Пример входных строк: 549300JV8KEETQJYUG13
. Это должно привести к строке типа 5493001931820141429261934301613
Поставка только одной строки в этом случае:
> as_numbers("549300JV8KEETQJYUG13")
[1] "5493001931820141429261934301613"
Ответы
Ответ 1
Используя Reduce
и gsub
, вы можете определить следующую функцию
replacer <- function(x) Reduce(function(x,r) gsub(map$original[r],
map$mapped[r], x, fixed=T), seq_len(nrow(map)),x)
# Let test it
replacer("549300JV8KEETQJYUG13")
#[1] "5493001931820141429261934301613"
Ответ 2
Мы можем использовать базовое преобразование:
#input and expected output
x <- "549300JV8KEETQJYUG13"
# "5493001931820141429261934301613"
#output
res <- paste0(strtoi(unlist(strsplit(x, "")), base = 36), collapse = "")
#test output
as_numbers(x) == res
# [1] TRUE
Производительность
Поскольку этот пост посвящен производительности, вот бенчмаркинг * для 3-х решений:
#input set up
map = data.table(mapped = c(0:35), original = c(0:9,LETTERS))
x <- rep(c("549300JV8KEETQJYUG13", "5493V8KE300J"), 1000)
#define functions
base_f <- function(string) {
sapply(string, function(x) {
paste0(strtoi(unlist(strsplit(x, "")), base = 36), collapse = "")
})
}
match_f <- function(string) {
mapped <- map$mapped
original <- map$original
sapply(strsplit(string, ""), function(y) {
paste0(mapped[match(y, original)], collapse= "")})
}
reduce_f <- function(string) {
Reduce(function(string,r)
gsub(map$original[r],
map$mapped[r], string, fixed = TRUE),
seq_len(nrow(map)), string)
}
#test if all return same output
all(base_f(x) == match_f(x))
# [1] TRUE
all(base_f(x) == reduce_f(x))
# [1] TRUE
library(rbenchmark)
benchmark(replications = 1000,
base_f(x),
match_f(x),
reduce_f(x))
# test replications elapsed relative user.self sys.self user.child sys.child
# 1 base_f(x) 1000 22.15 4.683 22.12 0 NA NA
# 2 match_f(x) 1000 19.18 4.055 19.11 0 NA NA
# 3 reduce_f(x) 1000 4.73 1.000 4.72 0 NA NA
* Примечание: microbenchmark() продолжает выдавать предупреждения, поэтому вместо этого используется rbenchmark(). Не стесняйтесь тестировать другие библиотеки и обновлять этот пост.
Ответ 3
Похоже на слияние:
map[as.data.table(unlist(strsplit(string, ""))),
.(mapped), on = c(original = "V1")][ , paste0(mapped, collapse = "")]
Обратите внимание, что оба "D1" и "1V" будут отображаться на "131"...
На вашем примере выводится: "5493001931820141429261934301613"
Вы можете использовать sep = "."
, если вы действительно хотите, чтобы это было обратимым отображением...
Ответ 4
Я бы использовал match
:
as_numbers <- function(string) {
lapply(strsplit(string, ""), function(y) {
paste0(map$mapped[match(y, map$original)], collapse= "")})
}
as_numbers(c("549300JV8KEETQJYUG13", "5493V8KE300J"))
#[[1]]
#[1] "5493001931820141429261934301613"
#
#[[2]]
#[1] "5493318201430019"
Добавлен вызов lapply
для правильного ввода длины > 1.
Если вам нужна дополнительная скорость, вы можете хранить map$mapped
и map$original
в отдельных векторах и использовать их в вызове match
вместо map$...
, поэтому вам не нужно подмножать data.frame/data.table столько раз (что довольно дорого).
Так как Q был около производительности, здесь была контрольная точка двух решений:
map = data.table(mapped = c(0:35), original = c(0:9,LETTERS))
x <- rep(c("549300JV8KEETQJYUG13", "5493V8KE300J"), 1000)
ascii_func <- function(string) {
lapply(string, function(x) {
x_ascii <- strtoi(charToRaw(x), 16)
paste(ifelse(x_ascii >= 65 & x_ascii <= 90,
x_ascii - 55, x_ascii - 48),
collapse = "")
})
}
match_func <- function(string) {
mapped <- map$mapped
original <- map$original
lapply(strsplit(string, ""), function(y) {
paste0(mapped[match(y, original)], collapse= "")})
}
library(microbenchmark)
microbenchmark(ascii_func(x), match_func(x), times = 25L)
#Unit: milliseconds
# expr min lq mean median uq max neval
# ascii_func(x) 83.47 92.55 96.91 96.82 103.06 112.07 25
# match_func(x) 24.30 24.74 26.86 26.11 28.67 31.55 25
identical(ascii_func(x), match_func(x))
#[1] TRUE