【问题标题】:R function: using argument as a character variable and column nameR函数:使用参数作为字符变量和列名
【发布时间】:2021-12-17 12:39:15
【问题描述】:

我有一些这种格式的数据:

#> # A tibble: 3 × 5
#>    item  cost  blue  pink black
#>   <int> <int> <int> <int> <int>
#> 1     1     4     1     0     1
#> 2     2    10     1     0     1
#> 3     3     3     0     1     1

我想要的输出是颜色列的相对频率。项目可以有不止一种颜色,因此相对频率不必总和为 1。

#>    color  rel_freq  
#>   <int> <int> 
#> 1  blue  0.66  
#> 2  pink  0.33  
#> 2  black 1.00  

我可以手动处理一种颜色,比如蓝色:

library(tidyverse)
df <- tibble::tribble(
  ~item, ~cost, ~blue, ~pink, ~black,
     1L,    4L,    1L,    0L,     1L,
     2L,   10L,    1L,    0L,     1L,
     3L,    3L,    0L,    1L,     1L
  )

df %>% 
  group_by(blue) %>% 
  summarise(count = n()) %>% 
  mutate(rel_freq = (count/sum(count)*100) ) %>%
  filter(blue==1) %>% 
  mutate(color = deparse(substitute(blue))) %>% 
  select(-blue, -count) %>% 
  select(color, everything())
  

给了

#>   color rel_freq
#>   <chr>    <dbl>
#> 1 blue      66.7

但是当我把它放到一个函数中时,我不知道如何传入一个参数,以便它可以被视为一个列(使用“curly-curly”符号)和一个字符变量(这就是我被卡住了)。

calc_rel_freq <- function(input_color){
df %>% 
  group_by({{input_color}}) %>% 
  summarise(count = n()) %>% 
  mutate(rel_freq = (count/sum(count)*100) ) %>%
  filter({{input_color}}==1) %>% 
  mutate(color = deparse(substitute({{input_color}}))) %>% # This is where I'm stuck.
  select(-{{input_color}}, -count) %>% 
  select(color, everything())
}
calc_rel_freq(blue)

我的最终目标是能够使用这样的函数:

input_colors <- c("blue", "pink", "black")
map(input_colors, calc_relative_freq)

数据输入代码如下:

library(tidyverse)
df <- tibble::tribble(
  ~item, ~cost, ~blue, ~pink, ~black,
     1L,    4L,    1L,    0L,     1L,
     2L,   10L,    1L,    0L,     1L,
     3L,    3L,    0L,    1L,     1L
  )

df

【问题讨论】:

    标签: r dplyr tidyverse purrr


    【解决方案1】:

    由于您想要的输出是相对频率,您可以更直接地做到这一点

    df %>% 
      select(-cost) %>% 
      pivot_longer(blue:black) %>% 
      group_by(name) %>% 
      summarize(rel_freq=mean(value))
    #   name  rel_freq
    #   <chr>    <dbl>
    # 1 black    1    
    # 2 blue     0.667
    # 3 pink     0.333
    

    如果你真的只想要一个,你可以在最后filter()

    【讨论】:

      【解决方案2】:

      我们可以将 dplyr 与 across 一起使用。对于所有答案,如果需要,我们可以轻松地pivot_longer 输出。

      library(dplyr)
      df %>% summarise(across(blue:black, mean))
      
      # A tibble: 1 × 3
         blue  pink black
        <dbl> <dbl> <dbl>
      1 0.667 0.333     1
      

      all_of

      across 还可以使用all_of 选择帮助器处理选定列的名称向量:

      library(dplyr)
      
      input_colors <- c("blue", "pink", "black")
      
      df %>% summarise(across(all_of(input_colors), mean))
      

      带有sym和双刘海(!!

      如果我们真的想使用非标准评估来使用字符元素作为 dplyr 函数中的列选择,我们可以使用 rlang 包转换为符号 (sym) 并评估 (!!):

      library(dplyr)
      library(purrr)
      library(rlang)
      
      map_dfc(input_colors, ~df %>% summarise(across(!!(sym(.x)), mean)))
      
      # A tibble: 1 × 3
         blue  pink black
        <dbl> <dbl> <dbl>
      1 0.667 0.333     1
      

      作为替代方案,我们可以先创建一个符号列表

      my_symbols<-input_colors %>% map(sym)
      

      然后使用across的循环:

      map_dfc(my_symbols, ~ df %>% summarise(across(.x, mean)))
      

      在用户定义的函数中使用双花括号 ({{}})

      尽管有警告消息,但效果很好:

      calc_rel_freq<-function(df, variable){
          df %>% summarise(across({{variable}}, mean))
      }
      
      rel_freq(df, input_colors)
      
      Note: Using an external vector in selections is ambiguous.
      ℹ Use `all_of(input_colors)` instead of `input_colors` to silence this message.
      ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
      This message is displayed once per session.
      
      # A tibble: 1 × 3
         blue  pink black
        <dbl> <dbl> <dbl>
      1 0.667 0.333     1
      

      【讨论】:

        最近更新 更多