我有一个包含一组对象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
- 函数和post关于将多个函数应用于向量和chaining functions的内容的东西。 Compose
的想法看起来很有希望,但我无法弄清楚如何使用我的规则作为字符串来调用Compose
。 (parse()
无效。)
任何提示?
答案 0 :(得分: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
的环境明确传递可能会更加健壮。
答案 1 :(得分:2)
已经有了一些好的答案,但抛出了另一个选项 - 将管道链构建为字符串然后对其进行评估。例如 - 对于第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 :(得分:1)
我认为你必须稍微改变一下方法(表达式只会让事情变得更糟):
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