Неожиданные значения при применении пользовательской функции в dplyr :: mutate

Мои данные выглядят так:

library(tidyverse)

df <- tribble(
    ~y_val, ~z_val,
    2, 4,
    5, 3, 
    8, 2, 
    1, 1, 
    9, 3)

У меня есть пользовательская функция fun_b(), которую я хотел бы применить к фрейму данных с помощью вызова dplyr :: mutate. Однако fun_b() использует функцию fun_a(), внутри которой есть цикл:

fun_a <- function(x, y, z, times = 1) {

    df <- data.frame()
    for (i in 1:times) {
        x <- x * 2 + i * x
        y <- y / 3 + i * y
        z <- z + 1 + z * i
    d <- data.frame(x, y, z)
    df <- rbind(df, d)
    }
    return(df)
}

fun_b <- function(x, y, z, times = 1) {
    df <- fun_a(x, y, z, times)
    x_r <- sum(df$x)
    y_r <- sum(df$y)
    z_r <- sum(df$z)
    val <- x_r / y_r * z_r
    return(val)
}

Когда я запускаю пользовательскую функцию:

df %>% 
    mutate(test = fun_b(x = 1, y = y_val, z = z_val, times = 1))

Каждое измененное значение в test показывает одно и то же значение (13,95). В этом нет смысла! Например, первая строка в таблице (y_val = 2, z_val = 4) должна быть 10,125!

fun_b(x = 1, y = 2, z = 4, times = 1)

Что тут происходит?


person emehex    schedule 24.10.2016    source источник
comment
Вам просто нужно сгруппировать по строкам, т.е. df %>% rowwise() %>% mutate(test = fun_b(x = 1, y = y_val, z = z_val, times = 1)), или просто убрать sum вызовы, чтобы ваши функции были векторизуемыми.   -  person alistaire    schedule 25.10.2016


Ответы (2)


Вы можете группировать построчно, чтобы функция оценивалась отдельно для каждой строки:

df %>% 
    rowwise() %>%
    mutate(test = fun_b(x = 1, y = y_val, z = z_val, times = 1))

## Source: local data frame [5 x 3]
## Groups: <by row>
## 
## # A tibble: 5 × 3
##   y_val z_val     test
##   <dbl> <dbl>    <dbl>
## 1     2     4 10.12500
## 2     5     3  3.15000
## 3     8     2  1.40625
## 4     1     1  6.75000
## 5     9     3  1.75000

или отредактируйте fun_b, чтобы он векторизовался, или просто позвольте R:

df %>% mutate(test = Vectorize(fun_b)(x = 1, y = y_val, z = z_val, times = 1))

## # A tibble: 5 × 3
##   y_val z_val     test
##   <dbl> <dbl>    <dbl>
## 1     2     4 10.12500
## 2     5     3  3.15000
## 3     8     2  1.40625
## 4     1     1  6.75000
## 5     9     3  1.75000
person alistaire    schedule 24.10.2016

Попробуйте следующее

df %>% 
    group_by(y_val, z_val) %>% 
    mutate(test = fun_b(x = 1, y = y_val, z = z_val, times = 1))

Получилось у меня 10,125.

person Jim Raynor    schedule 24.10.2016