把 dplyr group_by/summarize/spread 变成一个函数

Turn dplyr group_by/summarize/spread into a function

我有以下数据(一些组织、kpi 测量值、一长串变量(我在示例中给出了两个)。

   df <- tibble::tribble(
  ~ORG_NM, ~KPI_NM,        ~NUMR_VAL,       ~DENO_VAL,
    "AAA",   "xxx",                8,              10,
    "AAA",   "xxx",               10,              10,
    "BBB",   "xxx",                1,               7,
    "CCC",   "xxx",                9,               3,
    "CCC",   "yyy",                9,               4,
    "DDD",   "xxx",                1,               7,
    "AAA",   "yyy",                8,               3,
    "BBB",   "yyy",                6,               1
  )

我想总结每个变量并生成一个宽 table 以便每个组织只有一个记录。到目前为止,我的方法是使用我需要更改的重复代码, - 使用要汇总的变量名称和处理该变量的函数来汇总部分, - spread part - value = 更改新列的名称, - rename_at - 为所有具有值的展开列添加有意义的后缀,显示使用什么函数来实现它们。 最后,我需要更改 full_join 中数据框的名称以附加新列。

library(tidyverse)    


df_numrtr <- df %>%    
  group_by(ORG_NM, KPI_NM) %>%    
  summarise(mean_NUM_VAL = mean(NUMR_VAL)) %>%    
  spread(key = c(KPI_NM),  mean_NUM_VAL) %>%      
  ungroup() %>%    
  rename_at(vars(-ORG_NM), function(x) paste0(x, "_num_mean"))


df_denom  <- df %>%   
  group_by(ORG_NM, KPI_NM) %>%    
  summarise(mean_DENOM_VAL = mean(DENO_VAL)) %>%    
  spread(key = c(KPI_NM),  mean_DENOM_VAL) %>%      
  ungroup() %>%    
  rename_at(vars(-ORG_NM), function(x)    
    paste0(x, "_den_mean"))



df_final <-    
  df_numrtr %>%     
  full_join(df_denom) %>%     
  select(ORG_NM, sort(names(.))) 

  ORG_NM xxx_den_mean xxx_num_mean yyy_den_mean yyy_num_mean
  <chr>         <dbl>        <dbl>        <dbl>        <dbl>
1 AAA              10            9            3            8
2 BBB               7            1            1            6
3 CCC               3            9            4            9
4 DDD               7            1           NA           NA

我想摆脱重复的代码,并拥有一个函数,该函数将接受一个变量的名称和一个函数。我想要的伪代码函数看起来像

fnSummarize <- function(df, my_org_var, my_kpi_var, my_var, my_fun ){
  df_output<-df %>%    
    group_by({{my_groupby_var}}) %>%    
    summarise(paste0({{my_var}},"_",{{my_fun}}) = my_fun({{my_var}})) %>%    
    spread(key = {{my_kpi_var}},  paste0(my_var, my_fun)) %>%      
    ungroup()  %>% 
    rename_at(vars(-{{ my_org_var}}), function(x) paste0(x, {{myfun}}))
  return(df_output)
}

如何正确地将列名和要在该过程中使用的函数(如均值、求和、中值、sd)注入此类函数。

你很接近。问题是列名的组成,我把它拉到一个单独的行中:

fnSummarize <- function(df, my_org_var, my_kpi_var, my_var, my_fun ){
  colName <- str_c( rlang::enexpr(my_var),"_",rlang::enexpr(my_fun) )

  df %>%
    group_by( {{my_org_var}}, {{my_kpi_var}} ) %>%
    summarise( !!colName := {{my_fun}}({{my_var}}) ) %>%
    spread( key = {{my_kpi_var}}, colName ) %>%
    ungroup() %>%
    rename_at( vars(-{{my_org_var}}), str_c, "_", colName )
}

列名称与您的 df_numrtrdf_denom 略有不同,但这可以通过额外的字符串操作轻松调整。为了保持清洁,我把它留了下来。

fnSummarize( df, ORG_NM, KPI_NM, NUMR_VAL, mean )
# # A tibble: 4 x 3
#    ORG_NM xxx_NUMR_VAL_mean yyy_NUMR_VAL_mean
#    <chr>              <dbl>             <dbl>
#  1 AAA                    9                 8
#  2 BBB                    1                 6
#  3 CCC                    9                 9
#  4 DDD                    1                NA

## Demonstrating using sum instead of mean
fnSummarize( df, ORG_NM, KPI_NM, DENO_VAL, sum )
#  # A tibble: 4 x 3
#    ORG_NM xxx_DENO_VAL_sum yyy_DENO_VAL_sum
#    <chr>             <dbl>            <dbl>
#  1 AAA                  20                3
#  2 BBB                   7                1
#  3 CCC                   3                4
#  4 DDD                   7               NA

我还想指出,您可以通过纯粹的 dplyr 操作来解决您的任务,而不需要 rlang。例如,以下是同时应用 summean 的方式:

df %>% group_by( ORG_NM, KPI_NM ) %>%
  summarize_at( c("NUMR_VAL", "DENO_VAL"), list(mean=mean,sum=sum) ) %>%
  ungroup() %>% gather( "Variable", "Value", -ORG_NM, -KPI_NM ) %>% 
  mutate( Variable = map2_chr(Variable, KPI_NM, ~str_replace(.x,"VAL",.y)) ) %>%
  select( -KPI_NM ) %>% spread( Variable, Value )
# # A tibble: 4 x 9
#    ORG_NM DENO_xxx_mean DENO_xxx_sum DENO_yyy_mean DENO_yyy_sum NUMR_xxx_mean
#    <chr>          <dbl>        <dbl>         <dbl>        <dbl>         <dbl>
#  1 AAA               10           20             3            3             9
#  2 BBB                7            7             1            1             1
#  3 CCC                3            3             4            4             9
#  4 DDD                7            7            NA           NA             1
#  # … with 3 more variables: NUMR_xxx_sum <dbl>, NUMR_yyy_mean <dbl>,
#  #   NUMR_yyy_sum <dbl>