Применить набор функций к объекту - PullRequest
0 голосов
/ 17 мая 2018

У меня есть фрейм данных с набором объектов 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 -функциях, а также один пост о применении многих функций к вектору и кое-что о функциях цепочки . Идея Compose выглядит многообещающе, но я не мог понять, как позвонить на Compose с моими правилами в виде строки. (parse() не работает ..)

Есть подсказки?

Ответы [ 3 ]

0 голосов
/ 17 мая 2018

В этом случае можно использовать 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.

0 голосов
/ 17 мая 2018

Некоторые хорошие ответы уже есть, но добавьте другой вариант - создайте цепочку труб в виде строки, а затем оцените ее.Например - для строки 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
0 голосов
/ 17 мая 2018

Я думаю, что вы должны немного изменить подход (в этом случае выражения только ухудшат ситуацию):

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...