R for data science生信修炼R技巧

关于 dplyr 1.0.0 出来后我想分享的一些东西

2020-06-06  本文已影响0人  热衷组培的二货潜

于 2020-05-29 那天,期盼已久的 dplyr 1.0.0 终于出来了(emm,鸽了半个月)。

dplyr 在出 1.0.0 版本之前不久,于 hadely 在 twitter 发文 dplyr 发布推迟半个月到 29 号,同时也终于把那黄不拉几的 logo 换成了一个更炫目的 logo,新 logo 还是蛮好看的。

不过我还是喜欢粉笔画版本的这个。好了,闲扯就这么多吧,我反正就记得了这个鸽了半个月。

关于 dplyr 1.0.0 的几个我的笔记:

dplyr 1.0.0 出来了,我也该推一波相关资源了。

我想推荐的几本围绕 《R for data science》相关的几本书

我想推荐的几篇 dplyr 博文:

参考资源:


dplyr 1.0.0 小结

那么这一次 dplyr 1.0.0 更新后多了些什么内容呢?又带了怎样更便捷的操作。请允许我一一道来。

dplyr 包中有哪些核心函数呢?

下面我们来逐一介绍。

select()

rename()

mutate()

filter

可以通过布尔运算筛选符合条件的行

summarise()

汇总函数。一般结合 group_by()across()数学统计运算函数自定义函数 等连用。

arrange

*_join()

relocate()

slice()

top_n()、 sample_n()、 sample_frac() 这三个函数已经被 slice 新增的子函数所替代

其中 slice_head()slice_sample() 中新增了参数 n =prop =n 表示多上行,prop 表示所占数据行的比例。相当于函数 sample_n()sample_frac()

top_n 被函数 slice_min()slice_max() 所替代

across

across(.cols = everything(), .fns = NULL, ..., .names = NULL)

为什么我们要多使用 across()

注意:across() 函数不能与 select()rename() 函数连用,因为他们已经使用了选择的语法,我们如果想要使用函数来改变列名那么就需要使用函数 rename_with()

本次更新最为重要的一个函数。所有 *_if()、 *_at()、 *_all() 变体函数都已经被 across() 函数所取代,使得所有列进行相同操作更为便捷。

怎么转换我们之前基于 _at、_if、_all 等后缀的函数处理为 across()

across() 与其他函数连用

df %>% mutate_if(is.numeric, log)
df %>% mutate(across(where(is.numeric), log))

rescale01 <- function(x){
  rng <- range(x, na.rm = T)
  (x - rng[1])/(rng[2] - rng[1])
}

df <- tibble(x = 1:4, y = rnorm(4))

df %>%
  mutate(across(where(is.numeric), rescale01))
## # A tibble: 4 x 2
##       x     y
##   <dbl> <dbl>
## 1 0     0    
## 2 0.333 0.291
## 3 0.667 0.207
## 4 1     1
# 选择字符串列进行统计长度信息
starwars %>%
  summarise(across(where(is.character), ~length(unique(.x))))

# 选取数值列,进行求均值
starwars %>%
  group_by(homeworld) %>%
  filter(n() > 1) %>%
  summarise(across(where(is.numeric), ~ mean(.x, na.rm = T)))
starwars %>%
  count(across(contains("color")), sort = TRUE)
starwars %>%
  distinct(across(contains("color")))
# 查找所有没有缺失值 NA 的列
starwars %>%
  filter(across(everything(), ~ !is.na(.x)))
min_max <- list(
  min = ~min(.x, na.rm = T),
  max = ~max(.x, na.rm = T)
)

starwars %>%
  summarise(across(where(is.numeric), min_max))


# 怎么控制输出结果列名呢?
# 使用 glue 包
# {fn} 表示使用的函数名,{col} 表示操作的列名
starwars %>%
  summarise(across(where(is.numeric), min_max, .names = "{fn}.{col}"))
## # A tibble: 1 x 6
##   min.height max.height min.mass max.mass min.birth_year max.birth_year
##        <int>      <int>    <dbl>    <dbl>          <dbl>          <dbl>
## 1         66        264       15     1358              8            896

# 如果我们想要将同样函数处理的数据放置于一起,我们就需要将函数分开
# 我们可以看到结果是很奇怪的。
starwars %>%
  summarise(across(where(is.numeric), ~min(.x, na.rm = T), .names = "min.{col}"),
            across(where(is.numeric), ~max(.x, na.rm = T), .names = "max.{col}"))
## # A tibble: 1 x 9
##   min.height min.mass min.birth_year max.height max.mass max.birth_year
##        <int>    <dbl>          <dbl>      <int>    <dbl>          <dbl>
## 1         66       15              8        264     1358            896
## # ... with 3 more variables: max.min.height <int>, max.min.mass <dbl>,
## #   max.min.birth_year <dbl>

总之这是一个非常重要的函数。但是以下几种情况需要注意:

df <- data.frame(x = c(1, 2, 3), y = c(1, 4, 9))
df %>%
  summarise(n = n(), across(where(is.numeric), sd))
##    n x        y
## 1 NA 1 4.041452

# 可看到这里 n() 统计结果为 NA,因为 n 为一个数值,所以后面 across() 计算了他的 sd 值,3 的 sd 值为 NA,如果我们想解决这一个问题,我们就需要将 n() 统计放置于 across() 函数处理之后
df %>%
  summarise(across(where(is.numeric), sd), n = n())
##   x        y n
## 1 1 4.041452 3

# 还有另外一种方法,即在 across() 函数中加上一个条件 !n
df %>%
  summarise(n = n(), across(where(is.numeric) & !n, sd))
##   n x        y
## 1 3 1 4.041452

rowwise()

在 R 中 dplyr 通常是对列进行操作,然而对于行处理方面还是比较困难, rowwise()函数来对数据进行行处理,常与 c_across() 连用。

本节中列举了三个常见的案例:

当然这些问题我们可以通过类似 for 等循环来进行操作,但是我们可以通过管道的形式进行更便捷的操作,这里作者有一句经典的话:

Of course, someone has to write loops. It doesn’t have to be you. — Jenny Bryan

rowwise 按行来进行分组,和 group_by() 函数一样,并不会改变数据得内容,仅仅是进行分组:

df <- tibble(x = 1:2, y = 3:4, z = 5:6)
df %>% rowwise()
# 可以看到下面中多一个表示符号:Rowwise
## # A tibble: 2 x 3
## # Rowwise: 
##       x     y     z
##   <int> <int> <int>
## 1     1     3     5
## 2     2     4     6

# 计算的是数据中所有的数值的平均值
df %>% mutate(m = mean(c(x, y, z)))
## # A tibble: 2 x 4
##       x     y     z     m
##   <int> <int> <int> <dbl>
## 1     1     3     5   3.5
## 2     2     4     6   3.5

# 计算每一列的平均值
df %>% mutate(across(everything(), ~mean(.x, na.rm = T)))
## # A tibble: 2 x 3
##       x     y     z
##   <dbl> <dbl> <dbl>
## 1   1.5   3.5   5.5
## 2   1.5   3.5   5.5

# 计算的是每一行的平均值
df %>% rowwise() %>% mutate(m = mean(c(x, y, z)))
## # A tibble: 2 x 4
## # Rowwise: 
##       x     y     z     m
##   <int> <int> <int> <dbl>
## 1     1     3     5     3
## 2     2     4     6     4
df <- tibble(name = c("Mara", "Hadley"), x = 1:2, y = 3:4, z = 5:6)

# 结果仅仅只有值
df %>% 
  rowwise() %>% 
  summarise(m = mean(c(x, y, z)))
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 2 x 1
##       m
##   <dbl>
## 1     3
## 2     4


# 可以通过加上需要处理的行作为 summarise() 的行名,可以使用 `rowwise(name)`,保留 `name` 列
df %>% 
  rowwise(name) %>% 
  summarise(m = mean(c(x, y, z)))
## `summarise()` regrouping output by 'name' (override with `.groups` argument)
## # A tibble: 2 x 2
## # Groups:   name [2]
##   name       m
##   <chr>  <dbl>
## 1 Mara       3
## 2 Hadley     4


df <- tibble(id = 1:6, w = 10:15, x = 20:25, y = 30:35, z = 40:45)
df
## # A tibble: 6 x 5
##      id     w     x     y     z
##   <int> <int> <int> <int> <int>
## 1     1    10    20    30    40
## 2     2    11    21    31    41
## 3     3    12    22    32    42
## 4     4    13    23    33    43
## 5     5    14    24    34    44
## 6     6    15    25    35    45
# 使用 `rowwise` 对数据进行行分组 
rf <- df %>% rowwise(id)

rf %>% mutate(total = sum(c(w, x, y, z)))
## # A tibble: 6 x 6
## # Rowwise:  id
##      id     w     x     y     z total
##   <int> <int> <int> <int> <int> <int>
## 1     1    10    20    30    40   100
## 2     2    11    21    31    41   104
## 3     3    12    22    32    42   108
## 4     4    13    23    33    43   112
## 5     5    14    24    34    44   116
## 6     6    15    25    35    45   120
rf %>% summarise(total = sum(c(w, x, y, z)))
## `summarise()` regrouping output by 'id' (override with `.groups` argument)
## # A tibble: 6 x 2
## # Groups:   id [6]
##      id total
##   <int> <int>
## 1     1   100
## 2     2   104
## 3     3   108
## 4     4   112
## 5     5   116
## 6     6   120

c_across

常常与 rowwise() 函数连用,行处理中的 across()

rf <- tibble(id = 1:6, w = 10:15, x = 20:25, y = 30:35, z = 40:45) %>% rowwise(id)

rf %>% mutate(total = sum(c_across(w:z)))
## # A tibble: 6 x 6
## # Rowwise:  id
##      id     w     x     y     z total
##   <int> <int> <int> <int> <int> <int>
## 1     1    10    20    30    40   100
## 2     2    11    21    31    41   104
## 3     3    12    22    32    42   108
## 4     4    13    23    33    43   112
## 5     5    14    24    34    44   116
## 6     6    15    25    35    45   120
rf %>% mutate(total = sum(c_across(where(is.numeric))))
## # A tibble: 6 x 6
## # Rowwise:  id
##      id     w     x     y     z total
##   <int> <int> <int> <int> <int> <int>
## 1     1    10    20    30    40   100
## 2     2    11    21    31    41   104
## 3     3    12    22    32    42   108
## 4     4    13    23    33    43   112
## 5     5    14    24    34    44   116
## 6     6    15    25    35    45   120

ungroup() 取消分组,这里表示取消按照行进行分组

rf %>% 
  mutate(total = sum(c_across(w:z))) %>% 
  ungroup() %>% 
  mutate(across(w:z, ~ . / total))
## # A tibble: 6 x 6
##      id     w     x     y     z total
##   <int> <dbl> <dbl> <dbl> <dbl> <int>
## 1     1 0.1   0.2   0.3   0.4     100
## 2     2 0.106 0.202 0.298 0.394   104
## 3     3 0.111 0.204 0.296 0.389   108
## 4     4 0.116 0.205 0.295 0.384   112
## 5     5 0.121 0.207 0.293 0.379   116
## 6     6 0.125 0.208 0.292 0.375   120

内置行处理函数更快,对行进行操作,没有分成行、然后统计,最后连接到一起。

df %>% mutate(total = rowSums(across(where(is.numeric))))
## # A tibble: 6 x 6
##      id     w     x     y     z total
##   <int> <int> <int> <int> <int> <dbl>
## 1     1    10    20    30    40   101
## 2     2    11    21    31    41   106
## 3     3    12    22    32    42   111
## 4     4    13    23    33    43   116
## 5     5    14    24    34    44   121
## 6     6    15    25    35    45   126

df %>% mutate(mean = rowMeans(across(where(is.numeric))))
## # A tibble: 6 x 6
##      id     w     x     y     z  mean
##   <int> <int> <int> <int> <int> <dbl>
## 1     1    10    20    30    40  20.2
## 2     2    11    21    31    41  21.2
## 3     3    12    22    32    42  22.2
## 4     4    13    23    33    43  23.2
## 5     5    14    24    34    44  24.2
## 6     6    15    25    35    45  25.2

rowwise() 不仅适用于返回长度为 1 的向量的函数; 如果结果是一个列表,它可以与任何函数一起连用。这意味着 rowwise()mutate() 提供了一种优雅的方法,可以多次使用不同的参数调用函数,将输出存储在输入旁边。

一定要用 list() 函数来将命令括起来,比如 list(runif(n, min, max)) 而非 runif(n, min, max)

df <- tribble(
  ~ n, ~ min, ~ max,
    1,     0,     1,
    2,    10,   100,
    3,   100,  1000,
)

df %>% 
  rowwise() %>% 
  mutate(data = list(runif(n, min, max)))
## # A tibble: 3 x 4
## # Rowwise: 
##       n   min   max data     
##   <dbl> <dbl> <dbl> <list>   
## 1     1     0     1 <dbl [1]>
## 2     2    10   100 <dbl [2]>
## 3     3   100  1000 <dbl [3]>
# 这里就会得到  3*3 九种结果
df <- expand.grid(mean = c(-1, 0, 1), sd = c(1, 10, 100))

df %>% 
  rowwise() %>% 
  mutate(data = list(rnorm(10, mean, sd)))
df <- tribble(
   ~rng,     ~params,
   "runif",  list(n = 10), 
   "rnorm",  list(n = 20),
   "rpois",  list(n = 10, lambda = 5),
) %>%
  rowwise()

df %>% 
  mutate(data = list(do.call(rng, params)))
## # A tibble: 3 x 3
## # Rowwise: 
##   rng   params           data      
##   <chr> <list>           <list>    
## 1 runif <named list [1]> <dbl [10]>
## 2 rnorm <named list [1]> <dbl [20]>
## 3 rpois <named list [2]> <int [10]>

nest_by() 分组存储为一个 list

by_cyl <- mtcars %>% nest_by(cyl)
by_cyl
## # A tibble: 3 x 2
## # Rowwise:  cyl
##     cyl                data
##   <dbl> <list<tbl_df[,10]>>
## 1     4           [11 x 10]
## 2     6            [7 x 10]
## 3     8           [14 x 10]

按行线性建模

mods <- by_cyl %>% mutate(mod = list(lm(mpg ~ wt, data = data)))
mods
## # A tibble: 3 x 3
## # Rowwise:  cyl
##     cyl                data mod   
##   <dbl> <list<tbl_df[,10]>> <list>
## 1     4           [11 x 10] <lm>  
## 2     6            [7 x 10] <lm>  
## 3     8           [14 x 10] <lm>
mods <- mods %>% mutate(pred = list(predict(mod, data)))
mods
## # A tibble: 3 x 4
## # Rowwise:  cyl
##     cyl                data mod    pred      
##   <dbl> <list<tbl_df[,10]>> <list> <list>    
## 1     4           [11 x 10] <lm>   <dbl [11]>
## 2     6            [7 x 10] <lm>   <dbl [7]> 
## 3     8           [14 x 10] <lm>   <dbl [14]>

dplyr 简介

这次对于 dplyr 包函数更新了一个很重要的说明参考文件书,主要分为以下几个方面,方便我们系统的去学习(本文大多数例子也是从中而来)。

dplyr 简介,是学习 dplyr 包主要功能的最佳选择地方,没有之一,其中包括以下几个方面:

上一篇 下一篇

猜你喜欢

热点阅读