Я создал тиббл, в котором имя каждого столбца соответствует аргументам пользовательской функции. Столбцов много, поэтому я стараюсь избегать явного вызова и назначения каждого аргумента. Я слепо пробовал разные синтаксисы, но безуспешно. Если бы кто-нибудь мог мне помочь, я был бы признателен.
Вот МВЕ:
# Define simple function
myFunc <- function(a, b, c) a*b^c
# Create tibble of parameters (intentionally out of order)
tbl <- tibble(b = 2:6, a = 1:5, c = -1:3)
# Apply function by listing columns explicitly
tbl |> rowwise() |> mutate(myFunc(a = a, b= b, c=c))
# Fail to apply function...
tbl |> rowwise() |> mutate(myFunc())
# Fail again...
tbl |> rowwise() |> mutate(myFunc(.data))
# Continue ad nauseum...
@the-mad-statter Да!
Одним из вариантов было бы использовать семейство функций purrr::pmap
, например. используя pmap_dbl
, вы можете сделать:
myFunc <- function(a, b, c) a * b^c
library(dplyr, warn.conflicts = FALSE)
library(purrr)
tbl <- tibble(b = 2:6, a = 1:5, c = -1:3)
tbl %>%
mutate(res = pmap_dbl(., myFunc))
#> # A tibble: 5 × 4
#> b a c res
#> <int> <int> <int> <dbl>
#> 1 2 1 -1 0.5
#> 2 3 2 0 2
#> 3 4 3 1 12
#> 4 5 4 2 100
#> 5 6 5 3 1080
Боже мой, это так просто и элегантно, но я не могу заставить это работать в более сложной ситуации, которую пытаюсь выполнить. В более сложном примере функция имеет аргументы по умолчанию и возвращает тиббл, а не двойное значение. В результате я попробовал синтаксис `pmap(., myMoreComplexFunction), .keep = "none")` и получаю ошибку `! объект '.' не найдено. Есть идеи, почему?
Ааа, я понял, почему приведенный выше синтаксис не удался. Я использовал трубку |>
в mutate
, а не в %>%
. Теперь работает!!!
Ваши последние два вызова завершаются неудачей, потому что myFunc
ожидает три вектора, а не ноль аргументов или один data.frame.
Вы можете изменить его, чтобы последний вызов работал, придав ему аргумент, который получает местоимение данных .data
:
myFunc <- function(x) x$a * x$b ^ x$c
tbl |> rowwise() |> mutate(myFunc(.data))
Помните, что .data
не является обычным data.frame, поэтому вы не можете использовать x
так, как если бы он был.
возможно, пользовательская функция используется для разных целей и ее не нужно менять.
@Оньямбу, вообще-то, я этого ожидаю. Но вы всегда можете его завернуть.
Я бы предпочел этого не делать, но из любопытства... как бы вы это обернули?
@mikemtnbikes Ну myFuncWrapper = function (x) myFunc(x$a, x$b, x$c)
.
Вы можете использовать любое из следующих действий и выполнить привязку к данным:
do.call(myFunc, tbl)
[1] 0.5 2.0 12.0 100.0 1080.0
exec(myFunc, !!!tbl)
[1] 0.5 2.0 12.0 100.0 1080.0
NB: Что, если бы в tbl
было больше столбцов, некоторые из которых не определены в функции? Затем используйте:
eval(body(myFunc), tbl)
[1] 0.5 2.0 12.0 100.0 1080.0
Обратите внимание, что следующее не удается:
tbl$d <- 10
do.call(myFunc, tbl)
Error in (function (a, b, c) :
unused argument (d = c(10, 10, 10, 10, 10))
tbl %>%
+ mutate(res = pmap_dbl(., myFunc))
Error in `mutate()`:
ℹ In argument: `res = pmap_dbl(., myFunc)`.
Caused by error in `pmap_dbl()`:
ℹ In index: 1.
Caused by error in `.f()`:
! unused argument (d = .l[[4]][[i]])
Run `rlang::last_trace()` to see where the error occurred.
eval(body(myFunc), tbl) # THIS RUNS
[1] 0.5 2.0 12.0 100.0 1080.0
Наконец, самый безопасный способ — изменить формальные параметры функции напрямую с помощью данных. Это делается для того, чтобы гарантировать, что если в функции есть значения по умолчанию, эти значения сохраняются при изменении значений в списке. Обратите внимание, что eval(body(fun), ...)
может потерпеть неудачу, если функция имеет значения по умолчанию. Используйте код ниже
tbl$e <- "nothing"
do.call(myFunc, modifyList(a<-formals(myFunc), tbl)[names(a)])
[1] 0.5 2.0 12.0 100.0 1080.0
Примечание:
Если ваша функция не векторизована (что редко случается с большинством функций R), вызовите mapply
do.call(mapply, c(myFunc, modifyList(a<-formals(myFunc), tbl)[names(a)]))
[1] 0.5 2.0 12.0 100.0 1080.0
Ваш ответ игнорирует вызов rowwise()
. Для примера функции, предоставленной OP, это не имеет значения, но я предполагаю, что она присутствовала по какой-то причине.
@KonradRudolph по строкам предназначен для векторизации. Это не имеет никакого эффекта, если функция сама векторизована.
Да, в том-то и дело: предположительно функция OP не векторизована.
@KonradRudolph Я имел в виду другие мои решения. Я удалил pmap, как только понял, что он был опубликован. Ваш пройдет, поскольку вы изменили функцию. Извините за это.
Мы можем использовать !!! как показано:
library(dplyr)
library(rlang)
myFunc <- function(a, b, c) a*b^c
tbl <- tibble(b = 2:6, a = 1:5, c = -1:3)
tbl %>%
rowwise %>%
mutate(my = myFunc(!!!syms(set_names(names(.))))) %>%
ungroup
предоставление
# A tibble: 5 × 4
b a c my
<int> <int> <int> <dbl>
1 2 1 -1 0.5
2 3 2 0 2
3 4 3 1 12
4 5 4 2 100
5 6 5 3 1080
Хороший пример! Синтаксис более сложен, чем принятый ответ, но помогает мне понять!!! лучше.
Да, но принятый ответ не отвечает на заданный вопрос о том, как «Применить функцию rowwise()».
Верно, но это довольно тривиальная настройка. Еще добавлю.
Готовы ли вы добавить
...
к определению функции?