简介
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对象。
示例数据- 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 ]
复制代码- # 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>
复制代码- 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),只有参数中指定的列会被保留。- iris %>% select(Species, Width = Petal.Width, Length = Petal.Length)
复制代码- 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参数可以指定要移动到的位置,如果没有指定,参数中指定的列将会被移动到表的最前面(左侧)。- iris %>% relocate(epithet=Species)
复制代码- 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)。- iris %>% rename(V1=Sepal.Length, V2=Sepal.Width)
复制代码- 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是列名,其值会作为向量传入函数fn,fn的计算结果则作为新列new_col的值。fn结果向量的长度应当与表的行数一致或为1。如果新列名new_col已存在,那么列值会被覆盖。值被赋为NULL的列会则被移除。- iris %>% mutate(Genus="Iris", epithet=Species, Species=paste(Genus, epithet))
复制代码- 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),以及简写操作计数(count和tally)和去重(distinct)
filter用于筛选满足指定条件的列。其参数为filter(fn(col1,col2,...)),其中col是列名,其值会作为向量传入函数fn,只有fn的计算结果为TRUE的列会被保留。- iris %>% filter(Species=="virginica")
复制代码- 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的计算结果将作为排序的依据。- iris %>% arrange(desc(Species), -Sepal.Length)
复制代码- 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)几个常用的辅助函数- iris %>% slice_max(Petal.Width, n=10)
复制代码- 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是原表格中的列名,函数fn的计算结果应当是一个长度为1的向量,col_name是汇总后的列名。- iris %>% summarise(Petal.Width.Max=max(Petal.Width),Petal.Width.Min=min(Petal.Width))
复制代码- Petal.Width.Max Petal.Width.Min
- 1 2.5 0.1
复制代码 reframe与summarise类似,但是不限定fn计算结果的长度。如果fn计算结果的长度超过1,那么会生成多行。- iris %>% reframe(Petal.Width=quantile(Petal.Width),Petal.Length=quantile(Petal.Length))
复制代码- 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表连接
- # A tibble: 3 × 2
- name band
- <chr> <chr>
- 1 Mick Stones
- 2 John Beatles
- 3 Paul Beatles
复制代码- # A tibble: 3 × 2
- name plays
- <chr> <chr>
- 1 John guitar
- 2 Paul bass
- 3 Keith guitar
复制代码 表连接函数包括根据指定列合并表的inner_join、left_join、right_join、full_join和生成所有条目对枚举(笛卡尔积)的cross_join,以及用于筛选数据的semi_join和anti_join。- band_members %>% inner_join(band_instruments)
复制代码- # A tibble: 2 × 3
- name band plays
- <chr> <chr> <chr>
- 1 John Beatles guitar
- 2 Paul Beatles bass
复制代码- band_members %>% left_join(band_instruments)
复制代码- # A tibble: 3 × 3
- name band plays
- <chr> <chr> <chr>
- 1 Mick Stones <NA>
- 2 John Beatles guitar
- 3 Paul Beatles bass
复制代码- band_members %>% right_join(band_instruments)
复制代码- # A tibble: 3 × 3
- name band plays
- <chr> <chr> <chr>
- 1 John Beatles guitar
- 2 Paul Beatles bass
- 3 Keith <NA> guitar
复制代码- band_members %>% full_join(band_instruments)
复制代码- # 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
复制代码- band_members %>% cross_join(band_instruments)
复制代码- # 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
复制代码- band_members %>% semi_join(band_instruments)
复制代码- # A tibble: 2 × 2
- name band
- <chr> <chr>
- 1 John Beatles
- 2 Paul Beatles
复制代码- band_members %>% anti_join(band_instruments)
复制代码- # 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)- iris %>% gather("measurement","value",Sepal.Length:Petal.Width)
复制代码- 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 ]
复制代码 spread是gather的逆操作,能够根据指定的索引列,将一列数据转换为以索引列的值为列名的多列数据。其参数格式为spread(key_col, value_col)。spread不支持多列key_col或value_col,对于更高级的操作,参见pivot_wider- data.frame(Titanic) %>% spread(Class, Freq)
复制代码- 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用于在mutate、summarize和reframe中对多列应用同一函数- iris %>% mutate(across(Sepal.Length:Petal.Width, ~.x-mean(.x)))
复制代码- 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}"指代列名和函数名。 - 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}"
- ))
复制代码- 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_any和if_all用于在filter中对多列应用同一函数,并对筛选结果取并集或交集- iris %>% filter(if_any(Sepal.Length:Petal.Width,~.x>median(.x)))
复制代码- 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 ]
复制代码- iris %>% filter(if_all(Sepal.Length:Petal.Width,~.x>median(.x)))
复制代码- 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可以使用自定义函数对多列进行重命名- iris %>% rename_with(~str_replace(.x, "(.*)\\.(.).*", "\\2_\\1"))
复制代码- 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:- as_tibble(iris) %>%
- mutate(as.data.frame(prcomp(t(pick(Sepal.Length:Petal.Width)))$rotation))
复制代码- # 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_by和rowwise,或by/.by参数),分组后每组会独立应用函数。
可以使用group_by将表格转换为grouped_df,对grouped_df的所有操作均会自动按组进行。ungroup可以取消分组。- iris %>% group_by(Species) %>%
- summarize(mean.L_Sepal=mean(Sepal.Length), mean.L_Petal=mean(Petal.Length), n.Sample=n()) %>%
- ungroup()
复制代码- # 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参数- iris %>% reframe(q=seq(0,1,0.25), L_Sepal=quantile(Sepal.Length, q), .by=Species)
复制代码- 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 ]
复制代码- iris %>% filter(if_all(Sepal.Length:Petal.Width,~.x>median(.x)),.by=Species)
复制代码- 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的分组列进行排序,然后在各个组内分别按指定的列或计算结果进行排序。- data.frame(Titanic) %>% group_by(Class) %>% arrange(Freq, .by_group=TRUE)
复制代码- # 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函数:- data.frame(Titanic) %>% mutate(Class.Freq=sum(Freq),.by=Class) %>% arrange(Class.Freq)
复制代码- 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 ]
复制代码- data.frame(Titanic) %>% arrange(ave(Freq,Class,FUN=sum))
复制代码- 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_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等可选参数对要转换的列名进行拆分和格式转换。- iris %>% pivot_longer(
- Sepal.Length:Petal.Width,
- names_to=c("component","measure"), names_pattern="(.*)\\.(.*)",
- values_to=c("value")
- )
复制代码- # 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"。匹配该部分的内容将继续保留为列名。- iris %>% pivot_longer(
- Sepal.Length:Petal.Width,
- names_to=c("component",".value"), names_pattern="(.*)\\.(.*)"
- )
复制代码- # 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_col和value_col以外所有的列),key_col是要转换为列名的列,value_col列的数据则会转换为对应列的值。可选参数names_glue可以设置列名格式,values_fill可以设置缺失值的替换值。- data.frame(Titanic) %>% pivot_wider(
- names_from=c(Sex,Class), names_glue="{Sex}({Class})",
- values_from=Freq
- )
复制代码- # 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_col和key_col对应于多条value_col,可以通过values_fn参数指定数据的汇总方式- data.frame(Titanic) %>% pivot_wider(
- id_cols=c(Sex,Survived),
- names_from=Class,
- values_from=Freq, values_fn=sum
- )
复制代码- # 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包来实现。
预备知识:和
在进一步演示之前,先指出dplyr中的两种看起来相似但又略有不同的参数类型:和。
被用于filter()、mutate()、summarize()、reframe()、arrange()、slice()、count()、tally()、distinct()等可以对列做计算的函数中,用于将列作为参数传递给函数;而则主要被用在select()、rename()、relocate()、gather()、spread()函数,以及前述函数的.by参数中,用于对列进行选取。
我们首先介绍相对简单的的用法。
选取列的语法有两种风格,一种类似于base-r中的向量操作,另一种类似于集合操作符。
这些算符/函数既接受数字,也接受列名:- storms %>% select(c(2:5, status:pressure&!category), 1, latitude = lat, longitude = long)
复制代码- # 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
复制代码 如果以...的形式传入函数(如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函数的使用示例:- airquality %>% select(where(~ is.integer(.x) && !any(is.na(.x))))
复制代码- 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 ]
复制代码 其中~和.x是purrr匿名函数简写格式,详细可见?purrr::as_mapper。
则是一个(或多个)以列名为变量名的表达式,用于对列表进行修改或汇总。
在表达式中,列名指定的列将以向量的形式传入函数或算符中参与运算。
例如,- iris %>% mutate(LW.ratio_Petal = Petal.Length / Petal.Width)
复制代码- 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列中。
如果使用的是一些非向量化的函数,那么得到会得到一些非预期的结果:- iris %>% mutate(new_col=sum(Petal.Length, Petal.Width))
复制代码- 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.6的new_col
通过辅助函数c_across()和pick()可以在中以的格式选取列,前者会选取列拼接为向量,而后者会选取列为表:- 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)
复制代码- Species len_c_across nrow_pick ncol_pick
- 1 setosa 100 50 2
- 2 versicolor 100 50 2
- 3 virginica 100 50 2
复制代码 在中进行变量替换
通常接受“裸露”的列名作为参数(如iris %>% select(Species, Petal.Width)中的Species和Petal.Width)。如果列名存储在变量中,则需要借助辅助函数或通过“注入”的方式将变量中的内容插入到参数中。
变量可以分为普通变量(var)和函数参数(arg)两种,后者还包括值传递(f("str")中的"str")、变量传递(f(var)中的var)和字面量(“裸露”的列名)三种情况。此外还可以接受列号(数字)作为参数。分别可以通过不同的方式将这些内容插入到参数中:- var1 <- 4 # 普通变量,列号(数字)
- sp <- "Species" # 普通变量,列名(字符串)
- iris %>% select(any_of(var1), !!sp)
复制代码- 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 ]
复制代码 虽然看上去花样很多,但实际用到的只有三类语法,分别是:
- 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 |