9.6 拆分—应用—合并(Split-Apply-Combine)

在研究数据时一个很常见的问题是:如何对那些已被分成不同小组的变量进行统计计算。以下是经典的道路交通安全游戏Frogger的得分情况:

  1. (frogger_scores <- data.frame(
  2. player = rep(c("Tom", "Dick", "Harry"), times = c(2, 5, 3)),
  3. score = round(rlnorm(10, 8), -1)
  4. ))
  5. ## player score
  6. ## 1 Tom 2250
  7. ## 2 Tom 1510
  8. ## 3 Dick 1700
  9. ## 4 Dick 410
  10. ## 5 Dick 3720
  11. ## 6 Dick 1510
  12. ## 7 Dick 4500
  13. ## 8 Harry 2160
  14. ## 9 Harry 5070
  15. ## 10 Harry 2930

计算每个玩家的平均得分需要三个步骤。首先,我们按玩家来分开数据集:

  1. (scores_by_player <- with(
  2. frogger_scores,
  3. split(score, player)
  4. ))
  5. ## $Dick
  6. ## [1] 1700 410 3720 1510 4500
  7. ##
  8. ## $Harry
  9. ## [1] 2160 5070 2930
  10. ##
  11. ## $Tom
  12. ## [1] 2250 1510

然后,我们将(mean)函数应用于每个元素:

  1. (list_of_means_by_player <- lapply(scores_by_player, mean))
  2. ## $Dick
  3. ## [1] 2368
  4. ##
  5. ## $Harry
  6. ## [1] 3387
  7. ##
  8. ## $Tom
  9. ## [1] 1880

最后,我们把结果合并到单个向量中:

  1. (mean_by_player <- unlist(list_of_means_by_player))
  2. ## Dick Harry Tom
  3. ## 2368 3387 1880

如果使用vapplysapply,最后两个步骤可简化成一步,但拆分-应用-合并是如此常用,所以我们必须简化它。方法就是使用tapply函数,它能一次执行所有的三个步骤,一气呵成:

  1. with(frogger_scores, tapply(score, player, mean))
  2. ## Dick Harry Tom
  3. ## 2368 3387 1880

还有几个其他的tapply包装函数,例如byaggregate。它们的功能相同,但接口稍有不同。

提示

SQL的粉丝可能会注意到,拆分-应用-合并其实就是GROUP BY操作。