将dplyr group_by / summaryize / spread转换为函数

时间:2019-08-14 12:09:03

标签: dplyr tidyverse tidyr rlang

我有以下数据(一些组织,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
  )

我想总结每个变量并生成一个宽表,以便每个组织只有一个记录。到目前为止,我的方法是使用需要更改的重复代码, -用要汇总的变量的名称汇总部分以及应使用该变量处理的函数, -展开部分-值=更改新列的名称, -named_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)
}

如何正确地将列名和过程中要使用的函数(例如均值,总和,中位数,标准差)注入到该函数中。

1 个答案:

答案 0 :(得分:1)

您非常亲密。问题是列名的组成,我将其拖到另一行中:

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>