【问题标题】:Making tidyeval function inside case_when在 case_when 中制作 tidyeval 函数
【发布时间】:2020-02-13 13:16:13
【问题描述】:

我有一个数据集,我喜欢根据这些值的概率分布在其他值中估算一个值。让我们先做一些可重现的例子

library(tidyverse)
library(janitor)

dummy1 <- runif(5000, 0, 1)
dummy11 <- case_when(
    dummy1 < 0.776 ~ 1,
    dummy1 < 0.776 + 0.124 ~ 2,
    TRUE ~ 5)

df1 <- tibble(q1 = dummy11)

这是输出:

df1 %>% tabyl(q1)
 q1    n percent
  1 3888  0.7776
  2  605  0.1210
  5  507  0.1014

我使用 mutatesample 在值 1 和 2 之间共享值 = 5,如下所示:

df1 %>%
    mutate(q1 = case_when(q1 == 5 ~ sample(
        2,
        length(q1),
        prob = c(0.7776, 0.1210),
        replace = TRUE
    ),
    TRUE ~ as.integer(q1))
    )

结果如下:

q1    n percent
  1 4322  0.8644
  2  678  0.1356

这种方法似乎有效,但是由于我需要将其应用于多个变量,因此我尝试编写一个与 tidyeval 一起使用 tidyverse 的函数,就像这样

    my_impute <- function(.data, .prob_var, ...) {
        .prob_var <- enquo(.prob_var)

        .data %>%
            sample(2, prob=c(!!.prob_var), replace = TRUE) 
    }

# running on data 
df1 %>%
    mutate(q1 = case_when(q1 == 5 ~ !!my_impute(q1),
    TRUE ~ as.integer(q1))
    )

错误是:

Error in eval_tidy(pair$lhs, env = default_env) : object 'q1' not found

【问题讨论】:

  • 在最后一部分中,您从原始数据集中传递 prob,其中 'q1' 为 integer 在函数外部,它似乎基于 tabyl 输出
  • 对不起@akrun,我不关注,q1 是 1、2 或 5。啊,是的,我使用 tabyl 来显示输出
  • 我的意思是,在函数外部的代码中,您将prob 传递为prob = c(0.7776, 0.1210),它来自tabyl 输出。在函数内部,它只是传递列 'q1',而不是概率值

标签: r tidyverse case-when tidyeval


【解决方案1】:

我们需要tabyl生成的'percent'列中的prob值,所以函数可以修改为

library(janitor)
library(dplyr)

my_impute <- function(.data, .prob_var, vals, ...) {
        .prob_var = enquo(.prob_var)
        .prob_vals <- .data %>%
             janitor::tabyl(!!.prob_var) %>%
             filter(!!.prob_var %in% vals) %>%
             pull(percent)

         .data %>%
              mutate(!! .prob_var := case_when(!! .prob_var == 5 ~ 
                sample(
                        2,
                        n(),
                        prob = .prob_vals,
                        replace = TRUE
                    ),
                    TRUE ~ as.integer(q1))
                    )
    }


df1 %>% 
     my_impute(q1, vals = 1:2) %>%
     tabyl(q1)
# q1    n percent
# 1 4285   0.857
# 2  715   0.143

【讨论】:

  • 谢谢@akrun,你能在这里解释一下 1:2:filter(!!.prob_var %in% 1:2),所以如果我们有值 1、3 和 5(而不是 1,2 和 5)或更多 3 个值,可以说1,3,4,5,6。例如,我将输入操作为只有 1,3 和 5,我尝试将过滤器替换为 filter(!!.prob_var %in% c(1,3)) 并返回 1,2,3
  • @DanielG 在这种情况下,您可以将其作为另一个参数传递给函数。在这里,filter 正在过滤 'q1' 中的值为 1 和 2 的行。我将函数更改为将 'vals' 作为另一个值向量传递
  • 我只是想弄清楚,希望你不要介意。如果我们有dummy11 &lt;- case_when( dummy1 &lt; 0.776 ~ 1, dummy1 &lt; 0.776 + 0.124 ~ 3, TRUE ~ 5),那么使用更新的函数我将传递像df1 %&gt;% my_impute(q1, vals = c(1,3)) %&gt;% tabyl(q1) 这样的值,但输出返回值1、2 和3
  • @DanielG 这是一个不同的函数,您在每个步骤中只选择值的子集,在这种情况下,您可能需要更改函数内的case_when
【解决方案2】:

只是加我两分钱,新版本的rlang允许替换准引用过程:enquo() + !!你可以使用 curly-curly 来包含变量:函数就像:

my_impute <- function(.data, .prob_var, vals, ...) {

  #.prob_var = enquo(.prob_var)
  # commented out since it is no longer needed
  .prob_vals <- .data %>%
    janitor::tabyl({{.prob_var}}) %>%
    filter({{.prob_var}} %in% {{vals}}) %>%
    pull(percent)

  .data %>%
    mutate( {{.prob_var}} := case_when( {{.prob_var}} == 5 ~ 
                                       sample(
                                         2,
                                         n(),
                                         prob = {{.prob_vals}},
                                         replace = TRUE
                                       ),
                                     TRUE ~ as.integer(q1))
    )
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-31
    • 1970-01-01
    • 1970-01-01
    • 2019-09-21
    • 1970-01-01
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多