如何将函数中新创建的变量引用到辅助函数?

How do I quote a newly created variable in a function to a helper function?

提问人:ScottyJ 提问时间:1/5/2023 最后编辑:zephrylScottyJ 更新时间:1/5/2023 访问量:62

问:

问题

在函数中引用参数的正确方法是什么,该参数将用于创建将传递给另一个函数的新变量?

背景

最终目标是在数据帧中为具有 2 级层次结构的树状图创建标签,我正在尝试创建一个可重用的函数。下面是一个更基本的例子:

library(scales)
library(tidyverse)

# Create dataframe
region = rep(c("North", "South"), 3)
district <- sprintf("Dist-%d", 1:6)
sales <- seq(2000, 1500000000, length.out = 6)

df <- tibble(region, district, sales)
df
# A tibble: 6 × 3
  region district      sales
  <chr>  <chr>         <dbl>
1 North  Dist-1         2000
2 South  Dist-2    300001600
3 North  Dist-3    600001200
4 South  Dist-4    900000800
5 North  Dist-5   1200000400
6 South  Dist-6   1500000000

我创建了这个辅助函数来格式化货币。它将在 main 函数中使用,我的问题与将新的变量名称从 main 函数传递给这个帮助程序有关:

# First function for formatting currency
mydollars <- scales::label_dollar(prefix = "$",
                          largest_with_cents = 5000,
                          scale_cut = c(0, " K" = 1e3, " M" = 1e6, " B" = 1e9, " T" = 1e12)
)
# Example function output
mydollars(df$sales)
[1] "$2 K"   "$300 M" "$600 M" "$900 M" "$1.2 B" "$1.5 B"

这是利用上述帮助程序的主要功能。我将一个数据帧传递给函数,创建第 2 级“.index”标签,然后对数字列进行分组和聚合,我将其附加到“2”后缀,以便我知道它是第二个数字,我的问题出现在 with .如果我将该代码替换为 ,我就会使该函数正常工作。paste()mydollars("{{agg_number}}2")"Test String"

treemap_index1 <- function(df, category1, category2, agg_number){
  
  df_out <- df %>% 
    mutate("{{category2}}.index" := paste({{category2}}, mydollars({{agg_number}}), sep = "\n")) %>% 
    group_by({{category1}}) %>%
    mutate("{{agg_number}}2" := sum({{agg_number}}),
           "{{category1}}.index" := paste({{category1}}, 
                                          mydollars("{{agg_number}}2"), # Code breaks on this line
                                          sep = "\n")) %>%
    print()
  
  return(df_out)
  
}

treemap_index1(df, region, district, sales)

 rlang::last_error()
<error/dplyr:::mutate_error>
Error in `mutate()`:
! Problem while computing `region.index = paste(region, mydollars("{{agg_number}}2"), sep = "\n")`.
ℹ The error occurred in group 1: region = "North".
Caused by error in `x * scale`:
! non-numeric argument to binary operator
---
Backtrace:
  1. global treemap_index1(df, region, district, sales)
 10. scales (local) mydollars("{{agg_number}}2")
 11. scales::dollar(...)
 12. scales::number(...)
 13. scales:::scale_cut(...)
 14. base::cut(...)
Run `rlang::last_trace()` to see the full context.

如果我替换如下所示的违规代码,则该函数将正常工作:

treemap_index2 <- function(df, category1, category2, agg_number){
  
  df_out <- df %>% 
    mutate("{{category2}}.index" := paste({{category2}}, mydollars({{agg_number}}), sep = "\n")) %>% 
    group_by({{category1}}) %>%
    mutate("{{agg_number}}2" := sum({{agg_number}}),
           "{{category1}}.index" := paste({{category1}}, 
                                          "Test String", # Temporarily replaced code
                                          sep = "\n")) %>%
    print()
  
  return(df_out)
  
}
treemap_index2(df, region, district, sales)

# A tibble: 6 × 6
# Groups:   region [2]
  region district      sales district.index       sales2 region.index        
  <chr>  <chr>         <dbl> <chr>                 <dbl> <chr>               
1 North  Dist-1         2000 "Dist-1\n$2 K"   1800003600 "North\nTest String"
2 South  Dist-2    300001600 "Dist-2\n$300 M" 2700002400 "South\nTest String"
3 North  Dist-3    600001200 "Dist-3\n$600 M" 1800003600 "North\nTest String"
4 South  Dist-4    900000800 "Dist-4\n$900 M" 2700002400 "South\nTest String"
5 North  Dist-5   1200000400 "Dist-5\n$1.2 B" 1800003600 "North\nTest String"
6 South  Dist-6   1500000000 "Dist-6\n$1.5 B" 2700002400 "South\nTest String"

感谢帮助...

我将不胜感激有关如何将新变量名称正确传递给辅助函数的指导,并且由于我是数据屏蔽、仲裁、非标准评估的新手,因此非常感谢关于如何更好地完成这项工作的任何其他评论。谢谢。

R tidyeval nse data-masking quosure

评论


答:

2赞 stefan 1/5/2023 #1

改编莱昂内尔·亨利(Lionel Henry)的答案(@LionelHenry),一种选择是使用和代词,如下所示:rlang::englue.data

library(scales)
library(tidyverse)

treemap_index1 <- function(df, category1, category2, agg_number) {
  df %>%
    mutate("{{category2}}.index" := paste({{ category2 }}, mydollars({{ agg_number }}), sep = "\n")) %>%
    group_by({{ category1 }}) %>%
    mutate(
      "{{agg_number}}2" := sum({{ agg_number }}),
      "{{category1}}.index" := paste(
        {{ category1 }},
        mydollars(.data[[rlang::englue("{{agg_number}}2")]]),
        sep = "\n"
      )
    )
}

treemap_index1(df, region, district, sales)
#> # A tibble: 6 × 6
#> # Groups:   region [2]
#>   region district      sales district.index       sales2 region.index 
#>   <chr>  <chr>         <dbl> <chr>                 <dbl> <chr>        
#> 1 North  Dist-1         2000 "Dist-1\n$2 K"   1800003600 "North\n$2 B"
#> 2 South  Dist-2    300001600 "Dist-2\n$300 M" 2700002400 "South\n$3 B"
#> 3 North  Dist-3    600001200 "Dist-3\n$600 M" 1800003600 "North\n$2 B"
#> 4 South  Dist-4    900000800 "Dist-4\n$900 M" 2700002400 "South\n$3 B"
#> 5 North  Dist-5   1200000400 "Dist-5\n$1.2 B" 1800003600 "North\n$2 B"
#> 6 South  Dist-6   1500000000 "Dist-6\n$1.5 B" 2700002400 "South\n$3 B"

评论

0赞 ScottyJ 1/6/2023
这看起来像我需要的。我还没有尝试过,但您的回答和@LionelHenry链接非常有帮助。谢谢。