Tidyeval и применение семейства для добавления новых переменных в фрейм данных - PullRequest
0 голосов
/ 04 мая 2020

Я пытаюсь написать функцию для автоматизации создания некоторых новых переменных с помощью инструментов tidyverse. Я понял, что моя проблема связана с tidyeval, но я не совсем понял, где я ошибся в приведенном ниже коде, который просто воспроизводит имя переменной. В качестве второго шага я хотел бы сделать что-то помимо for l oop, чтобы применить эту функцию несколько раз. Я прочитал достаточно StackOverflow, но я не могу найти рабочий пример использования какой-то функции apply, создающей новые переменные в существующем фрейме данных. Спасибо!

library(tidyverse)
x = c(0,1,2,3,4)
y = c(0,2,4,5,8)
df <- data.frame(x,y)
df
simple_func <- function(x) {
  var_name <- paste0("pre_", x, "_months")
  var_name <-  enquo(var_name)
  df <- df %>%
    mutate(!! var_name := ifelse(x==y,1,0)) %>%
    mutate(!! var_name := replace_na(!! var_name))
  return(df)
}
simple_func(1)
#Desired result
temp <- data.frame("pre_1_months" = c(1,0,0,0,0))
temp
bind_cols(df,temp)

#Step 2, use some kind of apply function rather than a loop to apply this function sequentially
nums <- seq(1:10)
for (i in seq_along(nums)) {
  df <- simple_func(nums[i])
}
df

Ответы [ 2 ]

1 голос
/ 04 мая 2020

Чтобы основываться на ответе @akrun, более идиоматический c способ сделать это - передать df в качестве первого параметра вашей функции и указать x в качестве второго. Вы можете векторизовать функцию, поместив в нее l oop для однократного запуска для каждого элемента в x, используя rlang::syms вместо sym. Это также делает код короче, и вы можете добавить его в конвейер, как если бы это была функция dplyr.

simple_func <- function(df, x) 
{
    for(var_name in rlang::syms(paste0("pre_", x, "_months")))
    {
      df <- mutate(df, !! var_name := replace_na(ifelse(x==y,1,0)))
    }
    df
}

Итак, теперь вы можете:

df %>% simple_fun(1:5)
#>   x y pre_1_months pre_2_months pre_3_months pre_4_months pre_5_months
#> 1 0 0            1            1            1            1            1
#> 2 1 2            0            0            0            0            0
#> 3 2 4            0            0            0            0            0
#> 4 3 5            0            0            0            0            0
#> 5 4 8            0            0            0            0            0

РЕДАКТИРОВАТЬ

Следуя комментарию Лайонела Генри, а также отмечая желание ОП избегать циклов, вот единственная функция без циклов, которая может использоваться в конвейере с x произвольной длины, и который не зависит от преобразования в символы:

simple_func <- function(df, x) {
  f <- function(v) df <<- mutate(df, !!v := replace_na(ifelse(x == y, 1, 0)))
  lapply(paste0("pre_", x, "_months"), f)
  return(df)
}

Это работает точно так же:

df %>% simple_fun(1:10)
#>   x y pre_1_months pre_2_months pre_3_months pre_4_months pre_5_months pre_6_months
#> 1 0 0            1            1            1            1            1            1
#> 2 1 2            0            0            0            0            0            0
#> 3 2 4            0            0            0            0            0            0
#> 4 3 5            0            0            0            0            0            0
#> 5 4 8            0            0            0            0            0            0
#>   pre_7_months pre_8_months pre_9_months pre_10_months
#> 1            1            1            1             1
#> 2            0            0            0             0
#> 3            0            0            0             0
#> 4            0            0            0             0
#> 5            0            0            0             0

1 голос
/ 04 мая 2020

Поскольку это строка, мы можем использовать sym для преобразования в символ, а затем оценить (!!

simple_func <- function(x) {
    var_name <- paste0("pre_", x, "_months")
    var_name <-  rlang::sym(var_name)
    df %>%
      mutate(!! var_name := ifelse(x==y,1,0)) %>%
      mutate(!! var_name := replace_na(!! var_name))

    }

проверка с кодом OP

nums <- seq(1:10)
for (i in seq_along(nums)) {
   df <- simple_func(nums[i])
 }

df
#  x y pre_1_months pre_2_months pre_3_months pre_4_months pre_5_months pre_6_months pre_7_months pre_8_months
#1 0 0            1            1            1            1            1            1            1            1
#2 1 2            0            0            0            0            0            0            0            0
#3 2 4            0            0            0            0            0            0            0            0
#4 3 5            0            0            0            0            0            0            0            0
#5 4 8            0            0            0            0            0            0            0            0
#  pre_9_months pre_10_months
#1            1             1
#2            0             0
#3            0             0
#4            0             0
#5            0             0

Мы могли бы использовать map и изменить mutate на transmute

simple_func <- function(x) {
    var_name <- paste0("pre_", x, "_months")
    var_name <-  rlang::sym(var_name)
    df %>%
      transmute(!! var_name := ifelse(x==y,1,0)) %>%
      transmute(!! var_name := replace_na(!! var_name))

    }

library(purrr)
library(dplyr)
map_dfc(1:10, simple_func) %>% 
       bind_cols(df,.)
...