我们可以使用plyr()来对数据进行拆分、应用和合并,plyr()的输入可以是数组、数据框、列表,输出也可以是数组、数据框、列表,还可以不输出任何结果,因此可以极大的减少数据转换的负担。
Plyr包中,数据处理函数的名称由5个字符组成,格式为{adl}{adl_}ply。第一个字符根据输入数据的类型,可以时a{数组}、d{数据框}、l{列表};第二个字符表示输出的类型,与上述一致,不过可以有无输出“_”
我们先安装plyr函数
install.packages("plyr")
library(plyr)
一、adply()函数
Adply()函数用于接收数组输入和返回数据框(d),但是其并不要求输入的数据完全是数组,其主要是是否能被数字索引读取,及主要是否为“矩阵”形式的数据,因此其也可以接受数据框的输入
在之前的apply()函数系列函数中提到,apply()函数在进行行处理时,如果各列有不同的数据类型,则会发生数据转换,导致输出的数据类型出错,而使用adply()函数时就可以避免这种情况发生
> apply(iris,1,function(row){print(row)})
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
"5.1" "3.5" "1.4" "0.2" "setosa"
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
"4.9" "3.0" "1.4" "0.2" "setosa"
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
"4.7" "3.2" "1.3" "0.2" "setosa"
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
例如我们用函数查看数据框的每行,并且判断当前行的Sepal.Length是否大于0.5
> adply(iris,1,function(row){row$Sepal.Length>=5.0&row$Species=="setosa"})
Sepal.Length Sepal.Width Petal.Length Petal.Width Species V1
1 5.1 3.5 1.4 0.2 setosa TRUE
2 4.9 3.0 1.4 0.2 setosa FALSE
3 4.7 3.2 1.3 0.2 setosa FALSE
4 4.6 3.1 1.5 0.2 setosa FALSE
当然,我们可以尝试适当指定保存新数据的列名
> adply(iris,1,function(row){data.frame(sepal_ge_5_setosa=c(row$Sepal.Length>=5.0&row$Species=="setosa"))})
#这个函数接受每行数据作为row参数,然后使用data.frame()函数创建一个新的数据框,其中包含一个名为
#sepal_ge_5_setosa的列,记录了对Sepal.Length是否大于等于5.0且Species是否为"setosa"的条件判断结果
Sepal.Length Sepal.Width Petal.Length Petal.Width Species sepal_ge_5_setosa
1 5.1 3.5 1.4 0.2 setosa TRUE
2 4.9 3.0 1.4 0.2 setosa FALSE
3 4.7 3.2 1.3 0.2 setosa FALSE
4 4.6 3.1 1.5 0.2 setosa FALSE
5 5.0 3.6 1.4 0.2 setosa TRUE
6 5.4 3.9 1.7 0.4 setosa TRUE
二、ddply()函数
Ddply()函数接受数据框的输入,接受数据框
根adply不同的是,ddply()是根据.(variables)列出的列拆分数据,然后应用指定函数。
例如,我们从鸢尾花数据集中依据Species分别计算Sepal.Length的平均值
> ddply(iris,.(Species),function(sub){data.frame(Sepal.width.mean=mean(sub$Sepal.Width))})
Species Sepal.width.mean
1 setosa 3.428
2 versicolor 2.770
3 virginica 2.974
如果需要针对多个变量对数据分组,只要在.()中罗列多个条件或列名即可,同时让这些条件和列名用逗号分割
例如,我们用鸢尾花品种和Sepal.Length是否大于5来做判断条件,然后计算每个分组的Sepal.Width平均值
> ddply(iris,.(Species,Sepal.Length>=5),function(sub){data.frame(Sepal.width.mean=mean(sub$Sepal.Width))})
Species Sepal.Length >= 5 Sepal.width.mean
1 setosa FALSE 3.140000
2 setosa TRUE 3.620000
3 versicolor FALSE 2.400000
4 versicolor TRUE 2.777551
5 virginica FALSE 2.500000
6 virginica TRUE 2.983673
三、轻松进行按组运算
在常用类型计算中,我们可以使用transform()、mutate()、summarise()函数,让计算更容易
(一)transform
我们在这里使用美国职业棒球选手数据(baseball)数据进行演示。
> head(baseball)
id year stint team lg g ab r h X2b X3b hr rbi sb cs bb so ibb hbp sh sf
4 ansonca01 1871 1 RC1 25 120 29 39 11 3 0 16 6 2 2 1 NA NA NA NA
44 forceda01 1871 1 WS3 32 162 45 45 9 4 0 29 8 0 4 0 NA NA NA NA
68 mathebo01 1871 1 FW1 19 89 15 24 3 1 0 10 2 1 2 0 NA NA NA NA
99 startjo01 1871 1 NY2 33 161 35 58 5 1 1 34 4 2 3 0 NA NA NA NA
102 suttoez01 1871 1 CL1 29 128 35 45 3 7 3 23 3 1 1 0 NA NA NA NA
106 whitede01 1871 1 CL1 29 146 40 47 6 5 1 21 2 2 4 1 NA NA NA NA
我们以选手ID为分组依据,计算每个分组中当前值与当前分组中最小值之差,保存到cyear中
> head(ddply(baseball,.(id),transform,cyear=year-min(year)+1))
id year stint team lg g ab r h X2b X3b hr rbi sb cs bb so ibb hbp sh sf
1 aaronha01 1954 1 ML1 NL 122 468 58 131 27 6 13 69 2 2 28 39 NA 3 6 4
2 aaronha01 1955 1 ML1 NL 153 602 105 189 37 9 27 106 3 1 49 61 5 3 7 4
3 aaronha01 1956 1 ML1 NL 153 609 106 200 34 14 26 92 2 4 37 54 6 2 5 7
4 aaronha01 1957 1 ML1 NL 151 615 118 198 27 6 44 132 1 1 57 58 15 0 0 3
5 aaronha01 1958 1 ML1 NL 153 601 109 196 34 4 30 95 4 1 59 49 16 1 0 3
6 aaronha01 1959 1 ML1 NL 154 629 116 223 46 7 39 123 8 0 51 54 17 4 0 9
gidp cyear
1 13 1
2 20 2
3 21 3
4 13 4
5 21 5
6 19 6
(二)mutate()函数
使用mutate()函数向数据框中添加多个列时,后面添加的列可以直接引用前面添加的列
例如,我们可以在上述数据框下,生成一个log_cyear
> head(ddply(baseball,.(id),mutate,cyear=year-min(year)+1,log_year=log(cyear)))
id year stint team lg g ab r h X2b X3b hr rbi sb cs bb so ibb hbp sh sf
1 aaronha01 1954 1 ML1 NL 122 468 58 131 27 6 13 69 2 2 28 39 NA 3 6 4
2 aaronha01 1955 1 ML1 NL 153 602 105 189 37 9 27 106 3 1 49 61 5 3 7 4
3 aaronha01 1956 1 ML1 NL 153 609 106 200 34 14 26 92 2 4 37 54 6 2 5 7
4 aaronha01 1957 1 ML1 NL 151 615 118 198 27 6 44 132 1 1 57 58 15 0 0 3
5 aaronha01 1958 1 ML1 NL 153 601 109 196 34 4 30 95 4 1 59 49 16 1 0 3
6 aaronha01 1959 1 ML1 NL 154 629 116 223 46 7 39 123 8 0 51 54 17 4 0 9
gidp cyear log_year
1 13 1 0.0000000
2 20 2 0.6931472
3 21 3 1.0986123
4 13 4 1.3862944
5 21 5 1.6094379
6 19 6 1.7917595
(三)summarise()函数
Summarise()函数会返回只包含计算结果的新数据框。例如:我们用summerise()函数进行汇总每个组里年份最小的数据
> head(ddply(baseball,.(id),summarise,min_year=min(year)))
id min_year
1 aaronha01 1954
2 abernte02 1955
3 adairje01 1958
4 adamsba01 1906
5 adamsbo03 1946
6 adcocjo01 1950
如果我们求多个汇总值,则要罗列多个计算汇总值的表达式
> head(ddply(baseball,.(id),summarise,min_year=min(year),max_year=max(year)))
id min_year max_year
1 aaronha01 1954 1976
2 abernte02 1955 1972
3 adairje01 1958 1970
4 adamsba01 1906 1926
5 adamsbo03 1946 1959
6 adcocjo01 1950 1966
(四)subset()函数
从整个数据集中获取符合条件的子集,只要满足条件就可以抽取
例如,我们抽取各个选手参数最多的一年所在的行
head(ddply(baseball,.(id),subset,g=max(g)))
id year stint team lg g ab r h X2b X3b hr rbi sb cs bb so ibb hbp sh sf
1 aaronha01 1954 1 ML1 NL 122 468 58 131 27 6 13 69 2 2 28 39 NA 3 6 4
2 aaronha01 1955 1 ML1 NL 153 602 105 189 37 9 27 106 3 1 49 61 5 3 7 4
3 aaronha01 1956 1 ML1 NL 153 609 106 200 34 14 26 92 2 4 37 54 6 2 5 7
4 aaronha01 1957 1 ML1 NL 151 615 118 198 27 6 44 132 1 1 57 58 15 0 0 3
5 aaronha01 1958 1 ML1 NL 153 601 109 196 34 4 30 95 4 1 59 49 16 1 0 3
6 aaronha01 1959 1 ML1 NL 154 629 116 223 46 7 39 123 8 0 51 54 17 4 0 9
gidp
1 13
2 20
3 21
4 13
5 21
6 19
三、mdply()
Mdply()可以接受数据框数组,将指定行数应用到各列,然后合并执行结果
例如,我们利用mdply()函数来传递生成正态分布随机数
> (x<-data.frame(mean=1:5,sd=1:5))
mean sd
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
> mdply(x,rnorm,n=2)
mean sd V1 V2
1 1 1 3.3823125 0.7413170
2 2 2 0.3226491 3.9972046
3 3 3 -1.1046600 0.1680206
4 4 4 0.6956026 6.4022236
5 5 5 -1.0052694 11.9224206