第 4 章 用tidyverse包做探索性数据分析(EDA)

4.1 总述

常用操作总结如下:

4.1.1 数据变换总结

对单个表(数据框)的结构进行变换,但不利用单元格进行运算,常见的操作包括:

  • 行去重:distinct(.data, …, .keep_all = FALSE)
  • 行筛选:filter()
  • 列筛选:select()
  • 行排序:arrange()
  • 长转宽:spread(data, key=键所在列, value=键值所在列)
  • 宽转长:gather(data, 需要转换的各列, key = “新建的键名”, value = “新建的键值名”)
  • 列合并:unite(data, col=新列, 需要合并的各列, sep = "_", remove = TRUE)
  • 列分裂:separate(data, col, into=c(“列1”,“列2”,…), sep = “[^[:alnum:]]+”)
  • 嵌套与取消嵌套:nest()、unnest()

4.1.2 多表融合总结

将两个或多个表,通过共同的列联结起来,常见的操作包括:

  • 左联结:left_join(x, y, by = NULL)
  • 右联结:right_join(x, y, by = NULL)
  • 内联结:inner_join(x, y, by = NULL)
  • 全联结:full_join(x, y, by = NULL)
  • 半联结:semi_join(x, y, by = NULL)
  • 反联结:anti_join(x, y, by = NULL)
  • 多个表的联结:list(x1, x2, x3,…) %>% reduce(*_join, by = NULL)

将具有相同列的两个表或多个表,对行进行集合操作

  • intersect(x, y, …)
  • union(x, y, …)
  • union_all(x, y, …)
  • setdiff(x, y, …)
  • setequal(x, y, …)
  • list(x1, x2, x3,…) %>% reduce(*_join, by = NULL)

4.1.3 增加新列总结

对原有的列进行运算,得到长度与数据行数相同的新列。

  • 分组与取消分组:group_by()、ungroup()
  • 增加新列:
    • mutate()
    • mutate_all(.tbl, .funs, …)
    • mutate_at(.tbl, .vars, .funs, …)
    • mutate_if(.tbl, .predicate, .funs, …)
  • 仅保留新列:
    • transmute()
    • transmute_all()
    • transmute_at()
    • transmute_if()

4.1.4 数据汇总总结

分组对列进行统计汇总,统计汇总函数一般含一个数据参数,这个参数为代表多个观测的向量,返回单个数值。当返回多个值时,可以将其转换为list.

  • 数据汇总(一般会与分组结合):
    • summarise()
    • summarise_all(.tbl, .funs, …)
    • summarise_if(.tbl, .predicate, .funs, …)
    • summarise_at(.tbl, .vars, .funs, …, .cols = NULL)
    • 所有列采用相同汇总函数时也可用map()、map_*(),但其无法和group_by结合
  • 对summarise系列函数,若返回长度大于1的向量,可用list将结果包围。

4.1.5 函数复用总结

  • 单变量
    • 返回列表:map(.x, .f, …)
    • 返回 * 类型的向量:map_*(.x, .f, …)(* 可以是lgl、chr、int、dbl、raw)
    • 得到输出、打印、保存等其他动作:walk()
    • 返回数据框:map_dfc(.x, .f, …), map_dfr(.x, .f, …, .id = NULL)
    • 条件筛选,返回列表:map_if(.x, .p, .f, …, .else = NULL)、map_at(.x, .at, .f, …)
  • 2维变量:map2(.x, .y, .f, …)、map2_*(.x, .y, .f, …)等(没有if和at)
  • p维变量:pmap(.l, .f, …)、pmap_*(.x, .y, .f, …)等(没有if和at)
  • 递归复用:map_depth(.x, .depth, .f, …, .ragged = FALSE)

4.1.6 可视化总结

ggplot图层:

ggplot(data = <DATA>) +
  <GEOM_FUNCTION>(
    mapping = aes(<MAPPINGS>),
    stat = <STAT>,
    position = <POSITION>
  ) +
  <COORDINATE_FUNCTION> +
  <FACET_FUNCTION> +
  <SCALE_FUNCTION> +
  <THEME_FUNCTION>
  • GEOM_FUNCTION
    • 离散变量频次:geom_bar
    • 连续变量频次:geom_histogram、geom_freqpoly、 geom_density
    • 离散vs连续:geom_boxplot、geom_violin、并排geom_histogram
    • 离散vs离散:geom_tile(热力图,颜色深浅表示频次),geom_count(点的大小表示频次)、并排geom_bar
    • 连续vs连续:geom_point、geom_bin2d(方形封箱)、geom_hex(6边形封箱)、geom_smooth、geom_line、geom_density2d
    • 3变量: geom_contour(aes(z = z))、geom_tile(aes(fill = z))、geom_raster(aes(fill = z))
  • MAPPINGS
    • x、y轴:x、y
    • 点或边框的颜色:color (颜色字符串)
    • 填充颜色:fill (颜色字符串)
    • 形状:shape(最多6种,代表形状的数字)
    • 点的大小、线的粗细:size (毫米为单位的数字)
    • 透明度:alpha
    • 线型:linetype(整数或字符串: 0 = blank, 1 = solid, 2 = dashed, 3 = dotted, 4 = dotdash, 5 = longdash, 6 = twodash)
    • 边框粗细:stoke
    • group
  • STAT
    • “count”
    • “bin”
    • “identity”
    • “boxplot”
  • POSITION
    • “stack”:堆积
    • “identity”:独立重叠
    • “dodge”:侧面并列
    • “fill”:堆积归一
    • “jitter”:扰动
    • “dodge2”
  • COORDINATE_FUNCTION
    • coord_flip:坐标翻转
    • coord_fixed(ratio = 1/2):固定坐标比例
    • coord_polar:极坐标
    • coord_cartesian:笛卡尔坐标(默认,可修改坐标范围)coord_cartesian(xlim = c(0, 5))
  • FACET_FUNCTION
    • 单变量分面:facet_wrap
    • 双变量分面:facet_grid
点的形状代码

点的形状代码

下面做更详细的说明:

library(tidyverse)  
library(nycflights13)  

4.2 数据变换

先看下面的例子:

flights %>% filter(month == 1| month == 12, day == 1&!(arr_delay > 120 | dep_delay > 120)) %>% 
  select(year:day,starts_with("arr"),ends_with("time"),contains("de"),-sched_dep_time) %>% 
  arrange(sched_arr_time,desc(arr_time,arr_delay),dep_delay) %>% 
  gather(year:day,key = "date_type",value = "date_value") %>% 
  spread(key = date_type,value = date_value) %>% 
  unite(col="date", year,month,day, sep = "-", remove = TRUE) %>% 
  separate(col=date, into=c("year","month","day"), sep = "-")
## # A tibble: 1,775 x 10
##    arr_time arr_delay dep_time sched_arr_time air_time dep_delay dest 
##       <int>     <dbl>    <int>          <int>    <dbl>     <dbl> <chr>
##  1     2337       -28     2035              5      333         5 LAX  
##  2       28        23     2306              5       59        21 ROC  
##  3        3        -4     1929              7      192         9 BQN  
##  4     2355       -13     2124              8      303         6 LAS  
##  5        4        -4     2242              8       57        -8 BUF  
##  6     2350       -20     2258             10       40        -2 BOS  
##  7       12         2     2134             10      134         5 MCO  
##  8       25        14     2134             11      151        29 FLL  
##  9       42        27     2206             15      312        35 LAS  
## 10       24         9     2310             15       57        15 BUF  
## # ... with 1,765 more rows, and 3 more variables: year <chr>, month <chr>,
## #   day <chr>

4.2.1 filter

filter进行行筛选,各逗号分隔的条件需同时满足,常用筛选条件有:

  • >, >=, <, <=, != , == ,near()(浮点数最好不用==而用near,因为可能存在舍入误差),is.na(), %in%, between(x, left, right)
  • &, |, !, xor()(异或)

4.2.2 select

select进行列筛选,各逗号分隔的条件任满足其一即可,常用辅助筛选函数有:

  • :(从a列到b列), - (除了这些列)
  • starts_with()、ends_with()、contains()、matches()、num_range()、one_of()、everything()、last_col()

4.2.3 arrange

arrange对行进行排序,可以有多个排序条件,desc()用于降序。

4.3 多表融合

join系列函数相当于保持对应关系的横向拼接,left_join、right_join、inner_join、full_join都比较好理解。

semi_join仅保留x中与y可以联结的部分,不保留y的数据。anti_join丢弃x中可以与y联结的部分,也不保留y的数据。

集合操作相当于保持对应关系的纵向拼接。

a <- tibble(x1 = c(1:5, 5,5),x2=letters[c(1:5,5,4)])
b <- tibble(x2=letters[c(3:5,5)],x1 = c(3:5, 4))
c <- tibble(x2=letters[10:12],x1 = c(10:12))
union(a, b) # 去重
## [[1]]
## [1] 1 2 3 4 5 5 5
## 
## [[2]]
## [1] "a" "b" "c" "d" "e" "e" "d"
## 
## [[3]]
## [1] "c" "d" "e" "e"
## 
## [[4]]
## [1] 3 4 5 4
union_all(a, b) #不去重,相当于rbind(a,b)
## # A tibble: 11 x 2
##       x1 x2   
##    <dbl> <chr>
##  1     1 a    
##  2     2 b    
##  3     3 c    
##  4     4 d    
##  5     5 e    
##  6     5 e    
##  7     5 d    
##  8     3 c    
##  9     4 d    
## 10     5 e    
## 11     4 e
intersect(a, b)
## Warning: Length of logical index must be 1 or 4, not 0
## # A tibble: 0 x 0
setdiff(a, b)
## # A tibble: 6 x 2
##      x1 x2   
##   <dbl> <chr>
## 1     1 a    
## 2     2 b    
## 3     3 c    
## 4     4 d    
## 5     5 e    
## 6     5 d
setequal(a, b)
## [1] FALSE
list(a,b,c) %>% reduce(union)
## [[1]]
## [1] 1 2 3 4 5 5 5
## 
## [[2]]
## [1] "a" "b" "c" "d" "e" "e" "d"
## 
## [[3]]
## [1] "c" "d" "e" "e"
## 
## [[4]]
## [1] 3 4 5 4
## 
## [[5]]
## [1] "j" "k" "l"
## 
## [[6]]
## [1] 10 11 12
list(a,b,c) %>% reduce(union_all) # 相当于bind_rows(a,b,c)
## # A tibble: 14 x 2
##       x1 x2   
##    <dbl> <chr>
##  1     1 a    
##  2     2 b    
##  3     3 c    
##  4     4 d    
##  5     5 e    
##  6     5 e    
##  7     5 d    
##  8     3 c    
##  9     4 d    
## 10     5 e    
## 11     4 e    
## 12    10 j    
## 13    11 k    
## 14    12 l

无论是join函数还是集合操作,默认都是对2个表的操作,想让这些2元操作符用在多个表上,都可以用reduce函数。上面最后一行给出了一个例子。

当你想用for循环产生一个行数未知的数据框时,如果每个循环都用rbind将原数据与新数据连起来,会较慢,因为会把原数据复制一遍。如果将每个新数据存为list中,然后统一用bind_rows或reduce(union),则可以加快运行速度。

4.4 增加新列

4.4.1 mutate

mutate函数的用法为 mutate(.data, …),它在 … 参数中输入新的计算表达式,这些计算结果会作为新列附加在原tibble之后。其中的表达式具有以下特点:

  • 数据框中的列可以直接用列名引用,而不必用df$var的形式写出
  • 表达式引用列名时,代表整列的数据参与运算,而非“该列中的单行数据参与运算,再做循环遍历整列”
  • 表达式可返回向量或列表,但返回结果的长度必须与tibble行数相同或为1(如果返回结果长度为1,将会被复制到每一行)
  • 可输入多个表达式,中间用逗号分隔,后面的表达式可以直接引用前面表达式的结果

下面为一个例子:

df <- tibble(x1=1:4,x2=5:8,c1=c("a","a","b","b"))
df %>% mutate(y1=sin(x1)*lag(x2)+cumprod(x1)*sum(x2),
              y2=sum(x1),
              y3=y1+length(c1),
              # y3_2=c(1,length(c1)),  # 长度必须与行数相同,或为1,此行会报错
              y4=as.integer(as.factor(c1)),
              y5=c(x1[2],x2[1]/2,x1[3]+x2[4],5),
              y6=list(x1[2],x2[1]/2,x1[3]+x2[4],"5")
              )
## # A tibble: 4 x 9
##      x1    x2 c1       y1    y2    y3    y4    y5 y6       
##   <int> <int> <chr> <dbl> <int> <dbl> <int> <dbl> <list>   
## 1     1     5 a      NA      10  NA       1   2   <int [1]>
## 2     2     6 a      56.5    10  60.5     1   2.5 <dbl [1]>
## 3     3     7 b     157.     10 161.      2  11   <int [1]>
## 4     4     8 b     619.     10 623.      2   5   <chr [1]>

如果新列的列名与原tibble中某列相同,会将原来的列覆盖掉。

如果需要增加多列,多列的结果来自于一个函数的列表,

add_diff_df <- function(x,y) {
  return(data.frame(add=x+y,diff=x-y))
}
dfm <- df %>% mutate(x3=map2(x1,x2,add_diff_df))
dfm %>% unnest()
## # A tibble: 4 x 5
##      x1    x2 c1      add  diff
##   <int> <int> <chr> <int> <int>
## 1     1     5 a         6    -4
## 2     2     6 a         8    -4
## 3     3     7 b        10    -4
## 4     4     8 b        12    -4

4.4.2 mutate与group_by

分组之后,mutate将针对分组后的局部数据分别运算。因而计算结果的长度必须对应于分组后局部数据的行数或为1.下面是一个例子,和上面的表达式类似,但是y5与y6都会出错,所有结果全部发生变化。特别地length函数将计算局部数据的长度。

df %>% group_by(c1) %>% 
   mutate(y1=sin(x1)*lag(x2)+cumprod(x1)*sum(x2),
          y2=sum(x1),
          y3=y1+length(c1),
          # y3_2=c(1,length(c1)),  # 长度必须与行数相同,或为1,此行会报错
          y4=as.integer(as.factor(c1))
          # y5=c(x1[2],x2[1]/2,x1[3]+x2[4],5),
          # y6=list(x1[2],x2[1]/2,x1[3]+x2[4],"5")
          )
## # A tibble: 4 x 7
## # Groups:   c1 [2]
##      x1    x2 c1       y1    y2    y3    y4
##   <int> <int> <chr> <dbl> <int> <dbl> <int>
## 1     1     5 a      NA       3  NA       1
## 2     2     6 a      26.5     3  28.5     1
## 3     3     7 b      NA       7  NA       1
## 4     4     8 b     175.      7 177.      1

4.4.3 mutate_all

在上面的mutate函数中,表达式是任意写的,但是难以做到批量化。mutate_all、mutate_if、mutate_at是使之批量化的3个函数。

mutate_all的用法为mutate_all(.tbl, .funs, …),.funs的形式为函数fun或~ fun(.)或其列表, …为函数额外的参数。函数输出可以是向量或列表,但长度必须为1或与tibble行数相同。

# 未指定名,默认以原变量名为新变量名,则会将原数据覆盖掉
df %>% mutate_all(cumsum)
## # A tibble: 4 x 3
##      x1    x2    c1
##   <int> <int> <dbl>
## 1     1     5    NA
## 2     3    11    NA
## 3     6    18    NA
## 4    10    26    NA
# 如果给出列名,则会在变量名之后添加该列名后缀,以下划线分隔
df %>% mutate_all(list(cumsum=cumsum))
## # A tibble: 4 x 6
##      x1    x2 c1    x1_cumsum x2_cumsum c1_cumsum
##   <int> <int> <chr>     <int>     <int>     <dbl>
## 1     1     5 a             1         5        NA
## 2     2     6 a             3        11        NA
## 3     3     7 b             6        18        NA
## 4     4     8 b            10        26        NA
# 如果有多个表达式,则默认在变量名后添加fnx后缀,其中x为函数的序号
df %>% mutate_all(list(cumsum,cumprod))
## # A tibble: 4 x 9
##      x1    x2 c1    x1_fn1 x2_fn1 c1_fn1 x1_fn2 x2_fn2 c1_fn2
##   <int> <int> <chr>  <int>  <int>  <dbl>  <dbl>  <dbl>  <dbl>
## 1     1     5 a          1      5     NA      1      5     NA
## 2     2     6 a          3     11     NA      2     30     NA
## 3     3     7 b          6     18     NA      6    210     NA
## 4     4     8 b         10     26     NA     24   1680     NA
# 但在仅有一个函数的列表中,不会添加(list(~cumsum(.))、~cumsum(.)也不会)
df %>% mutate_all(list(cumsum))
## # A tibble: 4 x 3
##      x1    x2    c1
##   <int> <int> <dbl>
## 1     1     5    NA
## 2     3    11    NA
## 3     6    18    NA
## 4    10    26    NA
# 多个函数,也可分别指定变量名后缀
df %>% mutate_all(list(cumsum=cumsum,cumprod=cumprod))
## # A tibble: 4 x 9
##      x1    x2 c1    x1_cumsum x2_cumsum c1_cumsum x1_cumprod x2_cumprod
##   <int> <int> <chr>     <int>     <int>     <dbl>      <dbl>      <dbl>
## 1     1     5 a             1         5        NA          1          5
## 2     2     6 a             3        11        NA          2         30
## 3     3     7 b             6        18        NA          6        210
## 4     4     8 b            10        26        NA         24       1680
## # ... with 1 more variable: c1_cumprod <dbl>
# 也可用~ fun(.)的形式,这时会直接以函数名为后缀
df %>% mutate_all(list(cumsum,~cumprod(.)))
## # A tibble: 4 x 9
##      x1    x2 c1    x1_fn1 x2_fn1 c1_fn1 x1_cumprod x2_cumprod c1_cumprod
##   <int> <int> <chr>  <int>  <int>  <dbl>      <dbl>      <dbl>      <dbl>
## 1     1     5 a          1      5     NA          1          5         NA
## 2     2     6 a          3     11     NA          2         30         NA
## 3     3     7 b          6     18     NA          6        210         NA
## 4     4     8 b         10     26     NA         24       1680         NA
# 表达式结果长度可以是1
df %>% mutate_all(list(length=length))
## # A tibble: 4 x 6
##      x1    x2 c1    x1_length x2_length c1_length
##   <int> <int> <chr>     <int>     <int>     <int>
## 1     1     5 a             4         4         4
## 2     2     6 a             4         4         4
## 3     3     7 b             4         4         4
## 4     4     8 b             4         4         4
# 表达式结果可以是list,其长度与tibble行数相同
rtlist1 <- function(x) {
  list(x[2],x[3],x[4],x[1])
}
df %>% mutate_all(list(reorder2=rtlist1))
## # A tibble: 4 x 6
##      x1    x2 c1    x1_reorder2 x2_reorder2 c1_reorder2
##   <int> <int> <chr> <list>      <list>      <list>     
## 1     1     5 a     <int [1]>   <int [1]>   <chr [1]>  
## 2     2     6 a     <int [1]>   <int [1]>   <chr [1]>  
## 3     3     7 b     <int [1]>   <int [1]>   <chr [1]>  
## 4     4     8 b     <int [1]>   <int [1]>   <chr [1]>
# 表达式结果可以是list,其长度为1
rtlist2 <- function(x) {
  list(c(x[2],x[3],x[4],x[1]))
}
df %>% mutate_all(list(reorder2=rtlist2))
## # A tibble: 4 x 6
##      x1    x2 c1    x1_reorder2 x2_reorder2 c1_reorder2
##   <int> <int> <chr> <list>      <list>      <list>     
## 1     1     5 a     <int [4]>   <int [4]>   <chr [4]>  
## 2     2     6 a     <int [4]>   <int [4]>   <chr [4]>  
## 3     3     7 b     <int [4]>   <int [4]>   <chr [4]>  
## 4     4     8 b     <int [4]>   <int [4]>   <chr [4]>
# 可以在函数列表之后,给出函数的其他参数
df %>% mutate_all(list(mean=mean),na.rm = T)
## Warning in mean.default(c1, na.rm = TRUE): argument is not numeric or
## logical: returning NA
## # A tibble: 4 x 6
##      x1    x2 c1    x1_mean x2_mean c1_mean
##   <int> <int> <chr>   <dbl>   <dbl>   <dbl>
## 1     1     5 a         2.5     6.5      NA
## 2     2     6 a         2.5     6.5      NA
## 3     3     7 b         2.5     6.5      NA
## 4     4     8 b         2.5     6.5      NA
# 对多个函数给出额外的参数,用~ fun(.)的形式
df %>% mutate_all(list(mean=~mean(.,na.rm = T),nth=~nth(.,n=3)))
## Warning in mean.default(c1, na.rm = T): argument is not numeric or logical:
## returning NA
## # A tibble: 4 x 9
##      x1    x2 c1    x1_mean x2_mean c1_mean x1_nth x2_nth c1_nth
##   <int> <int> <chr>   <dbl>   <dbl>   <dbl>  <int>  <int> <chr> 
## 1     1     5 a         2.5     6.5      NA      3      7 b     
## 2     2     6 a         2.5     6.5      NA      3      7 b     
## 3     3     7 b         2.5     6.5      NA      3      7 b     
## 4     4     8 b         2.5     6.5      NA      3      7 b

4.4.4 mutate_at

mutate_at用法为mutate_at(.tbl, .vars, .funs, …, .cols = NULL)。.cols参数已被弃用。比mutate_all多一个.vars参数,其他与mutate_all相同。它针对.vars参数指定的列,按照对应函数进行运算。可以通过字符串向量直接将列名赋给.vars参数,也可用select函数中的语法,这时需要在外面用vars()函数包围。

df %>% mutate_at(c("x1","x2"),list(mean=mean),na.rm = T)
## # A tibble: 4 x 5
##      x1    x2 c1    x1_mean x2_mean
##   <int> <int> <chr>   <dbl>   <dbl>
## 1     1     5 a         2.5     6.5
## 2     2     6 a         2.5     6.5
## 3     3     7 b         2.5     6.5
## 4     4     8 b         2.5     6.5
df %>% mutate_at(vars(c1,x1:x2),list(mean=mean),na.rm = T)
## Warning in mean.default(c1, na.rm = TRUE): argument is not numeric or
## logical: returning NA
## # A tibble: 4 x 6
##      x1    x2 c1    c1_mean x1_mean x2_mean
##   <int> <int> <chr>   <dbl>   <dbl>   <dbl>
## 1     1     5 a          NA     2.5     6.5
## 2     2     6 a          NA     2.5     6.5
## 3     3     7 b          NA     2.5     6.5
## 4     4     8 b          NA     2.5     6.5
df %>% mutate_at(vars(-c1),list(mean=mean),na.rm = T)
## # A tibble: 4 x 5
##      x1    x2 c1    x1_mean x2_mean
##   <int> <int> <chr>   <dbl>   <dbl>
## 1     1     5 a         2.5     6.5
## 2     2     6 a         2.5     6.5
## 3     3     7 b         2.5     6.5
## 4     4     8 b         2.5     6.5
df %>% mutate_at(vars(matches("x")),list(mean=mean),na.rm = T)
## # A tibble: 4 x 5
##      x1    x2 c1    x1_mean x2_mean
##   <int> <int> <chr>   <dbl>   <dbl>
## 1     1     5 a         2.5     6.5
## 2     2     6 a         2.5     6.5
## 3     3     7 b         2.5     6.5
## 4     4     8 b         2.5     6.5

4.4.5 mutate_if

mutate_if用法为mutate_if(.tbl, .predicate, .funs, …)。比mutate_all多一个.predicate(谓词函数)参数,其他与mutate_all相同。谓词函数参数为函数名,它的输入为某列的数据,输出为单个TRUE或FALSE,对于tibble中列作为输入,输出为TRUE的列,会被选择进行后面的函数运算。它在根据列的类型选择进行处理时特别有用,下面为一些例子:

# 不指定列名时,会将原列覆盖
df %>% mutate_if(is.character, as.factor)
## # A tibble: 4 x 3
##      x1    x2 c1   
##   <int> <int> <fct>
## 1     1     5 a    
## 2     2     6 a    
## 3     3     7 b    
## 4     4     8 b
df %>% mutate_if(is.integer, as.double)
## # A tibble: 4 x 3
##      x1    x2 c1   
##   <dbl> <dbl> <chr>
## 1     1     5 a    
## 2     2     6 a    
## 3     3     7 b    
## 4     4     8 b
# 指定列名后缀
df %>% mutate_if(is.integer, list(double=as.double))
## # A tibble: 4 x 5
##      x1    x2 c1    x1_double x2_double
##   <int> <int> <chr>     <dbl>     <dbl>
## 1     1     5 a             1         5
## 2     2     6 a             2         6
## 3     3     7 b             3         7
## 4     4     8 b             4         8

当分组时,如果mutate_all()或mutate_if()包含了分组变量,分组变量会被忽略。如果mutate_at()中包含了分组变量,会出错(?)。可以用 -group_cols() 将其去掉,例如

df %>% group_by(x1) %>% mutate_at(vars(x1:x2),list(log2=log2))
## # A tibble: 4 x 5
## # Groups:   x1 [4]
##      x1    x2 c1    x1_log2 x2_log2
##   <int> <int> <chr>   <dbl>   <dbl>
## 1     1     5 a        0       2.32
## 2     2     6 a        1       2.58
## 3     3     7 b        1.58    2.81
## 4     4     8 b        2       3
df %>% group_by(x1) %>% mutate_at(vars(x1:x2,-group_cols()),list(log2=log2))
## # A tibble: 4 x 4
## # Groups:   x1 [4]
##      x1    x2 c1     log2
##   <int> <int> <chr> <dbl>
## 1     1     5 a      2.32
## 2     2     6 a      2.58
## 3     3     7 b      2.81
## 4     4     8 b      3

4.5 数据汇总

4.5.1 summarise与group_by

summarise函数的用法为summarise(.data, …),它在 … 参数中输入汇总计算表达式,计算结果必须为列表或长度为1的向量。返回一个m*n的tibble。在未分组的tibble中,m为1,n为表达式的个数。在分组的tibble中,m为分组组合数,n为分组维度加表达式的个数。对于表达式,除了返回结果的长度与mutate不同,其他均相同。下面为一些例子:

df %>% summarise(
              y1=n(),
              y2=sum(x1)+y1,
              y3=length(c1),
              y4=x1[3]+x2[4],
              y5=list(c(x1[2],3)),
            # y5_2=list(x1[2],3),  # list的长度必须为1
              y6=list(list(2,"a"))
              )
## # A tibble: 1 x 6
##      y1    y2    y3    y4 y5        y6        
##   <int> <int> <int> <int> <list>    <list>    
## 1     4    14     4    11 <dbl [2]> <list [2]>
# 更具有实际意义的返回列表结果的例子
probs <- c(0.01, 0.25, 0.5, 0.75, 0.99)
mtcars %>%
  group_by(cyl) %>%
  summarise(p = list(probs), q = list(quantile(mpg, probs))) %>%
  unnest()
## # A tibble: 15 x 3
##      cyl     p     q
##    <dbl> <dbl> <dbl>
##  1     4  0.01  21.4
##  2     4  0.25  22.8
##  3     4  0.5   26  
##  4     4  0.75  30.4
##  5     4  0.99  33.8
##  6     6  0.01  17.8
##  7     6  0.25  18.6
##  8     6  0.5   19.7
##  9     6  0.75  21  
## 10     6  0.99  21.4
## 11     8  0.01  10.4
## 12     8  0.25  14.4
## 13     8  0.5   15.2
## 14     8  0.75  16.2
## 15     8  0.99  19.1
df %>% group_by(c1) %>% 
  summarise(
            y1=n(),
            y2=sum(x1)+y1,
            y3=length(c1),
            y4=x1[3]+x2[4],
            y5=list(c(x1[2],3)),
          # y5_2=list(x1[2],3),  # list的长度必须为1
            y6=list(list(2,"a"))
            )
## # A tibble: 2 x 7
##   c1       y1    y2    y3    y4 y5        y6        
##   <chr> <int> <int> <int> <int> <list>    <list>    
## 1 a         2     5     2    NA <dbl [2]> <list [2]>
## 2 b         2     9     2    NA <dbl [2]> <list [2]>

4.5.2 summarise_all

对group_by以外的其他变量用同一个汇总函数处理。其用法为:

summarise_all(.tbl, .funs, ...)
# 未指定名,默认以原变量名为新变量名,则会将原数据覆盖掉
df %>% group_by(c1) %>% 
  summarise_all(sum)
## # A tibble: 2 x 3
##   c1       x1    x2
##   <chr> <int> <int>
## 1 a         3    11
## 2 b         7    15
# 如果给出列名,则会在变量名之后添加该列名后缀,以下划线分隔
df %>% group_by(c1) %>% 
  summarise_all(list(sum=sum))
## # A tibble: 2 x 3
##   c1    x1_sum x2_sum
##   <chr>  <int>  <int>
## 1 a          3     11
## 2 b          7     15
# 如果有多个表达式,则默认在变量名后添加fnx后缀,其中x为函数的序号
df %>% group_by(c1) %>% 
  summarise_all(list(sum=sum,prod=prod))
## # A tibble: 2 x 5
##   c1    x1_sum x2_sum x1_prod x2_prod
##   <chr>  <int>  <int>   <dbl>   <dbl>
## 1 a          3     11       2      30
## 2 b          7     15      12      56
# 表达式结果可以是长度为1的list,如果list中是向量,则在unnest时,会纵向展开
rtlist3 <- function(x) {
  list(c(sum=sum(x),prod=prod(x)))
}
df %>% group_by(c1) %>% 
  summarise_all(list(summarise=rtlist3)) %>% 
  unnest()
## # A tibble: 4 x 3
##   c1    x1_summarise x2_summarise
##   <chr>        <dbl>        <dbl>
## 1 a                3           11
## 2 a                2           30
## 3 b                7           15
## 4 b               12           56
# 如果list中是数据框,则在unnest时,会横向展开
rtlist4 <- function(x) {
  list(data.frame(sum=sum(x),prod=prod(x)))
}
df %>% group_by(c1) %>% 
  summarise_all(list(summarise=rtlist4)) %>% 
  unnest()
## # A tibble: 2 x 5
##   c1      sum  prod  sum1 prod1
##   <chr> <int> <dbl> <int> <dbl>
## 1 a         3     2    11    30
## 2 b         7    12    15    56
# 用~ fun(.)的形式给出其他参数
df %>% group_by(c1) %>% summarise_all(list(sum=~sum(.,na.rm = T),prod=~prod(.,na.rm = T)))
## # A tibble: 2 x 5
##   c1    x1_sum x2_sum x1_prod x2_prod
##   <chr>  <int>  <int>   <dbl>   <dbl>
## 1 a          3     11       2      30
## 2 b          7     15      12      56

4.5.3 summarise_at

summarise_at的用法为summarise_at(.tbl, .vars, .funs, …, .cols = NULL),与mutate_at非常相似,下面为例子:

df %>% group_by(c1) %>% summarise_at(c("x1","x2"),list(mean=mean),na.rm = T)
## # A tibble: 2 x 3
##   c1    x1_mean x2_mean
##   <chr>   <dbl>   <dbl>
## 1 a         1.5     5.5
## 2 b         3.5     7.5
df %>% group_by(c1) %>% summarise_at(vars(-x1),list(mean=mean),na.rm = T)
## # A tibble: 2 x 2
##   c1     mean
##   <chr> <dbl>
## 1 a       5.5
## 2 b       7.5
df %>% group_by(c1) %>% summarise_at(vars(matches("x")),list(mean=mean),na.rm = T)
## # A tibble: 2 x 3
##   c1    x1_mean x2_mean
##   <chr>   <dbl>   <dbl>
## 1 a         1.5     5.5
## 2 b         3.5     7.5

4.5.4 summarise_if

summarise_if的用法为summarise_if(.tbl, .predicate, .funs, …),与mutate_if的用法非常相似

df %>% group_by(c1) %>% summarise_if(is.numeric,list(mean=mean),na.rm = T)
## # A tibble: 2 x 3
##   c1    x1_mean x2_mean
##   <chr>   <dbl>   <dbl>
## 1 a         1.5     5.5
## 2 b         3.5     7.5

4.6 函数复用

mutate_all、mutate_at、mutate_if为我们提供了将一个计算函数用于tibble的多列的方法,summarise_all、summarise_at、summarise_if为我们提供了将一个汇总函数用于tibble的多列的方法。我们希望有更灵活的方法,可以将函数应用于多个数据,而不用for循环来做。map系列函数就是我们所需要的函数。

4.6.1 单变量输入函数的复用

下面的函数第1个参数为向量或列表,第2个参数为函数名,他们将第1个参数中的每个元素应用于第2个参数的函数中,然后进行迭代,结果返回和输入参数相同长度的列表或向量。用法为map*(.x, .f, …)。map()系列函数无法与group_by结合,但可以和mutate结合。

根据返回结果的类型不同,有下面不同的函数:

  • map() 返回列表
  • map_lgl() 返回逻辑型向量
  • map_int() 返回整数型向量
  • map_dbl() 返回浮点型向量
  • map_chr() 返回字符串型向量
  • map_dfc() 返回数据框,进行横向拼接
  • map_dfr() 返回数据框,进行纵向拼接
  • walk() 得到输出、打印、保存等其他动作

输入.f函数的第1个参数的是向量或列表中的单元素,而非整体。.f函数中的其他参数在…处输入。

.f也可以是由公式构成的匿名函数,匿名函数只有1个参数时用.代表该参数,有两个参数时用.x和.y,有3个及以上参数时,用..1,..2,..3等。

.f还可以是整数或字符串向量,表示通过序号或元素名对元素的选取。

下面为一些例子:

v <- c(x = 1:3, y = 4:5) # 长度为5的向量
l <- list(x = 1:3, y = 4:5) # 长度为2的列表
# 输入可以是向量,也可以是列表
map_int(l, length)
## x y 
## 3 2
map_int(v, length)
## x1 x2 x3 y1 y2 
##  1  1  1  1  1
# 匿名函数的例子
map(v,~paste0("v",.)) # 如果存在元素名,map系列函数都会保留
## $x1
## [1] "v1"
## 
## $x2
## [1] "v2"
## 
## $x3
## [1] "v3"
## 
## $y1
## [1] "v4"
## 
## $y2
## [1] "v5"
map(l,~paste0("l",.))
## $x
## [1] "l1" "l2" "l3"
## 
## $y
## [1] "l4" "l5"
# 比较返回列表与返回向量的区别
map_chr(v,~paste0("v",.))
##   x1   x2   x3   y1   y2 
## "v1" "v2" "v3" "v4" "v5"
# map_chr(l,~paste0("l",.)) #报错
# 通过整数对元素选取
x <- list(list(1, 2, 3), list(4, 5, 6), list(7, 8, 9))
x %>% map_dbl(2)
## [1] 2 5 8
# 通过元素名对元素的选取
l2 <- list(list(x=1, y=2, z=3), list(y=4, x=5, z=6), list(z=7, y=8, x=9))
l2 %>% map_dbl("x")
## [1] 1 5 9
# 输出数据框,横向拼接
map_dfc(v,~data.frame(norm=rnorm(10,.,0.1),unif=runif(10,.,.+1)))
##         norm     unif    norm1    unif1    norm2    unif2    norm3
## 1  1.0753033 1.650280 2.006800 2.754616 3.117874 3.105093 4.008019
## 2  1.0367549 1.085303 1.771020 2.622964 3.024188 3.836722 4.024567
## 3  1.0428377 1.374800 2.088398 2.224810 2.987436 3.084053 4.077093
## 4  0.9174224 1.159669 1.880053 2.608697 3.042806 3.359192 3.981208
## 5  0.8954185 1.275127 1.982656 2.585490 3.144074 3.286949 3.974310
## 6  0.7968249 1.104004 1.939280 2.087259 2.961990 3.600315 3.906071
## 7  1.1976937 1.127125 2.016616 2.840007 2.956056 3.769320 4.049965
## 8  0.9459962 1.573724 1.938233 2.821869 2.997946 3.137664 3.992186
## 9  0.8904820 1.124153 1.903216 2.830904 3.028775 3.871239 4.071003
## 10 0.8694181 1.536181 1.926387 2.068720 2.989094 3.692540 4.105332
##       unif3    norm4    unif4
## 1  4.759771 5.034544 5.613345
## 2  4.458248 4.886604 5.923357
## 3  4.942103 5.225777 5.071245
## 4  4.960957 5.077004 5.858694
## 5  4.887173 4.871795 5.456272
## 6  4.869073 4.936801 5.330719
## 7  4.075724 4.932118 5.551092
## 8  4.401400 5.083432 5.446184
## 9  4.971590 4.914900 5.950943
## 10 4.828913 5.153439 5.455534
# 输出数据框,纵向拼接,如果提供.id参数,则增加一列,将采用数据的元素名或序号放在其中,增加一列的列名即为赋给.id的字符串
map_dfr(v,~data.frame(norm=rnorm(10,.,0.1),unif=runif(10,.-0.5,.+0.5)),.id = "mean_var")
##    mean_var      norm      unif
## 1        x1 1.0708791 0.5880747
## 2        x1 0.8781000 1.2495620
## 3        x1 1.0287433 0.9118856
## 4        x1 0.8691908 1.3653589
## 5        x1 1.0841492 0.5029093
## 6        x1 1.0005644 0.7858144
## 7        x1 0.9404737 1.0381647
## 8        x1 1.0505004 0.5086889
## 9        x1 1.0308990 1.3675623
## 10       x1 0.9120655 1.3238767
## 11       x2 1.9913773 2.1220212
## 12       x2 1.8233118 1.8097720
## 13       x2 2.1406546 2.3972522
## 14       x2 1.9466037 2.3626673
## 15       x2 1.9880193 2.0886116
## 16       x2 2.0106460 2.1386600
## 17       x2 2.0581093 2.1673751
## 18       x2 2.0552548 1.7766257
## 19       x2 2.1078604 2.0709893
## 20       x2 1.9412146 1.5188596
## 21       x3 2.9350724 3.2955180
## 22       x3 3.0769033 2.7589227
## 23       x3 3.0886581 2.7239698
## 24       x3 3.1619614 3.3582480
## 25       x3 2.7843999 2.5798940
## 26       x3 3.0460515 2.5607751
## 27       x3 3.0347173 2.6861081
## 28       x3 3.0179112 3.1555520
## 29       x3 3.1013586 2.8199616
## 30       x3 3.0883008 3.1297776
## 31       y1 3.9715999 3.5241752
## 32       y1 3.8128867 4.2959428
## 33       y1 4.0465951 3.5544369
## 34       y1 3.8912629 4.4106573
## 35       y1 3.9066915 4.1402700
## 36       y1 4.0563442 3.6046812
## 37       y1 4.1540938 3.6414643
## 38       y1 3.9275614 3.6662299
## 39       y1 3.9316730 4.1293966
## 40       y1 3.9745933 4.4326297
## 41       y2 4.8979532 4.6215441
## 42       y2 4.9409384 4.9236838
## 43       y2 4.9815718 4.6613493
## 44       y2 4.9733924 5.0821287
## 45       y2 5.0080956 5.1103632
## 46       y2 4.9252380 5.2398957
## 47       y2 4.8836295 5.0382950
## 48       y2 5.1119673 4.5265885
## 49       y2 5.0074980 5.4223363
## 50       y2 4.9089065 4.7928093
# 输出其他动作
walk(l,print) # 比较与print(l)的不同
## [1] 1 2 3
## [1] 4 5

map_if和map_at用法是map的拓展,可参考mutate_if和mutate_at,符合条件的带入函数,不符合条件的保留原变量,最终返回结果的长度与输入长度相等。

map_if(v,~.>2,~paste0("v",.))
## $x1
## [1] 1
## 
## $x2
## [1] 2
## 
## $x3
## [1] "v3"
## 
## $y1
## [1] "v4"
## 
## $y2
## [1] "v5"
map_at(v,c("x3","y2"),~paste0("v",.))
## $x1
## [1] 1
## 
## $x2
## [1] 2
## 
## $x3
## [1] "v3"
## 
## $y1
## [1] 4
## 
## $y2
## [1] "v5"
map_at(v,vars(x1:x3,y2),~paste0("v",.))
## $x1
## [1] "v1"
## 
## $x2
## [1] "v2"
## 
## $x3
## [1] "v3"
## 
## $y1
## [1] 4
## 
## $y2
## [1] "v5"

4.6.2 多变量输入函数的复用

先以2变量为例来说明。两个变量可以是向量或列表,但长度需要相同,分别取出对应的元素赋给函数,然后迭代。用法为map2*(.x, .y, .f, …),* 可以是_lgl、_int、_dbl、_chr、_raw、_dfr、_dfc,另外walk2(.x, .y, .f, …)输出其他动作。pmap*(.l, .f, …)、pwalk(.l, .f, …) 与此类似。

mu <- list(5, 10, -3)
sigma <- list(1, 5, 10)
# 对map2函数来说,直接将两个变量作为前两个参数
map2(mu, sigma, rnorm, n = 5)
## [[1]]
## [1] 5.859541 6.123030 5.559965 4.921671 6.017920
## 
## [[2]]
## [1]  4.758399 13.054411 14.513107  7.036894  8.294030
## 
## [[3]]
## [1]   1.172990 -10.672129  -5.196920  16.418070   6.561991
# 对pmap函数来说,将多个变量组成列表作为第1个参数
pmap(list(mu, sigma), rnorm, n = 5)
## [[1]]
## [1] 5.465736 3.757822 4.988457 5.064998 3.583568
## 
## [[2]]
## [1]  6.616804  1.522934 24.228630 14.992567 12.823512
## 
## [[3]]
## [1]  -5.723785 -13.550543 -15.868876   1.246493   2.276911
# 3个变量的例子,列表与向量可以混用
n <- 4:6
pmap(list(n,mu, sigma), rnorm)
## [[1]]
## [1] 4.443106 5.121508 4.930414 3.693726
## 
## [[2]]
## [1]  6.9402645 12.4658913 12.5110741  0.7686838 10.3051696
## 
## [[3]]
## [1]  -7.337545   7.639635   4.911405  -1.419950 -17.186021  -1.082899

invoke_map对多种不同的函数进行复用。

map_depth(.x, .depth, .f, …, .ragged = FALSE) 递归进行函数复用。

4.7 可视化

内容较多,不再详述,可通过总述中的查找帮助文档,或参考相关书籍:

  • R for Data Science 第1、5、22章
  • ggplot2: Elegant Graphics for Data Analysis-第2版