dplyr和tidyr用法
简介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对象。
示例数据iris Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2setosa
2 4.9 3.0 1.4 0.2setosa
3 4.7 3.2 1.3 0.2setosa
4 4.6 3.1 1.5 0.2setosa
5 5.0 3.6 1.4 0.2setosa
6 5.4 3.9 1.7 0.4setosa
7 4.6 3.4 1.4 0.3setosa
8 5.0 3.4 1.5 0.2setosa
9 4.4 2.9 1.4 0.2setosa
10 4.9 3.1 1.5 0.1setosa
[ reached 'max' / getOption("max.print") -- omitted 140 rows ]storms# A tibble: 19,537 × 13
name year month dayhour latlong status categorywind pressure tropicalstorm_force_…¹
<chr> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <fct> <dbl> <int> <int> <int>
1 Amy 1975 6 27 027.5 -79 tropical de… NA 25 1013 NA
2 Amy 1975 6 27 628.5 -79 tropical de… NA 25 1013 NA
3 Amy 1975 6 27 1229.5 -79 tropical de… NA 25 1013 NA
4 Amy 1975 6 27 1830.5 -79 tropical de… NA 25 1013 NA
5 Amy 1975 6 28 031.5 -78.8 tropical de… NA 25 1012 NA
6 Amy 1975 6 28 632.4 -78.7 tropical de… NA 25 1012 NA
7 Amy 1975 6 28 1233.3 -78 tropical de… NA 25 1011 NA
8 Amy 1975 6 28 1834 -77 tropical de… NA 30 1006 NA
9 Amy 1975 6 29 034.4 -75.8 tropical st… NA 35 1004 NA
10 Amy 1975 6 29 634 -74.8 tropical st… NA 40 1002 NA
# ℹ 19,527 more rows
# ℹ abbreviated name: ¹tropicalstorm_force_diameter
# ℹ 1 more variable: hurricane_force_diameter <int>data.frame(Titanic) 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 No118
10 2nd Male Adult No154
[ 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
10setosa 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
10setosa 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) V1V2 Petal.Length Petal.Width Species
15.1 3.5 1.4 0.2setosa
24.9 3.0 1.4 0.2setosa
34.7 3.2 1.3 0.2setosa
44.6 3.1 1.5 0.2setosa
55.0 3.6 1.4 0.2setosa
65.4 3.9 1.7 0.4setosa
74.6 3.4 1.4 0.3setosa
85.0 3.4 1.5 0.2setosa
94.4 2.9 1.4 0.2setosa
10 4.9 3.1 1.5 0.1setosa
[ 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 setosaIrissetosa
2 4.9 3.0 1.4 0.2 Iris setosaIrissetosa
3 4.7 3.2 1.3 0.2 Iris setosaIrissetosa
4 4.6 3.1 1.5 0.2 Iris setosaIrissetosa
5 5.0 3.6 1.4 0.2 Iris setosaIrissetosa
6 5.4 3.9 1.7 0.4 Iris setosaIrissetosa
7 4.6 3.4 1.4 0.3 Iris setosaIrissetosa
8 5.0 3.4 1.5 0.2 Iris setosaIrissetosa
9 4.4 2.9 1.4 0.2 Iris setosaIrissetosa
10 4.9 3.1 1.5 0.1 Iris setosaIrissetosa
[ 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.1reframe与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.90dplyr表连接
# 示例数据
band_members# A tibble: 3 × 2
nameband
<chr> <chr>
1 MickStones
2 JohnBeatles
3 PaulBeatlesband_instruments# A tibble: 3 × 2
nameplays
<chr> <chr>
1 Johnguitar
2 Paulbass
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
nameband plays
<chr> <chr> <chr>
1 JohnBeatles guitar
2 PaulBeatles bassband_members %>% left_join(band_instruments)# A tibble: 3 × 3
nameband plays
<chr> <chr> <chr>
1 MickStones<NA>
2 JohnBeatles guitar
3 PaulBeatles bassband_members %>% right_join(band_instruments)# A tibble: 3 × 3
nameband plays
<chr> <chr> <chr>
1 JohnBeatles guitar
2 PaulBeatles bass
3 Keith <NA> guitarband_members %>% full_join(band_instruments)# A tibble: 4 × 3
nameband plays
<chr> <chr> <chr>
1 MickStones<NA>
2 JohnBeatles guitar
3 PaulBeatles bass
4 Keith <NA> guitarband_members %>% cross_join(band_instruments)# A tibble: 9 × 4
name.x band name.y plays
<chr><chr> <chr><chr>
1 Mick StonesJohn guitar
2 Mick StonesPaul bass
3 Mick StonesKeithguitar
4 John Beatles John guitar
5 John Beatles Paul bass
6 John Beatles Keithguitar
7 Paul Beatles John guitar
8 Paul Beatles Paul bass
9 Paul Beatles Keithguitarband_members %>% semi_join(band_instruments)# A tibble: 2 × 2
nameband
<chr> <chr>
1 JohnBeatles
2 PaulBeatlesband_members %>% anti_join(band_instruments)# A tibble: 1 × 2
nameband
<chr> <chr>
1 MickStonestidyr表调整
基础的表调整包括多列转单列的gather和单列转多列的spread。
gather可以将表格的列名转换为独立的列,同时保留列值和其他列数据的对应关系。其参数格式为gather("name_of_key_col", "name_of_value_col", cols_to_gather)
iris %>% gather("measurement","value",Sepal.Length:Petal.Width) Speciesmeasurement 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
10setosa 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 035 0
2 Male Child Yes 51113 0
3 Male Adult No 118 154 387670
4 Male Adult Yes571475192
5 Female Child No 0 017 0
6 Female Child Yes 11314 0
7 Female Adult No 41389 3
8 Female Adult Yes 1408076 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.74333330.44266667 -2.358-0.9993333setosa
2 -0.9433333 -0.05733333 -2.358-0.9993333setosa
3 -1.14333330.14266667 -2.458-0.9993333setosa
4 -1.24333330.04266667 -2.258-0.9993333setosa
5 -0.84333330.54266667 -2.358-0.9993333setosa
6 -0.44333330.84266667 -2.058-0.7993333setosa
7 -1.24333330.34266667 -2.358-0.8993333setosa
8 -0.84333330.34266667 -2.258-0.9993333setosa
9 -1.4433333 -0.15733333 -2.358-0.9993333setosa
10 -0.94333330.04266667 -2.258-1.0993333setosa
[ 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.5if_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.2setosa
2 4.7 3.2 1.3 0.2setosa
3 4.6 3.1 1.5 0.2setosa
4 5.0 3.6 1.4 0.2setosa
5 5.4 3.9 1.7 0.4setosa
6 4.6 3.4 1.4 0.3setosa
7 5.0 3.4 1.5 0.2setosa
8 4.9 3.1 1.5 0.1setosa
9 5.4 3.7 1.5 0.2setosa
10 4.8 3.4 1.6 0.2setosa
[ 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.5virginica
10 7.2 3.6 6.1 2.5virginica
[ 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.2setosa
2 4.9 3.0 1.4 0.2setosa
3 4.7 3.2 1.3 0.2setosa
4 4.6 3.1 1.5 0.2setosa
5 5.0 3.6 1.4 0.2setosa
6 5.4 3.9 1.7 0.4setosa
7 4.6 3.4 1.4 0.3setosa
8 5.0 3.4 1.5 0.2setosa
9 4.4 2.9 1.4 0.2setosa
10 4.9 3.1 1.5 0.1setosa
[ 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.09950.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.003830.00380
10 4.9 3.1 1.5 0.1 setosa-0.0769 -0.09990.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
6versicolor 0.00 4.9
7versicolor 0.25 5.6
8versicolor 0.50 5.9
9versicolor 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
Class Sex Age SurvivedFreq
<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 No154 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 No154
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 setosaSepal Length 5.1
2 setosaSepal Width 3.5
3 setosaPetal Length 1.4
4 setosaPetal Width 0.2
5 setosaSepal Length 4.9
6 setosaSepal Width 3
7 setosaPetal Length 1.4
8 setosaPetal Width 0.2
9 setosaSepal Length 4.7
10 setosaSepal 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 setosaSepal 5.1 3.5
2 setosaPetal 1.4 0.2
3 setosaSepal 4.9 3
4 setosaPetal 1.4 0.2
5 setosaSepal 4.7 3.2
6 setosaPetal 1.3 0.2
7 setosaSepal 4.6 3.1
8 setosaPetal 1.5 0.2
9 setosaSepal 5 3.6
10 setosaPetal 1.4 0.2
# ℹ 290 more rowspivot_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中的向量操作,另一种类似于集合操作符。
[*]base-r向量操作风格
[*]:表示连续范围
[*]c()表示并集
[*]-表示排除
[*]集合操作风格
[*]&表示交集
[*]|表示并集
[*]!表示排除
这些算符/函数既接受数字,也接受列名:
storms %>% select(c(2:5, status:pressure&!category), 1, latitude = lat, longitude = long)# A tibble: 19,537 × 10
year month dayhour status wind pressure namelatitude longitude
<dbl> <dbl> <int> <dbl> <fct> <int> <int> <chr> <dbl> <dbl>
11975 6 27 0 tropical depression 25 1013 Amy 27.5 -79
21975 6 27 6 tropical depression 25 1013 Amy 28.5 -79
31975 6 27 12 tropical depression 25 1013 Amy 29.5 -79
41975 6 27 18 tropical depression 25 1013 Amy 30.5 -79
51975 6 28 0 tropical depression 25 1012 Amy 31.5 -78.8
61975 6 28 6 tropical depression 25 1012 Amy 32.4 -78.7
71975 6 28 12 tropical depression 25 1011 Amy 33.3 -78
81975 6 28 18 tropical depression 30 1006 Amy 34 -77
91975 6 29 0 tropical storm 35 1004 Amy 34.4 -75.8
101975 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 510
[ 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.2setosa 7.000000
2 4.9 3.0 1.4 0.2setosa 7.000000
3 4.7 3.2 1.3 0.2setosa 6.500000
4 4.6 3.1 1.5 0.2setosa 7.500000
5 5.0 3.6 1.4 0.2setosa 7.000000
6 5.4 3.9 1.7 0.4setosa 4.250000
7 4.6 3.4 1.4 0.3setosa 4.666667
8 5.0 3.4 1.5 0.2setosa 7.500000
9 4.4 2.9 1.4 0.2setosa 7.000000
10 4.9 3.1 1.5 0.1setosa 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.2setosa 743.6
2 4.9 3.0 1.4 0.2setosa 743.6
3 4.7 3.2 1.3 0.2setosa 743.6
4 4.6 3.1 1.5 0.2setosa 743.6
5 5.0 3.6 1.4 0.2setosa 743.6
6 5.4 3.9 1.7 0.4setosa 743.6
7 4.6 3.4 1.4 0.3setosa 743.6
8 5.0 3.4 1.5 0.2setosa 743.6
9 4.4 2.9 1.4 0.2setosa 743.6
10 4.9 3.1 1.5 0.1setosa 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
3virginica 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.2setosa
2 0.2setosa
3 0.2setosa
4 0.2setosa
5 0.2setosa
6 0.4setosa
7 0.3setosa
8 0.2setosa
9 0.2setosa
10 0.1setosa
[ 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
页:
[1]