首页 > > > R语言dplyr的高级技巧
最近独立完成了一个用R语言实现的离线报表,想和大家分享一下实现过程中dplyr的一些技巧。
dplyr是一个利用pipline语法快速处理数据的R语言包。
标题大家可能比较难懂,下面我来举个例子:
在做数据处理的时候大家都应该这样写过吧:
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(.)到这里,你可能会问虽然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'))反正思想就是利用管道函数特性,在函数前,函数后增加转换逻辑。如果又发现共性的地方,就又可以将其封装成新的函数,然后再链式调用。
至此,也就给大家介绍了一下针对dplyr处理数据的一种思路,如果感兴趣的同学也可以看看这个链接Programming with dplyr。后一篇我会介绍如何在生产环境下写出高质量工程化的R项目。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31537575/viewspace-3146215/ ,如需转载,请注明出处,否则将追究法律责任。