【问题标题】:Using the pipe in selfmade function with tidyeval (quo_name)在 tidyeval (quo_name) 的自制函数中使用管道
【发布时间】:2023-07-17 03:05:01
【问题描述】:

我有两个函数:date_diff 和 group_stat。所以我已经阅读了这篇文章tidyverse 并尝试创建简单的函数并使用管道。

第一个函数创建一个difftime 并将它们命名为timex_minus_timey,但是当我将此结果传递到下一个函数时,我必须查看名称以便填写summary_var。有一个更好的方法吗?

library(tidyverse)
# 
set.seed(42)
data <- dplyr::bind_rows(
  tibble::tibble(Hosp = rep("A", 1000),
                 drg = sample(letters[1:5], 1000, replace = TRUE),
                 time1 = as.POSIXlt("2018-02-03 08:00:00", tz = "UTC") + rnorm(1000, 0, 60*60*60),
                 time2 = time1 + runif(1000, min = 10*60, max = 20*60)),

  tibble::tibble(Hosp = rep("B", 1000),
                 drg = sample(letters[1:5], 1000, replace = TRUE),
                 time1 = as.POSIXlt("2018-02-03 08:00:00", tz = "UTC") + rnorm(1000, 0, 60*60*60),
                 time2 = time1 + runif(1000, min = 10*60, max = 20*60))
)



date_diff <- function(df, stamp1, stamp2, units = "mins"){

  stamp1 <- rlang::enquo(stamp1)
  stamp2 <- rlang::enquo(stamp2)

  name <- paste0(rlang::quo_name(stamp1), "_minus_", rlang::quo_name(stamp2))

  out <- df %>%
    dplyr::mutate(!!name := as.numeric(difftime(!!stamp1, !!stamp2, units=units)))

  out
}


group_stat <- function(df, group_var, summary_var, .f) {

  func <- rlang::as_function(.f)

  group_var <-  rlang::enquo(group_var)
  summary_var <-rlang::enquo(summary_var)

  name <- paste0(rlang::quo_name(summary_var), "_", deparse(substitute(.f)))

  df %>%
    dplyr::group_by(!!group_var) %>%
    dplyr::summarise(!!name := func(!!summary_var, na.rm = TRUE))
}


data %>% 
  date_diff(time2, time1) %>%  
  group_stat(Hosp, summary_var = time2_minus_time1, mean)
#> # A tibble: 2 x 2
#>   Hosp  time2_minus_time1_mean
#>   <chr>                  <dbl>
#> 1 A                       15.1
#> 2 B                       14.9

reprex package (v0.2.1) 于 2019 年 5 月 2 日创建

【问题讨论】:

  • 有点不清楚你所说的“更好”是什么意思。由于group_stat 可以独立于date_diff 使用,因此需要知道要汇总哪一列。另一种方法是在group_stat 中引入一个假设,以便它需要一个特定的列名。然后你可以删除summary_var,因为它可以自动推断。总的来说,我认为需要更多详细信息来说明您要完成的工作才能有效地提供答案。
  • @ArtemSokolov:不,我不想硬编码名称。所以也许没有任何问题......

标签: r dplyr purrr rlang tidyeval


【解决方案1】:

如果您打算始终以这种方式一个接一个地使用这些函数,您可以使用date_diff 添加一个包含新列名称的属性,并让group_stat 使用该属性。使用if 条件时,仅当属性存在且未提供summary_var 参数时才使用该属性。

date_diff <- function(df, stamp1, stamp2, units = "mins"){

  stamp1 <- rlang::enquo(stamp1)
  stamp2 <- rlang::enquo(stamp2)

  name <- paste0(rlang::quo_name(stamp1), "_minus_", rlang::quo_name(stamp2))

  out <- df %>%
    dplyr::mutate(!!name := as.numeric(difftime(!!stamp1, !!stamp2, units=units)))

  attr(out, 'date_diff_nm') <- name
  out
}


group_stat <- function(df, group_var, summary_var, .f) {
  if(!is.null(attr(df, 'date_diff_nm')) & missing(summary_var))
      summary_var <- attr(df, 'date_diff_nm')

  group_var <-  rlang::enquo(group_var)
  name <- paste0(summary_var, "_", deparse(substitute(.f)))

  df %>%
    dplyr::group_by(!!group_var) %>% 
    dplyr::summarise_at(summary_var, funs(!!name := .f), na.rm = T)
}


data %>% 
  date_diff(time2, time1) %>% 
  group_stat(Hosp, .f = mean)

# # A tibble: 2 x 2
#   Hosp  time2_minus_time1_mean
#   <chr>                  <dbl>
# 1 A                       15.1
# 2 B                       14.9

【讨论】:

  • 非常感谢!然后我应该为 if 我不想将 group_stat 与 date_diff 一起使用,对吗?
  • 那是 else 部分应该还有 rlang::as_function 对吧?
  • 如果您在 date_diff 之后没有使用 group stat 并且您将 summary_var 作为字符提供,则无需更改。如果您只想输入不带引号的名称,那么是的,您需要一个 else 来获取 summary_var 的 quo_name。你不需要对这个函数做任何事情。
  • 所以不需要as_function??我可以使用 .f 吗?
  • 是的,因为您已经将实际函数作为参数传递。 as_formula 将字符串和公式转换为函数,因此如果您将其编写为公式(使用 ~ 和 .),则需要 as_function。
最近更新 更多