9.7 plyr
包
*apply
函数家族都很强大,但三个缺点使得它们不是那么易用。首先,名字有点晦涩。在lapply
中,“l”代表list是可理解的,但是,即便是使用R长达9年的我,也不知道tapply
中的“t”代表什么。
其次,参数的使用不完全一致。大多数的函数都以数据对象为首个参数,函数为第二参数。但mapply
的顺序却与此相反,而tapply
还要加上一个函数作为它的第三个参数。数据参数有时是X
,有时是object
;而简化参数有时是simplify
,有时是SIMPLIFY
。
第三,输出的形式不太可控。如果要把结果作为数据框返回或丢弃它,都需要花一些心思才能做到。
这时,plyr
包就派上用场了。它包含一系列名为**ply
的函数,其中的空格(星号)分别代表输入和输出的形式。例如,llply
的输入参数是列表,它将函数应用于每个元素上,并返回一个列表,这使它成为lapply
的一个替代函数:
library(plyr)
llply(prime_factors, unique)
## $two
## [1] 2
##
## $three
## [1] 3
##
## $four
## [1] 2
##
## $five
## [1] 5
##
## $six
## [1] 2 3
##
## $seven
## [1] 7
##
## $eight
## [1] 2
##
## $nine
## [1] 3
##
## $ten
## [1] 2 5
laply
以一个列表作为参数,并返回一个数组,这酷似sapply
。如果输入参数为空,它也会智能地返回一个空的逻辑向量(与返回空列表的sapply
不一样):
laply(prime_factors, length)
## [1] 1 1 2 1 2 1 3 2 2
laply(list(), length)
## logical(0)
raply
能取代replicate
(不是rapply
!),还有rlply
和rdply
函数能分别返回列表或数据框,还有一个r_ply
函数能丢弃结果(在绘图时有用):
raply(5, runif(1)) # 数组输出
## [1] 0.009415 0.226514 0.133015 0.698586 0.112846
rlply(5, runif(1)) # 列表给出
## [[1]]
## [1] 0.6646
##
## [[2]]
## [1] 0.2304
##
## [[3]]
## [1] 0.613
##
## [[4]]
## [1] 0.5532
##
## [[5]]
## [1] 0.3654
rdply(5, runif(1)) # 数据框输出
## .n V1
## 1 1 0.9068
## 2 2 0.0654
## 3 3 0.3788
## 4 4 0.5086
## 5 5 0.3502
r_ply(5, runif(1)) # 丢弃输出
## NULL
也许plyr
包中最常用的函数是ddply
,它的输入和输出都是数据框,它可以替换tapply
函数 。其优点是易于同时计算多个列。以下让我们为Frogger数据集添加一个level
列,用来表示玩家在游戏中达到的级别:
frogger_scores$level <- floor(log(frogger_scores$score))
调用ddply
的方法有好几种。这些方法的参数都包括一个数据框、要进行拆分的列的名称,以及要应用到每个元素上的函数。传递列时无需引号,但需要包含在.
的调用之后。
对于函数,你既可以使用colwise
告诉ddply
调用函数应用于每一列(除了第二个参数以外),也可以使用summarize
对指定的列进行操作:
ddply(
frogger_scores,
.(player),
colwise(mean) # 除了player之外,对每个列调用mean函数
)
## player score level
## 1 Dick 2368 7.200
## 2 Harry 3387 7.333
## 3 Tom 1880 7.000
ddply(
frogger_scores,
.(player),
summarize,
mean_score = mean(score), # 对score调用mean
max_level = max(level) #... 然后求level的max值
)
## player mean_score max_level
## 1 Dick 2368 8
## 2 Harry 3387 8
## 3 Tom 1880 7
使用colwise
指定时会更快,但你必须对每一列重复同样的事情;而summarize
更灵活,但你需要输入更多内容。
mapply
没有直接的替代函数,尽管m*ply
函数允许对多个参数进行循环。同样,vapply
和rapply
也没有替代函数 。