ITPub博客

首页 > > > R语言dplyr的高级技巧

R语言dplyr的高级技巧

作者:浪花小雨滴 时间:2020-06-17 14:10:26 0 删除 编辑

最近独立完成了一个用R语言实现的离线报表,想和大家分享一下实现过程中dplyr的一些技巧。

什么是dplyr?

dplyr是一个利用pipline语法快速处理数据的R语言包。

1.抽象公共过程单独调用

标题大家可能比较难懂,下面我来举个例子:

在做数据处理的时候大家都应该这样写过吧:

result <- data %>% 
  mutate(
    var1 = fun(var)
    ... 
  ) %>% 
  group_by(var2, var3) %>% 
  summarise(
    ...
  ) %>% 
  arrange(var2, var3, var4)

然后下面可能又来一段

result1 <- data %>% 
  filter(...) %>% 
  mutate(
    var1 = fun(var)
    ... 
  ) %>% 
  group_by(var2, var3) %>% 
  summarise(
    ...
  ) %>% 
  arrange(var2, var3, var4)

仅仅只是增加了一个filter,但是我们的代码行数确翻了一番。这样写还有一个严重的问题就是当mutate或者其他地方代码要加一点东西,就得在每一个地方都要加上,这是很麻烦的一个问题,那我们怎么解决这个问题呢?很简单,可能你已经想到了就是将这一部分代码封装成一个函数。我们来看低级版本:

my_fun <- function(.data){
  .data %>% 
  mutate(
    var1 = fun(var)
    ... 
  ) %>% 
    group_by(var2, var3) %>% 
    summarise(
      ...
    ) %>% 
    arrange(var2, var3, var4)
}

result <- data %>% 
  my_fun(.) # 这里.指代data 在执行代码的时候就会将data传入进来

result1 <- data %>% 
  filter(...) %>% 
  my_fun(.)

2.将可变转换为函数参数

到这里,你可能会问虽然mutate,summarise的计算不会变,但我group_by,arrange里面的变量可能会变。比如我先是group_by 年份,第二个我要group_by 月份,那怎么办呢?这就要涉及一些理论知识了,不过在这里我就直接给出实现方法,看后面如果大家感兴趣也可以另外写一篇。

my_fun <- function(.data, .group_var, .arrange_var){
  .group_var <- 
    rlang::syms(.group_var)
  .arrange_var <- 
    rlang::syms(.arrange_var)
  
  .data %>% 
  mutate(
    var1 = fun(var)
    ... 
  ) %>% 
    group_by(!!!.group_var) %>% 
    summarise(
      ...
    ) %>% 
    arrange(!!!.arrange_var)
}

result <- data %>% 
  my_fun(., 
         .group_var = c('var1', 'var2'),
         .arrange_var =  c('var1', 'var3'))

result1 <- data %>% 
  filter(...) %>% 
  my_fun(.,
         .group_var = c('var1', 'var2', 'var3'),
         .arrange_var =  c('var1', 'var4')) %>% 
  group_by_()

大功告成,就是这样。大家可以自己写一下。当然这种语法也是支持select等其他函数的。可能到这里,还是觉得那万一summarise,mutate里面的也不一样怎么办,或者说我希望变量名跟着我的计算的变量名变,我就直接给官方的代码了。

my_mutate <- function(df, expr) {
  expr <- enquo(expr)
  mean_name <- paste0("mean_", quo_name(expr))
  sum_name <- paste0("sum_", quo_name(expr))

  mutate(df,
    !! mean_name := mean(!! expr),
    !! sum_name := sum(!! expr)
  )
}

my_mutate(df, a)
#> # A tibble: 5 x 6
#>      g1    g2     a     b mean_a sum_a
#>   <dbl> <dbl> <int> <int>  <dbl> <int>
#> 1     1     1     5     4      3    15
#> 2     1     2     3     2      3    15
#> 3     2     1     4     1      3    15
#> 4     2     2     1     3      3    15
#> # … with 1 more row

但是笔者不建议这样做,因为考虑既然一个共性的逻辑抽象出来封装成函数,就需要保证一个函数中实现的逻辑不要太复杂,如果连计算的变量或者计算的内容都不一样了,比如一个计算均值,一个求和。那我建议还是分开写,这样代码也会清晰一些。下面给出笔者认为较合理的实现思路。假设我们有一段公共的逻辑,先产生一些新的变量,然后分组求和再排序。按照先前的就是写成这样。

my_fun <- function(.data, .group_var, .arrange_var){
  .group_var <- 
    rlang::syms(.group_var)
  .arrange_var <- 
    rlang::syms(.arrange_var)
  
  .data %>% 
  mutate(
    var1 = fun(var)
    ... 
  ) %>% 
    group_by(!!!.group_var) %>% 
    summarise(
      sum_var = sum(var1),
      sum_var2 = sum(var2),
    ) %>% 
    arrange(!!!.arrange_var)
}

其中mutate中计算依赖原始数据var这个变量,summarise中的sum_var2 依赖原始数据中的var2。那如果我们输入的数据有这两个变量只是名字不一样,此时我们可以这样:

result <- data %>% 
  rename(var = varxx, var2 = varxx2) %>% 
  my_fun(., 
         .group_var = c('var1', 'var2'),
         .arrange_var =  c('var1', 'var3'))

反正思想就是利用管道函数特性,在函数前,函数后增加转换逻辑。如果又发现共性的地方,就又可以将其封装成新的函数,然后再链式调用。

3.结语

至此,也就给大家介绍了一下针对dplyr处理数据的一种思路,如果感兴趣的同学也可以看看这个链接Programming with dplyr。后一篇我会介绍如何在生产环境下写出高质量工程化的R项目。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31537575/viewspace-3146215/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-05-16

  • 博文量
    4259
  • 访问量
    39968