dplyr和tidyr用法

简介

tidyverse是一套数据分析套件包,它极大地简化和拓展了使用R语言进行数据分析的操作,涵盖了数据导入、数据处理和可视化等多方面的功能。

dplyrtidyrtidyverse的重要组成部分,前者主要用于数据的处理,后者则主要用于数据的格式调整。

两者的主要处理的对象是data.frame(及衍生的data.tabletibble),这是一种用于存储高度结构化数据的对象。你可以将data.frame视作一张表格,其每一行为一个条目,每一列为一项属性。同一列中的所有数据类型一致,而同一行则不一定。data.frame中的每列都可以被视为一个向量,在dplyr的许多操作中可以体会到这一点。

magrittr的pipe操作符%>%是一种可以很好地简化和美化链式函数调用的语法工具。函数表达式function(value, args)可以用pipe改写为value %>% function(args)dplyrtidyrdata.frame操作函数都很好地支持了pipe语法。在后文介绍dplyrtidyr函数的参数格式时,我们默认使用pipe中的参数格式,省去函数的第一个参数,即要操作的data.frame对象。
示例数据

r 复制代码
iris
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1           5.1         3.5          1.4         0.2  setosa
2           4.9         3.0          1.4         0.2  setosa
3           4.7         3.2          1.3         0.2  setosa
4           4.6         3.1          1.5         0.2  setosa
5           5.0         3.6          1.4         0.2  setosa
6           5.4         3.9          1.7         0.4  setosa
7           4.6         3.4          1.4         0.3  setosa
8           5.0         3.4          1.5         0.2  setosa
9           4.4         2.9          1.4         0.2  setosa
10          4.9         3.1          1.5         0.1  setosa
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
r 复制代码
storms
plaintext 复制代码
# A tibble: 19,537 × 13
   name   year month   day  hour   lat  long status       category  wind pressure tropicalstorm_force_...¹
   <chr> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <fct>           <dbl> <int>    <int>                  <int>
 1 Amy    1975     6    27     0  27.5 -79   tropical de...       NA    25     1013                     NA
 2 Amy    1975     6    27     6  28.5 -79   tropical de...       NA    25     1013                     NA
 3 Amy    1975     6    27    12  29.5 -79   tropical de...       NA    25     1013                     NA
 4 Amy    1975     6    27    18  30.5 -79   tropical de...       NA    25     1013                     NA
 5 Amy    1975     6    28     0  31.5 -78.8 tropical de...       NA    25     1012                     NA
 6 Amy    1975     6    28     6  32.4 -78.7 tropical de...       NA    25     1012                     NA
 7 Amy    1975     6    28    12  33.3 -78   tropical de...       NA    25     1011                     NA
 8 Amy    1975     6    28    18  34   -77   tropical de...       NA    30     1006                     NA
 9 Amy    1975     6    29     0  34.4 -75.8 tropical st...       NA    35     1004                     NA
10 Amy    1975     6    29     6  34   -74.8 tropical st...       NA    40     1002                     NA
# ℹ 19,527 more rows
# ℹ abbreviated name: ¹​tropicalstorm_force_diameter
# ℹ 1 more variable: hurricane_force_diameter <int>
r 复制代码
data.frame(Titanic)
plaintext 复制代码
   Class    Sex   Age Survived Freq
1    1st   Male Child       No    0
2    2nd   Male Child       No    0
3    3rd   Male Child       No   35
4   Crew   Male Child       No    0
5    1st Female Child       No    0
6    2nd Female Child       No    0
7    3rd Female Child       No   17
8   Crew Female Child       No    0
9    1st   Male Adult       No  118
10   2nd   Male Adult       No  154
 [ reached 'max' / getOption("max.print") -- omitted 22 rows ]

简单操作:行列操作、连接和调整

dplyr列操作

列操作包括选取(select)、排序(relocate)、重命名(rename)和编辑(mutate),列操作不会改变行的数量或顺序。

select用于从表中选取部分列。其参数格式为select(colname)select(new_name=old_name),只有参数中指定的列会被保留。

r 复制代码
iris %>% select(Species, Width = Petal.Width, Length = Petal.Length)
plaintext 复制代码
   Species Width Length
1   setosa   0.2    1.4
2   setosa   0.2    1.4
3   setosa   0.2    1.3
4   setosa   0.2    1.5
5   setosa   0.2    1.4
6   setosa   0.4    1.7
7   setosa   0.3    1.4
8   setosa   0.2    1.5
9   setosa   0.2    1.4
10  setosa   0.1    1.5
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

relocate用于改变列的顺序,而不改变列的数目。其参数格式和select类似,为relocate(colname)relocate(new_name=old_name).before/.after参数可以指定要移动到的位置,如果没有指定,参数中指定的列将会被移动到表的最前面(左侧)。

r 复制代码
iris %>% relocate(epithet=Species)
plaintext 复制代码
   epithet Sepal.Length Sepal.Width Petal.Length Petal.Width
1   setosa          5.1         3.5          1.4         0.2
2   setosa          4.9         3.0          1.4         0.2
3   setosa          4.7         3.2          1.3         0.2
4   setosa          4.6         3.1          1.5         0.2
5   setosa          5.0         3.6          1.4         0.2
6   setosa          5.4         3.9          1.7         0.4
7   setosa          4.6         3.4          1.4         0.3
8   setosa          5.0         3.4          1.5         0.2
9   setosa          4.4         2.9          1.4         0.2
10  setosa          4.9         3.1          1.5         0.1
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

rename用于对列进行重命名,而不会改变列的数量和位置,其参数格式为rename(new_name=old_name)

r 复制代码
iris %>% rename(V1=Sepal.Length, V2=Sepal.Width)
plaintext 复制代码
    V1  V2 Petal.Length Petal.Width Species
1  5.1 3.5          1.4         0.2  setosa
2  4.9 3.0          1.4         0.2  setosa
3  4.7 3.2          1.3         0.2  setosa
4  4.6 3.1          1.5         0.2  setosa
5  5.0 3.6          1.4         0.2  setosa
6  5.4 3.9          1.7         0.4  setosa
7  4.6 3.4          1.4         0.3  setosa
8  5.0 3.4          1.5         0.2  setosa
9  4.4 2.9          1.4         0.2  setosa
10 4.9 3.1          1.5         0.1  setosa
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

mutate用于创建新列,也可以用来修改或移除已有的列。其参数格式为mutate(new_col=fn(old_col1, old_col2,...)),其中old_col<n>是列名,其值会作为向量传入函数fnfn的计算结果则作为新列new_col的值。fn结果向量的长度应当与表的行数一致或为1。如果新列名new_col已存在,那么列值会被覆盖。值被赋为NULL的列会则被移除。

r 复制代码
iris %>% mutate(Genus="Iris", epithet=Species, Species=paste(Genus, epithet))
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width     Species Genus epithet
1           5.1         3.5          1.4         0.2 Iris setosa  Iris  setosa
2           4.9         3.0          1.4         0.2 Iris setosa  Iris  setosa
3           4.7         3.2          1.3         0.2 Iris setosa  Iris  setosa
4           4.6         3.1          1.5         0.2 Iris setosa  Iris  setosa
5           5.0         3.6          1.4         0.2 Iris setosa  Iris  setosa
6           5.4         3.9          1.7         0.4 Iris setosa  Iris  setosa
7           4.6         3.4          1.4         0.3 Iris setosa  Iris  setosa
8           5.0         3.4          1.5         0.2 Iris setosa  Iris  setosa
9           4.4         2.9          1.4         0.2 Iris setosa  Iris  setosa
10          4.9         3.1          1.5         0.1 Iris setosa  Iris  setosa
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

dplyr行操作

行操作包括筛选(filter)、排序(arrange)、分片(slice)、汇总(summarize)和重构(reframe),以及简写操作计数(counttally)和去重(distinct)

filter用于筛选满足指定条件的列。其参数为filter(fn(col1,col2,...)),其中col<n>是列名,其值会作为向量传入函数fn,只有fn的计算结果为TRUE的列会被保留。

r 复制代码
iris %>% filter(Species=="virginica")
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1           6.3         3.3          6.0         2.5 virginica
2           5.8         2.7          5.1         1.9 virginica
3           7.1         3.0          5.9         2.1 virginica
4           6.3         2.9          5.6         1.8 virginica
5           6.5         3.0          5.8         2.2 virginica
6           7.6         3.0          6.6         2.1 virginica
7           4.9         2.5          4.5         1.7 virginica
8           7.3         2.9          6.3         1.8 virginica
9           6.7         2.5          5.8         1.8 virginica
10          7.2         3.6          6.1         2.5 virginica
 [ reached 'max' / getOption("max.print") -- omitted 40 rows ]

arrange用于按指定条件排序行。其参数格式为arrange(fn(...))fn的计算结果将作为排序的依据。

r 复制代码
iris %>% arrange(desc(Species), -Sepal.Length)
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1           7.9         3.8          6.4         2.0 virginica
2           7.7         3.8          6.7         2.2 virginica
3           7.7         2.6          6.9         2.3 virginica
4           7.7         2.8          6.7         2.0 virginica
5           7.7         3.0          6.1         2.3 virginica
6           7.6         3.0          6.6         2.1 virginica
7           7.4         2.8          6.1         1.9 virginica
8           7.3         2.9          6.3         1.8 virginica
9           7.2         3.6          6.1         2.5 virginica
10          7.2         3.2          6.0         1.8 virginica
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

slice用于选取指定的行,参数格式为slice(fn(...))fn的计算结果应当为一个整数向量,对应行数的行将会被保留。除了slice外,还有取首行(slice_head)、取尾行(slice_tail)、取随机行(slice_sample)、取最大行(slice_max)和取最小行(slice_min)几个常用的辅助函数

r 复制代码
iris %>% slice_max(Petal.Width, n=10)
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1           6.3         3.3          6.0         2.5 virginica
2           7.2         3.6          6.1         2.5 virginica
3           6.7         3.3          5.7         2.5 virginica
4           5.8         2.8          5.1         2.4 virginica
5           6.3         3.4          5.6         2.4 virginica
6           6.7         3.1          5.6         2.4 virginica
7           6.4         3.2          5.3         2.3 virginica
8           7.7         2.6          6.9         2.3 virginica
9           6.9         3.2          5.7         2.3 virginica
10          7.7         3.0          6.1         2.3 virginica
 [ reached 'max' / getOption("max.print") -- omitted 4 rows ]

summarise用于将多行汇总为一行。其参数格式为summarise(col_name=fn(col1,col2,...)),其中col<n>是原表格中的列名,函数fn的计算结果应当是一个长度为1的向量,col_name是汇总后的列名。

r 复制代码
iris %>% summarise(Petal.Width.Max=max(Petal.Width),Petal.Width.Min=min(Petal.Width))
plaintext 复制代码
  Petal.Width.Max Petal.Width.Min
1             2.5             0.1

reframesummarise类似,但是不限定fn计算结果的长度。如果fn计算结果的长度超过1,那么会生成多行。

r 复制代码
iris %>% reframe(Petal.Width=quantile(Petal.Width),Petal.Length=quantile(Petal.Length))
plaintext 复制代码
  Petal.Width Petal.Length
1         0.1         1.00
2         0.3         1.60
3         1.3         4.35
4         1.8         5.10
5         2.5         6.90

dplyr表连接

r 复制代码
# 示例数据
band_members
plaintext 复制代码
# A tibble: 3 × 2
  name  band   
  <chr> <chr>  
1 Mick  Stones 
2 John  Beatles
3 Paul  Beatles
r 复制代码
band_instruments
plaintext 复制代码
# A tibble: 3 × 2
  name  plays 
  <chr> <chr> 
1 John  guitar
2 Paul  bass  
3 Keith guitar

表连接函数包括根据指定列合并表的inner_joinleft_joinright_joinfull_join和生成所有条目对枚举(笛卡尔积)的cross_join,以及用于筛选数据的semi_joinanti_join

r 复制代码
band_members %>% inner_join(band_instruments)
plaintext 复制代码
# A tibble: 2 × 3
  name  band    plays 
  <chr> <chr>   <chr> 
1 John  Beatles guitar
2 Paul  Beatles bass  
r 复制代码
band_members %>% left_join(band_instruments)
plaintext 复制代码
# A tibble: 3 × 3
  name  band    plays 
  <chr> <chr>   <chr> 
1 Mick  Stones  <NA>  
2 John  Beatles guitar
3 Paul  Beatles bass  
r 复制代码
band_members %>% right_join(band_instruments)
plaintext 复制代码
# A tibble: 3 × 3
  name  band    plays 
  <chr> <chr>   <chr> 
1 John  Beatles guitar
2 Paul  Beatles bass  
3 Keith <NA>    guitar
r 复制代码
band_members %>% full_join(band_instruments)
plaintext 复制代码
# A tibble: 4 × 3
  name  band    plays 
  <chr> <chr>   <chr> 
1 Mick  Stones  <NA>  
2 John  Beatles guitar
3 Paul  Beatles bass  
4 Keith <NA>    guitar
r 复制代码
band_members %>% cross_join(band_instruments)
plaintext 复制代码
# A tibble: 9 × 4
  name.x band    name.y plays 
  <chr>  <chr>   <chr>  <chr> 
1 Mick   Stones  John   guitar
2 Mick   Stones  Paul   bass  
3 Mick   Stones  Keith  guitar
4 John   Beatles John   guitar
5 John   Beatles Paul   bass  
6 John   Beatles Keith  guitar
7 Paul   Beatles John   guitar
8 Paul   Beatles Paul   bass  
9 Paul   Beatles Keith  guitar
r 复制代码
band_members %>% semi_join(band_instruments)
plaintext 复制代码
# A tibble: 2 × 2
  name  band   
  <chr> <chr>  
1 John  Beatles
2 Paul  Beatles
r 复制代码
band_members %>% anti_join(band_instruments)
plaintext 复制代码
# A tibble: 1 × 2
  name  band  
  <chr> <chr> 
1 Mick  Stones

tidyr表调整

基础的表调整包括多列转单列的gather和单列转多列的spread

gather可以将表格的列名转换为独立的列,同时保留列值和其他列数据的对应关系。其参数格式为gather("name_of_key_col", "name_of_value_col", cols_to_gather)

r 复制代码
iris %>% gather("measurement","value",Sepal.Length:Petal.Width)
plaintext 复制代码
   Species  measurement value
1   setosa Sepal.Length   5.1
2   setosa Sepal.Length   4.9
3   setosa Sepal.Length   4.7
4   setosa Sepal.Length   4.6
5   setosa Sepal.Length   5.0
6   setosa Sepal.Length   5.4
7   setosa Sepal.Length   4.6
8   setosa Sepal.Length   5.0
9   setosa Sepal.Length   4.4
10  setosa Sepal.Length   4.9
 [ reached 'max' / getOption("max.print") -- omitted 590 rows ]

spreadgather的逆操作,能够根据指定的索引列,将一列数据转换为以索引列的值为列名的多列数据。其参数格式为spread(key_col, value_col)spread不支持多列key_colvalue_col,对于更高级的操作,参见pivot_wider

r 复制代码
data.frame(Titanic) %>% spread(Class, Freq)
plaintext 复制代码
     Sex   Age Survived 1st 2nd 3rd Crew
1   Male Child       No   0   0  35    0
2   Male Child      Yes   5  11  13    0
3   Male Adult       No 118 154 387  670
4   Male Adult      Yes  57  14  75  192
5 Female Child       No   0   0  17    0
6 Female Child      Yes   1  13  14    0
7 Female Adult       No   4  13  89    3
8 Female Adult      Yes 140  80  76   20

进阶操作:分组和批处理

批量处理

across用于在mutatesummarizereframe中对多列应用同一函数

r 复制代码
iris %>% mutate(across(Sepal.Length:Petal.Width, ~.x-mean(.x)))
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1    -0.7433333  0.44266667       -2.358  -0.9993333  setosa
2    -0.9433333 -0.05733333       -2.358  -0.9993333  setosa
3    -1.1433333  0.14266667       -2.458  -0.9993333  setosa
4    -1.2433333  0.04266667       -2.258  -0.9993333  setosa
5    -0.8433333  0.54266667       -2.358  -0.9993333  setosa
6    -0.4433333  0.84266667       -2.058  -0.7993333  setosa
7    -1.2433333  0.34266667       -2.358  -0.8993333  setosa
8    -0.8433333  0.34266667       -2.258  -0.9993333  setosa
9    -1.4433333 -0.15733333       -2.358  -0.9993333  setosa
10   -0.9433333  0.04266667       -2.258  -1.0993333  setosa
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

across默认不会修改列名,如果要修改列名可以通过.names参数指定,可以用"{.col}""{.fn}"指代列名和函数名。

r 复制代码
iris %>% reframe(q=seq(0,1,0.25),across(
  Sepal.Length:Petal.Width, list(quantile=~quantile(.x,q),percentage=~quantile(range(.x),q)), .names="{.col}_{.fn}"
))
plaintext 复制代码
     q Sepal.Length_quantile Sepal.Length_percentage Sepal.Width_quantile Sepal.Width_percentage
1 0.00                   4.3                     4.3                  2.0                    2.0
2 0.25                   5.1                     5.2                  2.8                    2.6
3 0.50                   5.8                     6.1                  3.0                    3.2
4 0.75                   6.4                     7.0                  3.3                    3.8
5 1.00                   7.9                     7.9                  4.4                    4.4
  Petal.Length_quantile Petal.Length_percentage Petal.Width_quantile Petal.Width_percentage
1                  1.00                   1.000                  0.1                    0.1
2                  1.60                   2.475                  0.3                    0.7
3                  4.35                   3.950                  1.3                    1.3
4                  5.10                   5.425                  1.8                    1.9
5                  6.90                   6.900                  2.5                    2.5

if_anyif_all用于在filter中对多列应用同一函数,并对筛选结果取并集或交集

r 复制代码
iris %>% filter(if_any(Sepal.Length:Petal.Width,~.x>median(.x)))
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1           5.1         3.5          1.4         0.2  setosa
2           4.7         3.2          1.3         0.2  setosa
3           4.6         3.1          1.5         0.2  setosa
4           5.0         3.6          1.4         0.2  setosa
5           5.4         3.9          1.7         0.4  setosa
6           4.6         3.4          1.4         0.3  setosa
7           5.0         3.4          1.5         0.2  setosa
8           4.9         3.1          1.5         0.1  setosa
9           5.4         3.7          1.5         0.2  setosa
10          4.8         3.4          1.6         0.2  setosa
 [ reached 'max' / getOption("max.print") -- omitted 113 rows ]
r 复制代码
iris %>% filter(if_all(Sepal.Length:Petal.Width,~.x>median(.x)))
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1           7.0         3.2          4.7         1.4 versicolor
2           6.4         3.2          4.5         1.5 versicolor
3           6.9         3.1          4.9         1.5 versicolor
4           6.3         3.3          4.7         1.6 versicolor
5           6.7         3.1          4.4         1.4 versicolor
6           5.9         3.2          4.8         1.8 versicolor
7           6.0         3.4          4.5         1.6 versicolor
8           6.7         3.1          4.7         1.5 versicolor
9           6.3         3.3          6.0         2.5  virginica
10          7.2         3.6          6.1         2.5  virginica
 [ reached 'max' / getOption("max.print") -- omitted 15 rows ]

rename_with可以使用自定义函数对多列进行重命名

r 复制代码
iris %>% rename_with(~str_replace(.x, "(.*)\\.(.).*", "\\2_\\1"))
plaintext 复制代码
   L_Sepal W_Sepal L_Petal W_Petal Species
1      5.1     3.5     1.4     0.2  setosa
2      4.9     3.0     1.4     0.2  setosa
3      4.7     3.2     1.3     0.2  setosa
4      4.6     3.1     1.5     0.2  setosa
5      5.0     3.6     1.4     0.2  setosa
6      5.4     3.9     1.7     0.4  setosa
7      4.6     3.4     1.4     0.3  setosa
8      5.0     3.4     1.5     0.2  setosa
9      4.4     2.9     1.4     0.2  setosa
10     4.9     3.1     1.5     0.1  setosa
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

多列赋值

通常,mutate函数的一个参数一次只能返回一列。如果想要一次返回多列,可以向将表转换为tibble对象,然后让fn返回一个data.frame

r 复制代码
as_tibble(iris) %>%
  mutate(as.data.frame(prcomp(t(pick(Sepal.Length:Petal.Width)))$rotation))
plaintext 复制代码
# A tibble: 150 × 9
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species     PC1     PC2      PC3      PC4
          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>   <dbl>    <dbl>    <dbl>
 1          5.1         3.5          1.4         0.2 setosa  -0.0771 -0.121   0.00327 -0.757  
 2          4.9         3            1.4         0.2 setosa  -0.0754 -0.0995  0.0824  -0.282  
 3          4.7         3.2          1.3         0.2 setosa  -0.0709 -0.110   0.0110  -0.0304 
 4          4.6         3.1          1.5         0.2 setosa  -0.0701 -0.0968 -0.0312  -0.0110 
 5          5           3.6          1.4         0.2 setosa  -0.0751 -0.124  -0.0436   0.0337 
 6          5.4         3.9          1.7         0.4 setosa  -0.0785 -0.126  -0.0463   0.00279
 7          4.6         3.4          1.4         0.3 setosa  -0.0671 -0.112  -0.0580   0.00412
 8          5           3.4          1.5         0.2 setosa  -0.0760 -0.112  -0.0166   0.0501 
 9          4.4         2.9          1.4         0.2 setosa  -0.0670 -0.0914 -0.00383  0.00380
10          4.9         3.1          1.5         0.1 setosa  -0.0769 -0.0999  0.0104   0.0773 
# ℹ 140 more rows

分组操作

mutate和行操作支持按特定列分组(group_byrowwise,或by/.by参数),分组后每组会独立应用函数。

可以使用group_by将表格转换为grouped_df,对grouped_df的所有操作均会自动按组进行。ungroup可以取消分组。

r 复制代码
iris %>% group_by(Species) %>% 
  summarize(mean.L_Sepal=mean(Sepal.Length), mean.L_Petal=mean(Petal.Length), n.Sample=n()) %>% 
  ungroup()
plaintext 复制代码
# A tibble: 3 × 4
  Species    mean.L_Sepal mean.L_Petal n.Sample
  <fct>             <dbl>        <dbl>    <int>
1 setosa             5.01         1.46       50
2 versicolor         5.94         4.26       50
3 virginica          6.59         5.55       50

也可以在操作函数中直接指定.by参数

r 复制代码
iris %>% reframe(q=seq(0,1,0.25), L_Sepal=quantile(Sepal.Length, q), .by=Species)
plaintext 复制代码
      Species    q L_Sepal
1      setosa 0.00     4.3
2      setosa 0.25     4.8
3      setosa 0.50     5.0
4      setosa 0.75     5.2
5      setosa 1.00     5.8
6  versicolor 0.00     4.9
7  versicolor 0.25     5.6
8  versicolor 0.50     5.9
9  versicolor 0.75     6.3
10 versicolor 1.00     7.0
 [ reached 'max' / getOption("max.print") -- omitted 5 rows ]
r 复制代码
iris %>% filter(if_all(Sepal.Length:Petal.Width,~.x>median(.x)),.by=Species)
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1           5.4         3.9          1.7         0.4     setosa
2           5.7         3.8          1.7         0.3     setosa
3           5.1         3.8          1.9         0.4     setosa
4           7.0         3.2          4.7         1.4 versicolor
5           6.4         3.2          4.5         1.5 versicolor
6           6.9         3.1          4.9         1.5 versicolor
7           6.3         3.3          4.7         1.6 versicolor
8           6.1         2.9          4.7         1.4 versicolor
9           6.7         3.1          4.4         1.4 versicolor
10          6.6         3.0          4.4         1.4 versicolor
 [ reached 'max' / getOption("max.print") -- omitted 12 rows ]

arrange函数默认忽略分组,且不支持.by/by参数。指定了.by_group=TRUE后,arrage会先对grouped_df的分组列进行排序,然后在各个组内分别按指定的列或计算结果进行排序。

r 复制代码
data.frame(Titanic) %>% group_by(Class) %>% arrange(Freq, .by_group=TRUE)
plaintext 复制代码
# A tibble: 32 × 5
# Groups:   Class [4]
   Class Sex    Age   Survived  Freq
   <fct> <fct>  <fct> <fct>    <dbl>
 1 1st   Male   Child No           0
 2 1st   Female Child No           0
 3 1st   Female Child Yes          1
 4 1st   Female Adult No           4
 5 1st   Male   Child Yes          5
 6 1st   Male   Adult Yes         57
 7 1st   Male   Adult No         118
 8 1st   Female Adult Yes        140
 9 2nd   Male   Child No           0
10 2nd   Female Child No           0
# ℹ 22 more rows

grouped_df分组列的排序不支持自定义函数,如果要根据计算结果排列组,可以先用mutate生产索引列,或者利用ave函数:

r 复制代码
data.frame(Titanic) %>% mutate(Class.Freq=sum(Freq),.by=Class) %>% arrange(Class.Freq)
plaintext 复制代码
   Class    Sex   Age Survived Freq Class.Freq
1    2nd   Male Child       No    0        285
2    2nd Female Child       No    0        285
3    2nd   Male Adult       No  154        285
4    2nd Female Adult       No   13        285
5    2nd   Male Child      Yes   11        285
6    2nd Female Child      Yes   13        285
7    2nd   Male Adult      Yes   14        285
8    2nd Female Adult      Yes   80        285
9    1st   Male Child       No    0        325
10   1st Female Child       No    0        325
 [ reached 'max' / getOption("max.print") -- omitted 22 rows ]
r 复制代码
data.frame(Titanic) %>% arrange(ave(Freq,Class,FUN=sum))
plaintext 复制代码
   Class    Sex   Age Survived Freq
1    2nd   Male Child       No    0
2    2nd Female Child       No    0
3    2nd   Male Adult       No  154
4    2nd Female Adult       No   13
5    2nd   Male Child      Yes   11
6    2nd Female Child      Yes   13
7    2nd   Male Adult      Yes   14
8    2nd Female Adult      Yes   80
9    1st   Male Child       No    0
10   1st Female Child       No    0
 [ reached 'max' / getOption("max.print") -- omitted 22 rows ]

表调整

pivot_longerpivot_wider可以实现比gatherspread更为精细和复杂的列转换操作。

pivot_longer的参数格式为pivot_longer(cols_to_gather, names_to="name_of_key_col", values_to="name_of_value_col"),另外还提供names_patternnames_transform等可选参数对要转换的列名进行拆分和格式转换。

r 复制代码
iris %>% pivot_longer(
  Sepal.Length:Petal.Width,
  names_to=c("component","measure"), names_pattern="(.*)\\.(.*)",
  values_to=c("value")
)
plaintext 复制代码
# A tibble: 600 × 4
   Species component measure value
   <fct>   <chr>     <chr>   <dbl>
 1 setosa  Sepal     Length    5.1
 2 setosa  Sepal     Width     3.5
 3 setosa  Petal     Length    1.4
 4 setosa  Petal     Width     0.2
 5 setosa  Sepal     Length    4.9
 6 setosa  Sepal     Width     3  
 7 setosa  Petal     Length    1.4
 8 setosa  Petal     Width     0.2
 9 setosa  Sepal     Length    4.7
10 setosa  Sepal     Width     3.2
# ℹ 590 more rows

如果只想将列名中的一部分内容转移为新行,可以使用names_to中的保留字".value"。匹配该部分的内容将继续保留为列名。

r 复制代码
iris %>% pivot_longer(
  Sepal.Length:Petal.Width,
  names_to=c("component",".value"), names_pattern="(.*)\\.(.*)"
)
plaintext 复制代码
# A tibble: 300 × 4
   Species component Length Width
   <fct>   <chr>      <dbl> <dbl>
 1 setosa  Sepal        5.1   3.5
 2 setosa  Petal        1.4   0.2
 3 setosa  Sepal        4.9   3  
 4 setosa  Petal        1.4   0.2
 5 setosa  Sepal        4.7   3.2
 6 setosa  Petal        1.3   0.2
 7 setosa  Sepal        4.6   3.1
 8 setosa  Petal        1.5   0.2
 9 setosa  Sepal        5     3.6
10 setosa  Petal        1.4   0.2
# ℹ 290 more rows

pivot_wider的参数格式为pivot_wider(id_cols=id_col, names_from=key_col, values_from=value_col),其中id_col是转换后仍要保留的列(如果留空,则使用key_colvalue_col以外所有的列),key_col是要转换为列名的列,value_col列的数据则会转换为对应列的值。可选参数names_glue可以设置列名格式,values_fill可以设置缺失值的替换值。

r 复制代码
data.frame(Titanic) %>% pivot_wider(
  names_from=c(Sex,Class), names_glue="{Sex}({Class})",
  values_from=Freq
)
plaintext 复制代码
# A tibble: 4 × 10
  Age   Survived `Male(1st)` `Male(2nd)` `Male(3rd)` `Male(Crew)` `Female(1st)` `Female(2nd)`
  <fct> <fct>          <dbl>       <dbl>       <dbl>        <dbl>         <dbl>         <dbl>
1 Child No                 0           0          35            0             0             0
2 Adult No               118         154         387          670             4            13
3 Child Yes                5          11          13            0             1            13
4 Adult Yes               57          14          75          192           140            80
# ℹ 2 more variables: `Female(3rd)` <dbl>, `Female(Crew)` <dbl>

如果一组id_colkey_col对应于多条value_col,可以通过values_fn参数指定数据的汇总方式

r 复制代码
data.frame(Titanic) %>% pivot_wider(
  id_cols=c(Sex,Survived),
  names_from=Class,
  values_from=Freq, values_fn=sum
)
plaintext 复制代码
# A tibble: 4 × 6
  Sex    Survived `1st` `2nd` `3rd`  Crew
  <fct>  <fct>    <dbl> <dbl> <dbl> <dbl>
1 Male   No         118   154   422   670
2 Female No           4    13   106     3
3 Male   Yes         62    25    88   192
4 Female Yes        141    93    90    20

高级用法:变量替换

如前文所见,dplyr函数的一大特征是:在函数中,列名可以像变量一样使用。这为简单操作带来方便的同时,也给更进一步的高级操作带来了很大的麻烦。

一个最大的问题是:如何告诉函数某个变量指代的是列名还是来自全局环境中的变量呢?

这样的操作主要通过rlang包来实现。

预备知识:<data-masking><tidy-select>

在进一步演示之前,先指出dplyr中的两种看起来相似但又略有不同的参数类型:<data-masking><tidy-select>

<data-masking>被用于filter()mutate()summarize()reframe()arrange()slice()count()tally()distinct()等可以对列做计算的函数中,用于将列作为参数传递给函数;而<tidy-select>则主要被用在select()rename()relocate()gather()spread()函数,以及前述函数的.by参数中,用于对列进行选取。

我们首先介绍相对简单的<tidy-select>的用法。

<tidy-select>选取列的语法有两种风格,一种类似于base-r中的向量操作,另一种类似于集合操作符。

  • base-r向量操作风格
    • :表示连续范围
    • c()表示并集
    • -表示排除
  • 集合操作风格
    • &表示交集
    • |表示并集
    • !表示排除

这些算符/函数既接受数字,也接受列名:

r 复制代码
storms %>% select(c(2:5, status:pressure&!category), 1, latitude = lat, longitude = long)
plaintext 复制代码
# A tibble: 19,537 × 10
    year month   day  hour status               wind pressure name  latitude longitude
   <dbl> <dbl> <int> <dbl> <fct>               <int>    <int> <chr>    <dbl>     <dbl>
 1  1975     6    27     0 tropical depression    25     1013 Amy       27.5     -79  
 2  1975     6    27     6 tropical depression    25     1013 Amy       28.5     -79  
 3  1975     6    27    12 tropical depression    25     1013 Amy       29.5     -79  
 4  1975     6    27    18 tropical depression    25     1013 Amy       30.5     -79  
 5  1975     6    28     0 tropical depression    25     1012 Amy       31.5     -78.8
 6  1975     6    28     6 tropical depression    25     1012 Amy       32.4     -78.7
 7  1975     6    28    12 tropical depression    25     1011 Amy       33.3     -78  
 8  1975     6    28    18 tropical depression    30     1006 Amy       34       -77  
 9  1975     6    29     0 tropical storm         35     1004 Amy       34.4     -75.8
10  1975     6    29     6 tropical storm         40     1002 Amy       34       -74.8
# ℹ 19,527 more rows

如果<tidy-select>...的形式传入函数(如select()relocate())中,,分隔项会自动用|(并)连接;如果<tidy-select>以单个参数的形式传入函数,则需要用c()|将各项手动连起来。另外,<tidy-select>,支持用=修改选取列的列名,而且会保留列的顺序,因此也可以用来对列进行重命名和重排。

<tidy-select>还支持一些函数来指定要选取的列,包括:

函数 作用
everything() 选取所有列
last_col(n) 选取倒数第n+1
starts_with("pattern") 列名以"pattern"开头的列
ends_with("pattern") 列名以"pattern"结尾的列
contains("pattern") 列名包含"pattern"的列
matches("pattern") 列名匹配正则表达式regex("pattern")的列
where(fn) fn(列内容)TRUE的列

注意与其他函数不同,where可以通过列的内容 而不是列名或者顺序来选取列,以下是一个where函数的使用示例:

r 复制代码
airquality %>% select(where(~ is.integer(.x) && !any(is.na(.x))))
plaintext 复制代码
   Temp Month Day
1    67     5   1
2    72     5   2
3    74     5   3
4    62     5   4
5    56     5   5
6    66     5   6
7    65     5   7
8    59     5   8
9    61     5   9
10   69     5  10
 [ reached 'max' / getOption("max.print") -- omitted 143 rows ]

其中~.xpurrr匿名函数简写格式,详细可见?purrr::as_mapper

<data-masking>则是一个(或多个)以列名为变量名的表达式,用于对列表进行修改或汇总。

<data-masking>表达式中,列名指定的列将以向量的形式传入函数或算符中参与运算。

例如,

r 复制代码
iris %>% mutate(LW.ratio_Petal = Petal.Length / Petal.Width)
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species LW.ratio_Petal
1           5.1         3.5          1.4         0.2  setosa       7.000000
2           4.9         3.0          1.4         0.2  setosa       7.000000
3           4.7         3.2          1.3         0.2  setosa       6.500000
4           4.6         3.1          1.5         0.2  setosa       7.500000
5           5.0         3.6          1.4         0.2  setosa       7.000000
6           5.4         3.9          1.7         0.4  setosa       4.250000
7           4.6         3.4          1.4         0.3  setosa       4.666667
8           5.0         3.4          1.5         0.2  setosa       7.500000
9           4.4         2.9          1.4         0.2  setosa       7.000000
10          4.9         3.1          1.5         0.1  setosa      15.000000
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

就是计算iris$Petal.Length/iris$Petal.Width的结果并保存到LW.ratio_Petal列中。

如果使用的是一些非向量化的函数,那么得到会得到一些非预期的结果:

r 复制代码
iris %>% mutate(new_col=sum(Petal.Length, Petal.Width))
plaintext 复制代码
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species new_col
1           5.1         3.5          1.4         0.2  setosa   743.6
2           4.9         3.0          1.4         0.2  setosa   743.6
3           4.7         3.2          1.3         0.2  setosa   743.6
4           4.6         3.1          1.5         0.2  setosa   743.6
5           5.0         3.6          1.4         0.2  setosa   743.6
6           5.4         3.9          1.7         0.4  setosa   743.6
7           4.6         3.4          1.4         0.3  setosa   743.6
8           5.0         3.4          1.5         0.2  setosa   743.6
9           4.4         2.9          1.4         0.2  setosa   743.6
10          4.9         3.1          1.5         0.1  setosa   743.6
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

可以看到,new_col的值均为743.6,这是因为实际进行的运算变成了sum(iris$Petal.Length, iris$Petal.Width),得到的单一值被填充为一列,得到了全为743.6new_col

通过辅助函数c_across()pick()可以在<data-masking>中以<tidy-select>的格式选取列,前者会选取列拼接为向量,而后者会选取列为表:

r 复制代码
iris %>% reframe(
  len_c_across=length(c_across(c(Petal.Length,Petal.Width))),
  nrow_pick=nrow(pick(Petal.Length,Petal.Width)),
  ncol_pick=ncol(pick(Petal.Length,Petal.Width)),
  .by=Species)
plaintext 复制代码
     Species len_c_across nrow_pick ncol_pick
1     setosa          100        50         2
2 versicolor          100        50         2
3  virginica          100        50         2

<tidy-select>中进行变量替换

<tidy-select>通常接受"裸露"的列名作为参数(如iris %>% select(Species, Petal.Width)中的SpeciesPetal.Width)。如果列名存储在变量中,则需要借助辅助函数或通过"注入"的方式将变量中的内容插入到参数中。

变量可以分为普通变量(var)和函数参数(arg)两种,后者还包括值传递(f("str")中的"str")、变量传递(f(var)中的var)和字面量("裸露"的列名)三种情况。此外<tidy-select>还可以接受列号(数字)作为参数。分别可以通过不同的方式将这些内容插入到<tidy-select>参数中:

r 复制代码
var1 <- 4 # 普通变量,列号(数字)
sp <- "Species" # 普通变量,列名(字符串)

iris %>% select(any_of(var1), !!sp)
plaintext 复制代码
   Petal.Width Species
1          0.2  setosa
2          0.2  setosa
3          0.2  setosa
4          0.2  setosa
5          0.2  setosa
6          0.4  setosa
7          0.3  setosa
8          0.2  setosa
9          0.2  setosa
10         0.1  setosa
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
r 复制代码
(function(arg1, arg2, arg3, arg4){
  iris %>% select(enexpr(arg1), all_of(arg2), sym(arg3), {{arg4}})
})(2, var1, sp, Sepal.Length) # 函数参数,分别为 值传递列号(数字),变量传递列号(数字),变量传递列名(字符串),字面量
plaintext 复制代码
   Sepal.Width Petal.Width Species Sepal.Length
1          3.5         0.2  setosa          5.1
2          3.0         0.2  setosa          4.9
3          3.2         0.2  setosa          4.7
4          3.1         0.2  setosa          4.6
5          3.6         0.2  setosa          5.0
6          3.9         0.4  setosa          5.4
7          3.4         0.3  setosa          4.6
8          3.4         0.2  setosa          5.0
9          2.9         0.2  setosa          4.4
10         3.1         0.1  setosa          4.9
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

虽然看上去花样很多,但实际用到的只有三类语法,分别是:

  • tidyselect::辅助函数any_of()all_of()
  • rlang引用格式sym()ensym()enexpr()和注入符!!
    • 以及这一格式的简写形式{{ }}

any_of()all_of()与前面提到的starts_with()ends_with()等函数类似,接受一个字符串向量并选取相应列名的列。如果列名不存在于表中,any_of()会忽略这些列,而all_of()则会报错。

sym()ensym()enexpr()都是expr()的变体,它们都属于rlang的defuse函数家族。这个函数家族主要用于以字面形式存储表达式(而不是运行后的结果)。例如,expr(1+1)返回+, 1, 1,而不是1+1的结果2。defuse后的表达式可以赋值给变量,用来传递给其他函数,或者在需要的时候运行(通过eval()函数)。

sym()的功能是将字符串转化为符号。符号是一类简单的表达式,它不包含任何运算,仅仅指代一个字面值。例如,sym("sp")的结果为sp,而sym(sp)的结果则为Species(因为之前给sp赋值了sp <- "Species")。注意,即使传入的字符串中包含了函数或运算,也会被强行转化为符号,而不是运算式。

enexpr()的功能是将函数参数转化为表达式。例如,(function(arg){expr(arg)})(1+1)返回的结果是arg,而(function(arg){enexpr(arg)})(1+1)则返回+, 1, 1ensym()enexpr()类似,但要求参数的内容本身是一个符号(如(function(arg){enexpr(arg)})(var)得到var,而(function(arg){enexpr(arg)})(1+1)会报错);ensym()也支持传入参数的内容是字符串,所以在一些情况下可以和sym()通用。

"注入符"!!在向函数传递变量时使用,功能是将变量的内容注入变量所在位置。例如,(function(arg){ensym(arg)})(!!sp)会得到(function(arg){ensym(arg)})(!!sp),而不是(function(arg){ensym(arg)})(sp)的结果sp。这可以用来向一些默认接受变量字面量的函数传递变量的内容。

{{ }}!!enexpr()的简写,可以简化书写,但有些情况下会导致不需要的注入。

下面的表格展示了各种情况下可以使用的变量替换操作:

变量类型(<-表示变量,=表示函数参数) 辅助函数 表达式 注入 {{ }}
var <- "Species" all_of(var) sym(var) !!var {{var}}
var <- 5 all_of(var) enexpr(var) !!var {{var}}
arg = "Species" all_of(arg) sym(arg) !!arg {{arg}}
arg = 5 all_of(arg) enexpr(arg) !!arg {{arg}}
var <- "Species";arg = var all_of(arg) sym(arg) !!arg any_of({{arg}})
var <- 5;arg = var all_of(arg) enexpr(arg) !!arg any_of({{arg}})
arg = Species / ensym(arg) / {{arg}}

<data-masking>中进行变量替换

<data-masking>中注入变量名的主要用到的函数是c_across(),该函数接受一个<tidy-select>表达式,将其转换为一组<data-masking>列。

虽然这不是c_across()最初被设计用作的目的(它最初是用来配合rowwise()让一些非向量化函数实现向量化操作的),但是确是一种有效的手段。

前面提到,c_across()会将选取列按向量方式拼接。因此,如果c_across()选取了超过一列,其返回结果的长度也会变成多倍,因此选取前最好确认一下需要替换的变量只包含一列的列号/列名。

另一种做法是利用sym()/ensym()!!,与<tidy-select>可以灵活使用引用格式和注入符号不同,在<data-masking>中两者必须连用,而且只支持列名:

r 复制代码
iris %>% transmute(!!sym(sp))
plaintext 复制代码
   Species
1   setosa
2   setosa
3   setosa
4   setosa
5   setosa
6   setosa
7   setosa
8   setosa
9   setosa
10  setosa
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
r 复制代码
(function(arg){
  iris %>% transmute(!!ensym(arg))
})(Petal.Length)
plaintext 复制代码
   Petal.Length
1           1.4
2           1.4
3           1.3
4           1.5
5           1.4
6           1.7
7           1.4
8           1.5
9           1.4
10          1.5
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

!!ensym()/!!enexpr(){{ }}格式不支持直接通过列号选取列,而且如果传入的是字符串变量,可能会得到不正确的结果(而且不会报错):

r 复制代码
iris %>% transmute(!!enexpr(sp))
plaintext 复制代码
   "Species"
1    Species
2    Species
3    Species
4    Species
5    Species
6    Species
7    Species
8    Species
9    Species
10   Species
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
r 复制代码
(function(arg1, arg2, arg3){
  iris %>% transmute(!!enexpr(arg1), !!enexpr(arg2), !!enexpr(arg3))
})(var1, sp, Petal.Width)
plaintext 复制代码
   var1      sp Petal.Width
1     4 Species         0.2
2     4 Species         0.2
3     4 Species         0.2
4     4 Species         0.2
5     4 Species         0.2
6     4 Species         0.4
7     4 Species         0.3
8     4 Species         0.2
9     4 Species         0.2
10    4 Species         0.1
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

如果传入!!enexpr()的是字面列名,但列名同时和一个变量名冲突(如Petal.Width<-"Petal.Length"),则列名会被优先解析(所以用Petal.Width传入列名的做法是安全的)。但如果传入!!enexpr()的是一个字符串变量,那么变量的内容会被注入,如果想要按照变量的内容选取列,请使用!!ensym()c_across()

下面的表格展示了各种情况下可以使用的变量替换操作:

变量类型(<-表示变量,=表示函数参数) c_across() 表达式注入
var <- "Species" c_across({{var}}) !!sym(var)
var <- 5 c_across({{var}}) /
arg = "Species" c_across({{arg}}) !!sym(arg)
arg = 5 c_across({{arg}}) /
var <- "Species";arg = var c_across(any_of(arg)) /
var <- 5;arg = var c_across(any_of(arg)) /
arg = Species / !!ensym(arg)

针对参数名的变量替换

变量替换的最后一种情况是用来指定新列的名称,可以用在rename()select()mutate()summarize()reframe()等支持。这可以通过引用注入(及简写{{ }})或glue字符串两种格式实现。

glue是一类模板字符串,用{}括起的变量名标记变量应当插入的位置。({}内也支持表达式,详见?glue::glue

当列名需要通过变量替换指定时,赋值符号需要修改为:=

r 复制代码
new_col <- "L_Petal"
iris %>% select("{str_to_lower(sp)}_name" := Species, {{new_col}} := Petal.Length)
plaintext 复制代码
   species_name L_Petal
1        setosa     1.4
2        setosa     1.4
3        setosa     1.3
4        setosa     1.5
5        setosa     1.4
6        setosa     1.7
7        setosa     1.4
8        setosa     1.5
9        setosa     1.4
10       setosa     1.5
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
r 复制代码
(function(arg1, arg2, arg3, arg4){
  iris %>% select({{arg1}} := 5, !!ensym(arg2) := 1, "{arg1}" := Sepal.Width, !!sym(arg4) := 3)
    # arg1会被替换为"sp",而不是"Species"
})(sp, L_Sepal, "W_Sepal", new_col)
plaintext 复制代码
       sp L_Sepal Species L_Petal
1  setosa     5.1     3.5     1.4
2  setosa     4.9     3.0     1.4
3  setosa     4.7     3.2     1.3
4  setosa     4.6     3.1     1.5
5  setosa     5.0     3.6     1.4
6  setosa     5.4     3.9     1.7
7  setosa     4.6     3.4     1.4
8  setosa     5.0     3.4     1.5
9  setosa     4.4     2.9     1.4
10 setosa     4.9     3.1     1.5
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

注意,由于!!ensym(){{ }}优先解析字面量,因此传入函数的变量名("sp")而不是变量的值"Species"会被使用。如果要注入变量的内容,请使用!!sym()

变量类型(<-表示变量,=表示函数参数) glue字符串 表达式注入 {{ }}
var <- "Species" "{var}" !!sym(var) {{var}}
arg = "Species" "{arg}" !!sym(arg) {{arg}}
var <- "Species";arg = var "{arg}" !!sym(arg) /
arg = Species / !!ensym(arg) {{arg}}

另一种做法

pipe支持通过代码块执行复杂的运算。

r 复制代码
storms %>% filter(any(wind>=160), .by=c(name, year)) %>% 
  mutate(id=paste(year, name)) %>% {
  id <- unique(.$id)
  n <- length(id)
  title <- paste("tracks for storms", paste0(id[-n], collapse = ", "), "and", id[n])
  ggplot(., aes(lat, long)) +
    geom_point(aes(color=id, size=wind)) +
    geom_path(aes(group=id)) +
    ggtitle(title)
}

在这个例子中,函数ggplot()和``ggtitle()都需要用到由%>%传递过来的数据,但无法在一个管道符表达式中写下,此时就可以通过代码块来实现。在代码块中,可以(也需要)用.`显式指定传入的数据,返回的是最后一句代码运行的结果。

%T>%是一类特殊的的管道符,与普通的%>%不同,它在函数执行后返回的不是函数执行的结果,而是数据本身。这一操作符通常用来在pipe的中间打印数据或绘图。与代码块配合后可以简写对数据的原地修改。

r 复制代码
iris %T>%
  {.$species_Name <- .[,sp]} %T>% # 相当于 mutate(species_Name=!!sym(sp))
  {.[,sp] <- NULL} %T>% # 相当于 mutate({{sp}}:=NULL)
  {. <- .[, 3:5]} # 相当于 select(3:5)
plaintext 复制代码
   Petal.Length Petal.Width species_Name
1           1.4         0.2       setosa
2           1.4         0.2       setosa
3           1.3         0.2       setosa
4           1.5         0.2       setosa
5           1.4         0.2       setosa
6           1.7         0.4       setosa
7           1.4         0.3       setosa
8           1.5         0.2       setosa
9           1.4         0.2       setosa
10          1.5         0.1       setosa
 [ reached 'max' / getOption("max.print") -- omitted 140 rows ]

代码块不会修改iris本身的值,只会修改每次%T>%传递过来的值。上述的每个代码块都用base-r格式进行了mutateselect操作。该做法不局限于用来进行tidyverse操作,具有很高的自由度。