R: `Ошибка в f (x): не удалось найти функцию" f "` при попытке использовать столбец функций в качестве аргумента в таблице - PullRequest
2 голосов
/ 11 июля 2019

Я экспериментирую с использованием функций в фреймах данных (tidyverse tibbles) в R и столкнулся с некоторыми трудностями. Ниже приведен минимальный (тривиальный) пример моей проблемы.

Предположим, у меня есть функция, которая принимает три аргумента: x и y - это числа, а f - это функция. Он выполняет f(x) + y и возвращает вывод:

func_then_add = function(x, y, f) {
  result = f(x) + y
  return(result)
}

И у меня есть несколько простых функций, которые он может использовать как f:

squarer = function(x) {
  result = x^2
  return(result)
}

cuber = function(x) {
  result = x^3
  return(result)
}

Сделано само по себе, func_then_add работает как рекламируется:

> func_then_add(5, 2, squarer)
[1] 27
> func_then_add(6, 11, cuber)
[1] 227

Но допустим, у меня есть датафрейм (tidyverse tibble) с двумя столбцами для числовых аргументов и одним столбцом, для которого я хочу функцию:

library(tidyverse)
library(magrittr)

test_frame = tribble(
  ~arg_1, ~arg_2, ~func,
  5, 2, squarer,
  6, 11, cuber
)

> test_frame
# A tibble: 2 x 3
  arg_1 arg_2 func  
  <dbl> <dbl> <list>
1     5     2 <fn>  
2     6    11 <fn>  

Затем я хочу создать еще один столбец result, равный func_then_add, примененный к этим трем столбцам. Это должно быть 27 и 227, как раньше. Но когда я пытаюсь это сделать, я получаю сообщение об ошибке:

> test_frame %>% mutate(result=func_then_add(.$arg_1, .$arg_2, .$func))
Error in f(x) : could not find function "f"

Почему это происходит, и как мне получить то, что я хочу правильно? Признаюсь, я новичок в "функциональном программировании", поэтому, возможно, я просто делаю очевидную синтаксическую ошибку ...

Ответы [ 2 ]

2 голосов
/ 11 июля 2019

Это потому, что вы должны наносить на карту вместо мутации.Mutate вызывает функцию один раз и предоставляет целые столбцы в качестве аргументов.

Вторая проблема заключается в том, что test_frame$func[1] - это не функция, а список с одним элементом.Вы не можете иметь столбцы «функции», только столбцы списка.

Попробуйте:

test_frame$result <- with(test_frame, 
          map_dbl(1:2, ~ func_then_add(arg_1[.], arg_2[.], func[[.]])))

Результат:

# A tibble: 2 x 4
  arg_1 arg_2 func   result
  <dbl> <dbl> <list>  <dbl>
1     5     2 <fn>       27
2     6    11 <fn>      227

РЕДАКТИРОВАТЬ : более простое решение с использованием dplyr, mutate и rowwise:

test_frame %>% rowwise %>% mutate(res=func_then_add(arg_1, arg_2, func))

Откровенно говоря, я немного озадачен этим последним.Почему func, а не func[[1]]?func должен быть списком, а не функцией.mutate и rowwise делают здесь что-то зловещее, например, автоматическое преобразование списка в вектор.

Edit 2 : на самом деле, это явно написано в руководстве rowwise:

Его основное влияние - позволить вам работать со списочными переменными в «summaze ()» и «mutate ()» без использования «[[1]]».

Окончательное редактирование: В последнее время я настолько зациклился на tidyverse, что даже не подумал о простейшем варианте - использовании базы R:

apply(test_frame, 1, function(x) func_then_add(x$arg_1, x$arg_2, x$func))
2 голосов
/ 11 июля 2019

Не самый элегантный, но мы можем сделать:

test_frame %>% 
  mutate(Res= map(seq_along(.$func), function(x)
        func_then_add(.$arg_1, .$arg_2, .$func[[x]]))) 

РЕДАКТИРОВАТЬ : Вышеуказанные значения map относятся ко всем данным, что на самом деле не то, что ОП хочет.Как предлагает @January, это может быть лучше применено как:

Result <- test_frame %>% 
  mutate(Res= map(seq_along(.$func), function(x)
       func_then_add(.$arg_1[x], .$arg_2[x], .$func[[x]])))

Result$Res 

Вышеупомянутое снова не очень эффективно, так как возвращает список.Лучшая альтернатива (опять же, как предлагает @January, это использовать map_dbl, который возвращает тот же тип данных, что и его объекты:

test_frame %>% 
   mutate(Res= map_dbl(seq_along(.$func), function(x)
       func_then_add(.$arg_1[x], .$arg_2[x], .$func[[x]])))
# A tibble: 2 x 4
  arg_1 arg_2 func     Res
  <dbl> <dbl> <list> <dbl>
1     5     2 <fn>      27
2     6    11 <fn>     227
...