将一组函数应用于对象

时间:2018-05-17 12:07:56

标签: r

我有一个包含一组对象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()无效。)

任何提示?

3 个答案:

答案 0 :(得分:2)

在这种情况下,您可以将mapplyReducemget一起使用。

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