9.7 plyr

*apply函数家族都很强大,但三个缺点使得它们不是那么易用。首先,名字有点晦涩。在lapply中,“l”代表list是可理解的,但是,即便是使用R长达9年的我,也不知道tapply中的“t”代表什么。

其次,参数的使用不完全一致。大多数的函数都以数据对象为首个参数,函数为第二参数。但mapply的顺序却与此相反,而tapply还要加上一个函数作为它的第三个参数。数据参数有时是X,有时是object;而简化参数有时是simplify,有时是SIMPLIFY

第三,输出的形式不太可控。如果要把结果作为数据框返回或丢弃它,都需要花一些心思才能做到。

这时,plyr包就派上用场了。它包含一系列名为**ply的函数,其中的空格(星号)分别代表输入和输出的形式。例如,llply的输入参数是列表,它将函数应用于每个元素上,并返回一个列表,这使它成为lapply的一个替代函数:

  1. library(plyr)
  2. llply(prime_factors, unique)
  3. ## $two
  4. ## [1] 2
  5. ##
  6. ## $three
  7. ## [1] 3
  8. ##
  9. ## $four
  10. ## [1] 2
  11. ##
  12. ## $five
  13. ## [1] 5
  14. ##
  15. ## $six
  16. ## [1] 2 3
  17. ##
  18. ## $seven
  19. ## [1] 7
  20. ##
  21. ## $eight
  22. ## [1] 2
  23. ##
  24. ## $nine
  25. ## [1] 3
  26. ##
  27. ## $ten
  28. ## [1] 2 5

laply以一个列表作为参数,并返回一个数组,这酷似sapply。如果输入参数为空,它也会智能地返回一个空的逻辑向量(与返回空列表的sapply不一样):

  1. laply(prime_factors, length)
  2. ## [1] 1 1 2 1 2 1 3 2 2
  3. laply(list(), length)
  4. ## logical(0)

raply能取代replicate(不是rapply!),还有rlplyrdply函数能分别返回列表或数据框,还有一个r_ply函数能丢弃结果(在绘图时有用):

  1. raply(5, runif(1)) # 数组输出
  2. ## [1] 0.009415 0.226514 0.133015 0.698586 0.112846
  3. rlply(5, runif(1)) # 列表给出
  4. ## [[1]]
  5. ## [1] 0.6646
  6. ##
  7. ## [[2]]
  8. ## [1] 0.2304
  9. ##
  10. ## [[3]]
  11. ## [1] 0.613
  12. ##
  13. ## [[4]]
  14. ## [1] 0.5532
  15. ##
  16. ## [[5]]
  17. ## [1] 0.3654
  18. rdply(5, runif(1)) # 数据框输出
  19. ## .n V1
  20. ## 1 1 0.9068
  21. ## 2 2 0.0654
  22. ## 3 3 0.3788
  23. ## 4 4 0.5086
  24. ## 5 5 0.3502
  25. r_ply(5, runif(1)) # 丢弃输出
  26. ## NULL

也许plyr包中最常用的函数是ddply,它的输入和输出都是数据框,它可以替换tapply函数 。其优点是易于同时计算多个列。以下让我们为Frogger数据集添加一个level列,用来表示玩家在游戏中达到的级别:

  1. frogger_scores$level <- floor(log(frogger_scores$score))

调用ddply的方法有好几种。这些方法的参数都包括一个数据框、要进行拆分的列的名称,以及要应用到每个元素上的函数。传递列时无需引号,但需要包含在.的调用之后。

对于函数,你既可以使用colwise告诉ddply调用函数应用于每一列(除了第二个参数以外),也可以使用summarize对指定的列进行操作:

  1. ddply(
  2. frogger_scores,
  3. .(player),
  4. colwise(mean) # 除了player之外,对每个列调用mean函数
  5. )
  6. ## player score level
  7. ## 1 Dick 2368 7.200
  8. ## 2 Harry 3387 7.333
  9. ## 3 Tom 1880 7.000
  10. ddply(
  11. frogger_scores,
  12. .(player),
  13. summarize,
  14. mean_score = mean(score), # 对score调用mean
  15. max_level = max(level) #... 然后求level的max值
  16. )
  17. ## player mean_score max_level
  18. ## 1 Dick 2368 8
  19. ## 2 Harry 3387 8
  20. ## 3 Tom 1880 7

使用colwise指定时会更快,但你必须对每一列重复同样的事情;而summarize更灵活,但你需要输入更多内容。

mapply没有直接的替代函数,尽管m*ply函数允许对多个参数进行循环。同样,vapplyrapply也没有替代函数 。