Применить набор функций к объекту
У меня есть dataframe с набором объектов df$data
и набором правил для применения к каждому объекту df$rules
.
df <- data.frame(
data = c(1,2,3),
rules = c("rule1", "rule1, rule2, rule3", "rule3, rule2"),
stringsAsFactors = FALSE
)
Правила
rule1 <- function(data) {
data * 2
}
rule2 <- function(data) {
data + 1
}
rule3 <- function(data) {
data ^ 3
}
Для каждой строки в ядре данных я хочу применить все правила, указанные в столбце rules
. Правила должны применяться последовательно.
Что я понял:
apply_rules <- function(data, rules) {
for (i in 1:length(data)) {
rules_now <- unlist(strsplit(rules[i], ", "))
for (j in 1:length(rules_now)) {
data[i] <- apply_rule(data[i], rules_now[j])
}
}
return(data)
}
apply_rule <- function(data, rule) {
return(sapply(data, rule))
}
apply_rules(df$data, df$rules)
# [1] 2 125 28
Хотя это работает, я уверен, что должны быть более элегантные решения. На SO я мог бы найти много материала о apply
-functions, а также один посте о применении многих функций к вектору и что - то о цепочках функций. Идея Compose
выглядит многообещающе, но я не мог понять, как сделать вызов Compose
с моими правилами как строки. (parse()
не работает..)
Любые намеки?
Ответы
Ответ 1
Несколько хороших ответов уже есть, но в другой вариант - постройте цепочку труб в виде строки, затем оцените ее. Например - для строки 1 - eval(parse(text = "1 %>% rule1"))
дает 2
eval_chain <- function(df) {
eval(parse(text = paste(c(df$data, unlist(strsplit(df$rules, ", "))), collapse=" %>% ")))
}
df$value <- sapply(1:nrow(df), function(i) df[i, ] %>% eval_chain)
# data rules value
# 1 1 rule1 2
# 2 2 rule1, rule2, rule3 125
# 3 3 rule3, rule2 28
Ответ 2
В этом случае вы можете использовать mapply
и Reduce
вместе с mget
.
mapply(function(d,r) Reduce(function(lhs,rhs) rhs(lhs),
c(d,mget(strsplit(r,", ")[[1]],envir = globalenv())))
,df$data
,df$rules)
# [1] 2 125 28
Возможно, вам придется настроить аргумент envir
mget
для вашего конкретного случая. Вероятно, было бы более надежно передавать среду, в которой ваши правила определены для mget
.
Ответ 3
Я думаю, что вам нужно немного изменить подход (в этом случае выражения только ухудшат ситуацию):
df <- data.frame(
data = c(1,2,3),
rules = c("rule1", "rule1, rule2, rule3", "rule3, rule2"),
stringsAsFactors = FALSE
)
# list of functions
fun_list <- list(
rule1 = function(x) x*2,
rule2 = function(x) x+1,
rule3 = function(x) x^3
)
# function to call list of functions
call_funs <- function(x, fun_vec) {
for (i in seq_along(fun_vec)) {
x <- fun_list[[fun_vec[[i]]]](x)
}
x
}
(want <- unlist(Map(call_funs, df$data, strsplit(gsub(" ", "", df$rules), ","))))
# 2 125 28