找回密码
 立即注册
首页 业界区 业界 dplyr和tidyr用法

dplyr和tidyr用法

闰咄阅 前天 00:05
简介

tidyverse是一套数据分析套件包,它极大地简化和拓展了使用R语言进行数据分析的操作,涵盖了数据导入、数据处理和可视化等多方面的功能。
dplyr和tidyr是tidyverse的重要组成部分,前者主要用于数据的处理,后者则主要用于数据的格式调整。
两者的主要处理的对象是data.frame(及衍生的data.table和tibble),这是一种用于存储高度结构化数据的对象。你可以将data.frame视作一张表格,其每一行为一个条目,每一列为一项属性。同一列中的所有数据类型一致,而同一行则不一定。data.frame中的每列都可以被视为一个向量,在dplyr的许多操作中可以体会到这一点。
magrittr的pipe操作符%>%是一种可以很好地简化和美化链式函数调用的语法工具。函数表达式function(value, args)可以用pipe改写为value %>% function(args)。dplyr和tidyr的data.frame操作函数都很好地支持了pipe语法。在后文介绍dplyr和tidyr函数的参数格式时,我们默认使用pipe中的参数格式,省去函数的第一个参数,即要操作的data.frame对象。
示例数据
  1. iris
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
  2. 1           5.1         3.5          1.4         0.2  setosa
  3. 2           4.9         3.0          1.4         0.2  setosa
  4. 3           4.7         3.2          1.3         0.2  setosa
  5. 4           4.6         3.1          1.5         0.2  setosa
  6. 5           5.0         3.6          1.4         0.2  setosa
  7. 6           5.4         3.9          1.7         0.4  setosa
  8. 7           4.6         3.4          1.4         0.3  setosa
  9. 8           5.0         3.4          1.5         0.2  setosa
  10. 9           4.4         2.9          1.4         0.2  setosa
  11. 10          4.9         3.1          1.5         0.1  setosa
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
  1. storms
复制代码
  1. # A tibble: 19,537 × 13
  2.    name   year month   day  hour   lat  long status       category  wind pressure tropicalstorm_force_…¹
  3.    <chr> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <fct>           <dbl> <int>    <int>                  <int>
  4. 1 Amy    1975     6    27     0  27.5 -79   tropical de…       NA    25     1013                     NA
  5. 2 Amy    1975     6    27     6  28.5 -79   tropical de…       NA    25     1013                     NA
  6. 3 Amy    1975     6    27    12  29.5 -79   tropical de…       NA    25     1013                     NA
  7. 4 Amy    1975     6    27    18  30.5 -79   tropical de…       NA    25     1013                     NA
  8. 5 Amy    1975     6    28     0  31.5 -78.8 tropical de…       NA    25     1012                     NA
  9. 6 Amy    1975     6    28     6  32.4 -78.7 tropical de…       NA    25     1012                     NA
  10. 7 Amy    1975     6    28    12  33.3 -78   tropical de…       NA    25     1011                     NA
  11. 8 Amy    1975     6    28    18  34   -77   tropical de…       NA    30     1006                     NA
  12. 9 Amy    1975     6    29     0  34.4 -75.8 tropical st…       NA    35     1004                     NA
  13. 10 Amy    1975     6    29     6  34   -74.8 tropical st…       NA    40     1002                     NA
  14. # ℹ 19,527 more rows
  15. # ℹ abbreviated name: ¹​tropicalstorm_force_diameter
  16. # ℹ 1 more variable: hurricane_force_diameter <int>
复制代码
  1. data.frame(Titanic)
复制代码
  1.    Class    Sex   Age Survived Freq
  2. 1    1st   Male Child       No    0
  3. 2    2nd   Male Child       No    0
  4. 3    3rd   Male Child       No   35
  5. 4   Crew   Male Child       No    0
  6. 5    1st Female Child       No    0
  7. 6    2nd Female Child       No    0
  8. 7    3rd Female Child       No   17
  9. 8   Crew Female Child       No    0
  10. 9    1st   Male Adult       No  118
  11. 10   2nd   Male Adult       No  154
  12. [ reached 'max' / getOption("max.print") -- omitted 22 rows ]
复制代码
简单操作:行列操作、连接和调整

dplyr列操作

列操作包括选取(select)、排序(relocate)、重命名(rename)和编辑(mutate),列操作不会改变行的数量或顺序。
select用于从表中选取部分列。其参数格式为select(colname)或select(new_name=old_name),只有参数中指定的列会被保留。
  1. iris %>% select(Species, Width = Petal.Width, Length = Petal.Length)
复制代码
  1.    Species Width Length
  2. 1   setosa   0.2    1.4
  3. 2   setosa   0.2    1.4
  4. 3   setosa   0.2    1.3
  5. 4   setosa   0.2    1.5
  6. 5   setosa   0.2    1.4
  7. 6   setosa   0.4    1.7
  8. 7   setosa   0.3    1.4
  9. 8   setosa   0.2    1.5
  10. 9   setosa   0.2    1.4
  11. 10  setosa   0.1    1.5
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
relocate用于改变列的顺序,而不改变列的数目。其参数格式和select类似,为relocate(colname)或relocate(new_name=old_name)。.before/.after参数可以指定要移动到的位置,如果没有指定,参数中指定的列将会被移动到表的最前面(左侧)。
  1. iris %>% relocate(epithet=Species)
复制代码
  1.    epithet Sepal.Length Sepal.Width Petal.Length Petal.Width
  2. 1   setosa          5.1         3.5          1.4         0.2
  3. 2   setosa          4.9         3.0          1.4         0.2
  4. 3   setosa          4.7         3.2          1.3         0.2
  5. 4   setosa          4.6         3.1          1.5         0.2
  6. 5   setosa          5.0         3.6          1.4         0.2
  7. 6   setosa          5.4         3.9          1.7         0.4
  8. 7   setosa          4.6         3.4          1.4         0.3
  9. 8   setosa          5.0         3.4          1.5         0.2
  10. 9   setosa          4.4         2.9          1.4         0.2
  11. 10  setosa          4.9         3.1          1.5         0.1
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
rename用于对列进行重命名,而不会改变列的数量和位置,其参数格式为rename(new_name=old_name)。
  1. iris %>% rename(V1=Sepal.Length, V2=Sepal.Width)
复制代码
  1.     V1  V2 Petal.Length Petal.Width Species
  2. 1  5.1 3.5          1.4         0.2  setosa
  3. 2  4.9 3.0          1.4         0.2  setosa
  4. 3  4.7 3.2          1.3         0.2  setosa
  5. 4  4.6 3.1          1.5         0.2  setosa
  6. 5  5.0 3.6          1.4         0.2  setosa
  7. 6  5.4 3.9          1.7         0.4  setosa
  8. 7  4.6 3.4          1.4         0.3  setosa
  9. 8  5.0 3.4          1.5         0.2  setosa
  10. 9  4.4 2.9          1.4         0.2  setosa
  11. 10 4.9 3.1          1.5         0.1  setosa
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
mutate用于创建新列,也可以用来修改或移除已有的列。其参数格式为mutate(new_col=fn(old_col1, old_col2,...)),其中old_col是列名,其值会作为向量传入函数fn,fn的计算结果则作为新列new_col的值。fn结果向量的长度应当与表的行数一致或为1。如果新列名new_col已存在,那么列值会被覆盖。值被赋为NULL的列会则被移除。
  1. iris %>% mutate(Genus="Iris", epithet=Species, Species=paste(Genus, epithet))
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width     Species Genus epithet
  2. 1           5.1         3.5          1.4         0.2 Iris setosa  Iris  setosa
  3. 2           4.9         3.0          1.4         0.2 Iris setosa  Iris  setosa
  4. 3           4.7         3.2          1.3         0.2 Iris setosa  Iris  setosa
  5. 4           4.6         3.1          1.5         0.2 Iris setosa  Iris  setosa
  6. 5           5.0         3.6          1.4         0.2 Iris setosa  Iris  setosa
  7. 6           5.4         3.9          1.7         0.4 Iris setosa  Iris  setosa
  8. 7           4.6         3.4          1.4         0.3 Iris setosa  Iris  setosa
  9. 8           5.0         3.4          1.5         0.2 Iris setosa  Iris  setosa
  10. 9           4.4         2.9          1.4         0.2 Iris setosa  Iris  setosa
  11. 10          4.9         3.1          1.5         0.1 Iris setosa  Iris  setosa
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
dplyr行操作

行操作包括筛选(filter)、排序(arrange)、分片(slice)、汇总(summarize)和重构(reframe),以及简写操作计数(count和tally)和去重(distinct)
filter用于筛选满足指定条件的列。其参数为filter(fn(col1,col2,...)),其中col是列名,其值会作为向量传入函数fn,只有fn的计算结果为TRUE的列会被保留。
  1. iris %>% filter(Species=="virginica")
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
  2. 1           6.3         3.3          6.0         2.5 virginica
  3. 2           5.8         2.7          5.1         1.9 virginica
  4. 3           7.1         3.0          5.9         2.1 virginica
  5. 4           6.3         2.9          5.6         1.8 virginica
  6. 5           6.5         3.0          5.8         2.2 virginica
  7. 6           7.6         3.0          6.6         2.1 virginica
  8. 7           4.9         2.5          4.5         1.7 virginica
  9. 8           7.3         2.9          6.3         1.8 virginica
  10. 9           6.7         2.5          5.8         1.8 virginica
  11. 10          7.2         3.6          6.1         2.5 virginica
  12. [ reached 'max' / getOption("max.print") -- omitted 40 rows ]
复制代码
arrange用于按指定条件排序行。其参数格式为arrange(fn(...)),fn的计算结果将作为排序的依据。
  1. iris %>% arrange(desc(Species), -Sepal.Length)
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
  2. 1           7.9         3.8          6.4         2.0 virginica
  3. 2           7.7         3.8          6.7         2.2 virginica
  4. 3           7.7         2.6          6.9         2.3 virginica
  5. 4           7.7         2.8          6.7         2.0 virginica
  6. 5           7.7         3.0          6.1         2.3 virginica
  7. 6           7.6         3.0          6.6         2.1 virginica
  8. 7           7.4         2.8          6.1         1.9 virginica
  9. 8           7.3         2.9          6.3         1.8 virginica
  10. 9           7.2         3.6          6.1         2.5 virginica
  11. 10          7.2         3.2          6.0         1.8 virginica
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
slice用于选取指定的行,参数格式为slice(fn(...)),fn的计算结果应当为一个整数向量,对应行数的行将会被保留。除了slice外,还有取首行(slice_head)、取尾行(slice_tail)、取随机行(slice_sample)、取最大行(slice_max)和取最小行(slice_min)几个常用的辅助函数
  1. iris %>% slice_max(Petal.Width, n=10)
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
  2. 1           6.3         3.3          6.0         2.5 virginica
  3. 2           7.2         3.6          6.1         2.5 virginica
  4. 3           6.7         3.3          5.7         2.5 virginica
  5. 4           5.8         2.8          5.1         2.4 virginica
  6. 5           6.3         3.4          5.6         2.4 virginica
  7. 6           6.7         3.1          5.6         2.4 virginica
  8. 7           6.4         3.2          5.3         2.3 virginica
  9. 8           7.7         2.6          6.9         2.3 virginica
  10. 9           6.9         3.2          5.7         2.3 virginica
  11. 10          7.7         3.0          6.1         2.3 virginica
  12. [ reached 'max' / getOption("max.print") -- omitted 4 rows ]
复制代码
summarise用于将多行汇总为一行。其参数格式为summarise(col_name=fn(col1,col2,...)),其中col是原表格中的列名,函数fn的计算结果应当是一个长度为1的向量,col_name是汇总后的列名。
  1. iris %>% summarise(Petal.Width.Max=max(Petal.Width),Petal.Width.Min=min(Petal.Width))
复制代码
  1.   Petal.Width.Max Petal.Width.Min
  2. 1             2.5             0.1
复制代码
reframe与summarise类似,但是不限定fn计算结果的长度。如果fn计算结果的长度超过1,那么会生成多行。
  1. iris %>% reframe(Petal.Width=quantile(Petal.Width),Petal.Length=quantile(Petal.Length))
复制代码
  1.   Petal.Width Petal.Length
  2. 1         0.1         1.00
  3. 2         0.3         1.60
  4. 3         1.3         4.35
  5. 4         1.8         5.10
  6. 5         2.5         6.90
复制代码
dplyr表连接
  1. # 示例数据
  2. band_members
复制代码
  1. # A tibble: 3 × 2
  2.   name  band   
  3.   <chr> <chr>  
  4. 1 Mick  Stones
  5. 2 John  Beatles
  6. 3 Paul  Beatles
复制代码
  1. band_instruments
复制代码
  1. # A tibble: 3 × 2
  2.   name  plays
  3.   <chr> <chr>
  4. 1 John  guitar
  5. 2 Paul  bass  
  6. 3 Keith guitar
复制代码
表连接函数包括根据指定列合并表的inner_join、left_join、right_join、full_join和生成所有条目对枚举(笛卡尔积)的cross_join,以及用于筛选数据的semi_join和anti_join。
  1. band_members %>% inner_join(band_instruments)
复制代码
  1. # A tibble: 2 × 3
  2.   name  band    plays
  3.   <chr> <chr>   <chr>
  4. 1 John  Beatles guitar
  5. 2 Paul  Beatles bass  
复制代码
  1. band_members %>% left_join(band_instruments)
复制代码
  1. # A tibble: 3 × 3
  2.   name  band    plays
  3.   <chr> <chr>   <chr>
  4. 1 Mick  Stones  <NA>  
  5. 2 John  Beatles guitar
  6. 3 Paul  Beatles bass  
复制代码
  1. band_members %>% right_join(band_instruments)
复制代码
  1. # A tibble: 3 × 3
  2.   name  band    plays
  3.   <chr> <chr>   <chr>
  4. 1 John  Beatles guitar
  5. 2 Paul  Beatles bass  
  6. 3 Keith <NA>    guitar
复制代码
  1. band_members %>% full_join(band_instruments)
复制代码
  1. # A tibble: 4 × 3
  2.   name  band    plays
  3.   <chr> <chr>   <chr>
  4. 1 Mick  Stones  <NA>  
  5. 2 John  Beatles guitar
  6. 3 Paul  Beatles bass  
  7. 4 Keith <NA>    guitar
复制代码
  1. band_members %>% cross_join(band_instruments)
复制代码
  1. # A tibble: 9 × 4
  2.   name.x band    name.y plays
  3.   <chr>  <chr>   <chr>  <chr>
  4. 1 Mick   Stones  John   guitar
  5. 2 Mick   Stones  Paul   bass  
  6. 3 Mick   Stones  Keith  guitar
  7. 4 John   Beatles John   guitar
  8. 5 John   Beatles Paul   bass  
  9. 6 John   Beatles Keith  guitar
  10. 7 Paul   Beatles John   guitar
  11. 8 Paul   Beatles Paul   bass  
  12. 9 Paul   Beatles Keith  guitar
复制代码
  1. band_members %>% semi_join(band_instruments)
复制代码
  1. # A tibble: 2 × 2
  2.   name  band   
  3.   <chr> <chr>  
  4. 1 John  Beatles
  5. 2 Paul  Beatles
复制代码
  1. band_members %>% anti_join(band_instruments)
复制代码
  1. # A tibble: 1 × 2
  2.   name  band  
  3.   <chr> <chr>
  4. 1 Mick  Stones
复制代码
tidyr表调整

基础的表调整包括多列转单列的gather和单列转多列的spread。
gather可以将表格的列名转换为独立的列,同时保留列值和其他列数据的对应关系。其参数格式为gather("name_of_key_col", "name_of_value_col", cols_to_gather)
  1. iris %>% gather("measurement","value",Sepal.Length:Petal.Width)
复制代码
  1.    Species  measurement value
  2. 1   setosa Sepal.Length   5.1
  3. 2   setosa Sepal.Length   4.9
  4. 3   setosa Sepal.Length   4.7
  5. 4   setosa Sepal.Length   4.6
  6. 5   setosa Sepal.Length   5.0
  7. 6   setosa Sepal.Length   5.4
  8. 7   setosa Sepal.Length   4.6
  9. 8   setosa Sepal.Length   5.0
  10. 9   setosa Sepal.Length   4.4
  11. 10  setosa Sepal.Length   4.9
  12. [ reached 'max' / getOption("max.print") -- omitted 590 rows ]
复制代码
spread是gather的逆操作,能够根据指定的索引列,将一列数据转换为以索引列的值为列名的多列数据。其参数格式为spread(key_col, value_col)。spread不支持多列key_col或value_col,对于更高级的操作,参见pivot_wider
  1. data.frame(Titanic) %>% spread(Class, Freq)
复制代码
  1.      Sex   Age Survived 1st 2nd 3rd Crew
  2. 1   Male Child       No   0   0  35    0
  3. 2   Male Child      Yes   5  11  13    0
  4. 3   Male Adult       No 118 154 387  670
  5. 4   Male Adult      Yes  57  14  75  192
  6. 5 Female Child       No   0   0  17    0
  7. 6 Female Child      Yes   1  13  14    0
  8. 7 Female Adult       No   4  13  89    3
  9. 8 Female Adult      Yes 140  80  76   20
复制代码
进阶操作:分组和批处理

批量处理

across用于在mutate、summarize和reframe中对多列应用同一函数
  1. iris %>% mutate(across(Sepal.Length:Petal.Width, ~.x-mean(.x)))
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
  2. 1    -0.7433333  0.44266667       -2.358  -0.9993333  setosa
  3. 2    -0.9433333 -0.05733333       -2.358  -0.9993333  setosa
  4. 3    -1.1433333  0.14266667       -2.458  -0.9993333  setosa
  5. 4    -1.2433333  0.04266667       -2.258  -0.9993333  setosa
  6. 5    -0.8433333  0.54266667       -2.358  -0.9993333  setosa
  7. 6    -0.4433333  0.84266667       -2.058  -0.7993333  setosa
  8. 7    -1.2433333  0.34266667       -2.358  -0.8993333  setosa
  9. 8    -0.8433333  0.34266667       -2.258  -0.9993333  setosa
  10. 9    -1.4433333 -0.15733333       -2.358  -0.9993333  setosa
  11. 10   -0.9433333  0.04266667       -2.258  -1.0993333  setosa
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
across默认不会修改列名,如果要修改列名可以通过.names参数指定,可以用"{.col}"和"{.fn}"指代列名和函数名。 
  1. iris %>% reframe(q=seq(0,1,0.25),across(
  2.   Sepal.Length:Petal.Width, list(quantile=~quantile(.x,q),percentage=~quantile(range(.x),q)), .names="{.col}_{.fn}"
  3. ))
复制代码
  1.      q Sepal.Length_quantile Sepal.Length_percentage Sepal.Width_quantile Sepal.Width_percentage
  2. 1 0.00                   4.3                     4.3                  2.0                    2.0
  3. 2 0.25                   5.1                     5.2                  2.8                    2.6
  4. 3 0.50                   5.8                     6.1                  3.0                    3.2
  5. 4 0.75                   6.4                     7.0                  3.3                    3.8
  6. 5 1.00                   7.9                     7.9                  4.4                    4.4
  7.   Petal.Length_quantile Petal.Length_percentage Petal.Width_quantile Petal.Width_percentage
  8. 1                  1.00                   1.000                  0.1                    0.1
  9. 2                  1.60                   2.475                  0.3                    0.7
  10. 3                  4.35                   3.950                  1.3                    1.3
  11. 4                  5.10                   5.425                  1.8                    1.9
  12. 5                  6.90                   6.900                  2.5                    2.5
复制代码
if_any和if_all用于在filter中对多列应用同一函数,并对筛选结果取并集或交集
  1. iris %>% filter(if_any(Sepal.Length:Petal.Width,~.x>median(.x)))
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
  2. 1           5.1         3.5          1.4         0.2  setosa
  3. 2           4.7         3.2          1.3         0.2  setosa
  4. 3           4.6         3.1          1.5         0.2  setosa
  5. 4           5.0         3.6          1.4         0.2  setosa
  6. 5           5.4         3.9          1.7         0.4  setosa
  7. 6           4.6         3.4          1.4         0.3  setosa
  8. 7           5.0         3.4          1.5         0.2  setosa
  9. 8           4.9         3.1          1.5         0.1  setosa
  10. 9           5.4         3.7          1.5         0.2  setosa
  11. 10          4.8         3.4          1.6         0.2  setosa
  12. [ reached 'max' / getOption("max.print") -- omitted 113 rows ]
复制代码
  1. iris %>% filter(if_all(Sepal.Length:Petal.Width,~.x>median(.x)))
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
  2. 1           7.0         3.2          4.7         1.4 versicolor
  3. 2           6.4         3.2          4.5         1.5 versicolor
  4. 3           6.9         3.1          4.9         1.5 versicolor
  5. 4           6.3         3.3          4.7         1.6 versicolor
  6. 5           6.7         3.1          4.4         1.4 versicolor
  7. 6           5.9         3.2          4.8         1.8 versicolor
  8. 7           6.0         3.4          4.5         1.6 versicolor
  9. 8           6.7         3.1          4.7         1.5 versicolor
  10. 9           6.3         3.3          6.0         2.5  virginica
  11. 10          7.2         3.6          6.1         2.5  virginica
  12. [ reached 'max' / getOption("max.print") -- omitted 15 rows ]
复制代码
rename_with可以使用自定义函数对多列进行重命名
  1. iris %>% rename_with(~str_replace(.x, "(.*)\\.(.).*", "\\2_\\1"))
复制代码
  1.    L_Sepal W_Sepal L_Petal W_Petal Species
  2. 1      5.1     3.5     1.4     0.2  setosa
  3. 2      4.9     3.0     1.4     0.2  setosa
  4. 3      4.7     3.2     1.3     0.2  setosa
  5. 4      4.6     3.1     1.5     0.2  setosa
  6. 5      5.0     3.6     1.4     0.2  setosa
  7. 6      5.4     3.9     1.7     0.4  setosa
  8. 7      4.6     3.4     1.4     0.3  setosa
  9. 8      5.0     3.4     1.5     0.2  setosa
  10. 9      4.4     2.9     1.4     0.2  setosa
  11. 10     4.9     3.1     1.5     0.1  setosa
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
多列赋值

通常,mutate函数的一个参数一次只能返回一列。如果想要一次返回多列,可以向将表转换为tibble对象,然后让fn返回一个data.frame:
  1. as_tibble(iris) %>%
  2.   mutate(as.data.frame(prcomp(t(pick(Sepal.Length:Petal.Width)))$rotation))
复制代码
  1. # A tibble: 150 × 9
  2.    Sepal.Length Sepal.Width Petal.Length Petal.Width Species     PC1     PC2      PC3      PC4
  3.           <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>   <dbl>    <dbl>    <dbl>
  4. 1          5.1         3.5          1.4         0.2 setosa  -0.0771 -0.121   0.00327 -0.757  
  5. 2          4.9         3            1.4         0.2 setosa  -0.0754 -0.0995  0.0824  -0.282  
  6. 3          4.7         3.2          1.3         0.2 setosa  -0.0709 -0.110   0.0110  -0.0304
  7. 4          4.6         3.1          1.5         0.2 setosa  -0.0701 -0.0968 -0.0312  -0.0110
  8. 5          5           3.6          1.4         0.2 setosa  -0.0751 -0.124  -0.0436   0.0337
  9. 6          5.4         3.9          1.7         0.4 setosa  -0.0785 -0.126  -0.0463   0.00279
  10. 7          4.6         3.4          1.4         0.3 setosa  -0.0671 -0.112  -0.0580   0.00412
  11. 8          5           3.4          1.5         0.2 setosa  -0.0760 -0.112  -0.0166   0.0501
  12. 9          4.4         2.9          1.4         0.2 setosa  -0.0670 -0.0914 -0.00383  0.00380
  13. 10          4.9         3.1          1.5         0.1 setosa  -0.0769 -0.0999  0.0104   0.0773
  14. # ℹ 140 more rows
复制代码
分组操作

mutate和行操作支持按特定列分组(group_by和rowwise,或by/.by参数),分组后每组会独立应用函数。
可以使用group_by将表格转换为grouped_df,对grouped_df的所有操作均会自动按组进行。ungroup可以取消分组。
  1. iris %>% group_by(Species) %>%
  2.   summarize(mean.L_Sepal=mean(Sepal.Length), mean.L_Petal=mean(Petal.Length), n.Sample=n()) %>%
  3.   ungroup()
复制代码
  1. # A tibble: 3 × 4
  2.   Species    mean.L_Sepal mean.L_Petal n.Sample
  3.   <fct>             <dbl>        <dbl>    <int>
  4. 1 setosa             5.01         1.46       50
  5. 2 versicolor         5.94         4.26       50
  6. 3 virginica          6.59         5.55       50
复制代码
也可以在操作函数中直接指定.by参数
  1. iris %>% reframe(q=seq(0,1,0.25), L_Sepal=quantile(Sepal.Length, q), .by=Species)
复制代码
  1.       Species    q L_Sepal
  2. 1      setosa 0.00     4.3
  3. 2      setosa 0.25     4.8
  4. 3      setosa 0.50     5.0
  5. 4      setosa 0.75     5.2
  6. 5      setosa 1.00     5.8
  7. 6  versicolor 0.00     4.9
  8. 7  versicolor 0.25     5.6
  9. 8  versicolor 0.50     5.9
  10. 9  versicolor 0.75     6.3
  11. 10 versicolor 1.00     7.0
  12. [ reached 'max' / getOption("max.print") -- omitted 5 rows ]
复制代码
  1. iris %>% filter(if_all(Sepal.Length:Petal.Width,~.x>median(.x)),.by=Species)
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
  2. 1           5.4         3.9          1.7         0.4     setosa
  3. 2           5.7         3.8          1.7         0.3     setosa
  4. 3           5.1         3.8          1.9         0.4     setosa
  5. 4           7.0         3.2          4.7         1.4 versicolor
  6. 5           6.4         3.2          4.5         1.5 versicolor
  7. 6           6.9         3.1          4.9         1.5 versicolor
  8. 7           6.3         3.3          4.7         1.6 versicolor
  9. 8           6.1         2.9          4.7         1.4 versicolor
  10. 9           6.7         3.1          4.4         1.4 versicolor
  11. 10          6.6         3.0          4.4         1.4 versicolor
  12. [ reached 'max' / getOption("max.print") -- omitted 12 rows ]
复制代码
arrange函数默认忽略分组,且不支持.by/by参数。指定了.by_group=TRUE后,arrage会先对grouped_df的分组列进行排序,然后在各个组内分别按指定的列或计算结果进行排序。
  1. data.frame(Titanic) %>% group_by(Class) %>% arrange(Freq, .by_group=TRUE)
复制代码
  1. # A tibble: 32 × 5
  2. # Groups:   Class [4]
  3.    Class Sex    Age   Survived  Freq
  4.    <fct> <fct>  <fct> <fct>    <dbl>
  5. 1 1st   Male   Child No           0
  6. 2 1st   Female Child No           0
  7. 3 1st   Female Child Yes          1
  8. 4 1st   Female Adult No           4
  9. 5 1st   Male   Child Yes          5
  10. 6 1st   Male   Adult Yes         57
  11. 7 1st   Male   Adult No         118
  12. 8 1st   Female Adult Yes        140
  13. 9 2nd   Male   Child No           0
  14. 10 2nd   Female Child No           0
  15. # ℹ 22 more rows
复制代码
对grouped_df分组列的排序不支持自定义函数,如果要根据计算结果排列组,可以先用mutate生产索引列,或者利用ave函数:
  1. data.frame(Titanic) %>% mutate(Class.Freq=sum(Freq),.by=Class) %>% arrange(Class.Freq)
复制代码
  1.    Class    Sex   Age Survived Freq Class.Freq
  2. 1    2nd   Male Child       No    0        285
  3. 2    2nd Female Child       No    0        285
  4. 3    2nd   Male Adult       No  154        285
  5. 4    2nd Female Adult       No   13        285
  6. 5    2nd   Male Child      Yes   11        285
  7. 6    2nd Female Child      Yes   13        285
  8. 7    2nd   Male Adult      Yes   14        285
  9. 8    2nd Female Adult      Yes   80        285
  10. 9    1st   Male Child       No    0        325
  11. 10   1st Female Child       No    0        325
  12. [ reached 'max' / getOption("max.print") -- omitted 22 rows ]
复制代码
  1. data.frame(Titanic) %>% arrange(ave(Freq,Class,FUN=sum))
复制代码
  1.    Class    Sex   Age Survived Freq
  2. 1    2nd   Male Child       No    0
  3. 2    2nd Female Child       No    0
  4. 3    2nd   Male Adult       No  154
  5. 4    2nd Female Adult       No   13
  6. 5    2nd   Male Child      Yes   11
  7. 6    2nd Female Child      Yes   13
  8. 7    2nd   Male Adult      Yes   14
  9. 8    2nd Female Adult      Yes   80
  10. 9    1st   Male Child       No    0
  11. 10   1st Female Child       No    0
  12. [ reached 'max' / getOption("max.print") -- omitted 22 rows ]
复制代码
表调整

pivot_longer和pivot_wider可以实现比gather和spread更为精细和复杂的列转换操作。
pivot_longer的参数格式为pivot_longer(cols_to_gather, names_to="name_of_key_col", values_to="name_of_value_col"),另外还提供names_pattern、names_transform等可选参数对要转换的列名进行拆分和格式转换。
  1. iris %>% pivot_longer(
  2.   Sepal.Length:Petal.Width,
  3.   names_to=c("component","measure"), names_pattern="(.*)\\.(.*)",
  4.   values_to=c("value")
  5. )
复制代码
  1. # A tibble: 600 × 4
  2.    Species component measure value
  3.    <fct>   <chr>     <chr>   <dbl>
  4. 1 setosa  Sepal     Length    5.1
  5. 2 setosa  Sepal     Width     3.5
  6. 3 setosa  Petal     Length    1.4
  7. 4 setosa  Petal     Width     0.2
  8. 5 setosa  Sepal     Length    4.9
  9. 6 setosa  Sepal     Width     3  
  10. 7 setosa  Petal     Length    1.4
  11. 8 setosa  Petal     Width     0.2
  12. 9 setosa  Sepal     Length    4.7
  13. 10 setosa  Sepal     Width     3.2
  14. # ℹ 590 more rows
复制代码
如果只想将列名中的一部分内容转移为新行,可以使用names_to中的保留字".value"。匹配该部分的内容将继续保留为列名。
  1. iris %>% pivot_longer(
  2.   Sepal.Length:Petal.Width,
  3.   names_to=c("component",".value"), names_pattern="(.*)\\.(.*)"
  4. )
复制代码
  1. # A tibble: 300 × 4
  2.    Species component Length Width
  3.    <fct>   <chr>      <dbl> <dbl>
  4. 1 setosa  Sepal        5.1   3.5
  5. 2 setosa  Petal        1.4   0.2
  6. 3 setosa  Sepal        4.9   3  
  7. 4 setosa  Petal        1.4   0.2
  8. 5 setosa  Sepal        4.7   3.2
  9. 6 setosa  Petal        1.3   0.2
  10. 7 setosa  Sepal        4.6   3.1
  11. 8 setosa  Petal        1.5   0.2
  12. 9 setosa  Sepal        5     3.6
  13. 10 setosa  Petal        1.4   0.2
  14. # ℹ 290 more rows
复制代码
pivot_wider的参数格式为pivot_wider(id_cols=id_col, names_from=key_col, values_from=value_col),其中id_col是转换后仍要保留的列(如果留空,则使用key_col和value_col以外所有的列),key_col是要转换为列名的列,value_col列的数据则会转换为对应列的值。可选参数names_glue可以设置列名格式,values_fill可以设置缺失值的替换值。
  1. data.frame(Titanic) %>% pivot_wider(
  2.   names_from=c(Sex,Class), names_glue="{Sex}({Class})",
  3.   values_from=Freq
  4. )
复制代码
  1. # A tibble: 4 × 10
  2.   Age   Survived `Male(1st)` `Male(2nd)` `Male(3rd)` `Male(Crew)` `Female(1st)` `Female(2nd)`
  3.   <fct> <fct>          <dbl>       <dbl>       <dbl>        <dbl>         <dbl>         <dbl>
  4. 1 Child No                 0           0          35            0             0             0
  5. 2 Adult No               118         154         387          670             4            13
  6. 3 Child Yes                5          11          13            0             1            13
  7. 4 Adult Yes               57          14          75          192           140            80
  8. # ℹ 2 more variables: `Female(3rd)` <dbl>, `Female(Crew)` <dbl>
复制代码
如果一组id_col和key_col对应于多条value_col,可以通过values_fn参数指定数据的汇总方式
  1. data.frame(Titanic) %>% pivot_wider(
  2.   id_cols=c(Sex,Survived),
  3.   names_from=Class,
  4.   values_from=Freq, values_fn=sum
  5. )
复制代码
  1. # A tibble: 4 × 6
  2.   Sex    Survived `1st` `2nd` `3rd`  Crew
  3.   <fct>  <fct>    <dbl> <dbl> <dbl> <dbl>
  4. 1 Male   No         118   154   422   670
  5. 2 Female No           4    13   106     3
  6. 3 Male   Yes         62    25    88   192
  7. 4 Female Yes        141    93    90    20
复制代码
高级用法:变量替换

如前文所见,dplyr函数的一大特征是:在函数中,列名可以像变量一样使用。这为简单操作带来方便的同时,也给更进一步的高级操作带来了很大的麻烦。
一个最大的问题是:如何告诉函数某个变量指代的是列名还是来自全局环境中的变量呢?
这样的操作主要通过rlang包来实现。
预备知识:和

在进一步演示之前,先指出dplyr中的两种看起来相似但又略有不同的参数类型:和。
被用于filter()、mutate()、summarize()、reframe()、arrange()、slice()、count()、tally()、distinct()等可以对列做计算的函数中,用于将列作为参数传递给函数;而则主要被用在select()、rename()、relocate()、gather()、spread()函数,以及前述函数的.by参数中,用于对列进行选取。
我们首先介绍相对简单的的用法。
选取列的语法有两种风格,一种类似于base-r中的向量操作,另一种类似于集合操作符。

  • base-r向量操作风格

    • :表示连续范围
    • c()表示并集
    • -表示排除

  • 集合操作风格

    • &表示交集
    • |表示并集
    • !表示排除

这些算符/函数既接受数字,也接受列名:
  1. storms %>% select(c(2:5, status:pressure&!category), 1, latitude = lat, longitude = long)
复制代码
  1. # A tibble: 19,537 × 10
  2.     year month   day  hour status               wind pressure name  latitude longitude
  3.    <dbl> <dbl> <int> <dbl> <fct>               <int>    <int> <chr>    <dbl>     <dbl>
  4. 1  1975     6    27     0 tropical depression    25     1013 Amy       27.5     -79  
  5. 2  1975     6    27     6 tropical depression    25     1013 Amy       28.5     -79  
  6. 3  1975     6    27    12 tropical depression    25     1013 Amy       29.5     -79  
  7. 4  1975     6    27    18 tropical depression    25     1013 Amy       30.5     -79  
  8. 5  1975     6    28     0 tropical depression    25     1012 Amy       31.5     -78.8
  9. 6  1975     6    28     6 tropical depression    25     1012 Amy       32.4     -78.7
  10. 7  1975     6    28    12 tropical depression    25     1011 Amy       33.3     -78  
  11. 8  1975     6    28    18 tropical depression    30     1006 Amy       34       -77  
  12. 9  1975     6    29     0 tropical storm         35     1004 Amy       34.4     -75.8
  13. 10  1975     6    29     6 tropical storm         40     1002 Amy       34       -74.8
  14. # ℹ 19,527 more rows
复制代码
如果以...的形式传入函数(如select()、relocate())中,,分隔项会自动用|(并)连接;如果以单个参数的形式传入函数,则需要用c()或|将各项手动连起来。另外,,支持用=修改选取列的列名,而且会保留列的顺序,因此也可以用来对列进行重命名和重排。
还支持一些函数来指定要选取的列,包括:
函数作用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函数的使用示例:
  1. airquality %>% select(where(~ is.integer(.x) && !any(is.na(.x))))
复制代码
  1.    Temp Month Day
  2. 1    67     5   1
  3. 2    72     5   2
  4. 3    74     5   3
  5. 4    62     5   4
  6. 5    56     5   5
  7. 6    66     5   6
  8. 7    65     5   7
  9. 8    59     5   8
  10. 9    61     5   9
  11. 10   69     5  10
  12. [ reached 'max' / getOption("max.print") -- omitted 143 rows ]
复制代码
其中~和.x是purrr匿名函数简写格式,详细可见?purrr::as_mapper。
则是一个(或多个)以列名为变量名的表达式,用于对列表进行修改或汇总。
在表达式中,列名指定的列将以向量的形式传入函数或算符中参与运算。
例如,
  1. iris %>% mutate(LW.ratio_Petal = Petal.Length / Petal.Width)
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width Species LW.ratio_Petal
  2. 1           5.1         3.5          1.4         0.2  setosa       7.000000
  3. 2           4.9         3.0          1.4         0.2  setosa       7.000000
  4. 3           4.7         3.2          1.3         0.2  setosa       6.500000
  5. 4           4.6         3.1          1.5         0.2  setosa       7.500000
  6. 5           5.0         3.6          1.4         0.2  setosa       7.000000
  7. 6           5.4         3.9          1.7         0.4  setosa       4.250000
  8. 7           4.6         3.4          1.4         0.3  setosa       4.666667
  9. 8           5.0         3.4          1.5         0.2  setosa       7.500000
  10. 9           4.4         2.9          1.4         0.2  setosa       7.000000
  11. 10          4.9         3.1          1.5         0.1  setosa      15.000000
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
就是计算iris$Petal.Length/iris$Petal.Width的结果并保存到LW.ratio_Petal列中。
如果使用的是一些非向量化的函数,那么得到会得到一些非预期的结果:
  1. iris %>% mutate(new_col=sum(Petal.Length, Petal.Width))
复制代码
  1.    Sepal.Length Sepal.Width Petal.Length Petal.Width Species new_col
  2. 1           5.1         3.5          1.4         0.2  setosa   743.6
  3. 2           4.9         3.0          1.4         0.2  setosa   743.6
  4. 3           4.7         3.2          1.3         0.2  setosa   743.6
  5. 4           4.6         3.1          1.5         0.2  setosa   743.6
  6. 5           5.0         3.6          1.4         0.2  setosa   743.6
  7. 6           5.4         3.9          1.7         0.4  setosa   743.6
  8. 7           4.6         3.4          1.4         0.3  setosa   743.6
  9. 8           5.0         3.4          1.5         0.2  setosa   743.6
  10. 9           4.4         2.9          1.4         0.2  setosa   743.6
  11. 10          4.9         3.1          1.5         0.1  setosa   743.6
  12. [ reached 'max' / getOption("max.print") -- omitted 140 rows ]
复制代码
可以看到,new_col的值均为743.6,这是因为实际进行的运算变成了sum(iris$Petal.Length, iris$Petal.Width),得到的单一值被填充为一列,得到了全为743.6的new_col
通过辅助函数c_across()和pick()可以在中以的格式选取列,前者会选取列拼接为向量,而后者会选取列为表:
  1. iris %>% reframe(
  2.   len_c_across=length(c_across(c(Petal.Length,Petal.Width))),
  3.   nrow_pick=nrow(pick(Petal.Length,Petal.Width)),
  4.   ncol_pick=ncol(pick(Petal.Length,Petal.Width)),
  5.   .by=Species)
复制代码
  1.      Species len_c_across nrow_pick ncol_pick
  2. 1     setosa          100        50         2
  3. 2 versicolor          100        50         2
  4. 3  virginica          100        50         2
复制代码
在中进行变量替换

通常接受“裸露”的列名作为参数(如iris %>% select(Species, Petal.Width)中的Species和Petal.Width)。如果列名存储在变量中,则需要借助辅助函数或通过“注入”的方式将变量中的内容插入到参数中。
变量可以分为普通变量(var)和函数参数(arg)两种,后者还包括值传递(f("str")中的"str")、变量传递(f(var)中的var)和字面量(“裸露”的列名)三种情况。此外还可以接受列号(数字)作为参数。分别可以通过不同的方式将这些内容插入到参数中:
  1. var1 <- 4 # 普通变量,列号(数字)
  2. sp <- "Species" # 普通变量,列名(字符串)
  3. iris %>% select(any_of(var1), !!sp)
复制代码
  1.    Petal.Width Species
  2. 1          0.2  setosa
  3. 2          0.2  setosa
  4. 3          0.2  setosa
  5. 4          0.2  setosa
  6. 5          0.2  setosa
  7. 6          0.4  setosa
  8. 7          0.3  setosa
  9. 8          0.2  setosa
  10. 9          0.2  setosa
  11. 10         0.1  setosa
  12. [ 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

相关推荐

您需要登录后才可以回帖 登录 | 立即注册