6.18 汇总数据

6.18.1 问题

你想要按照组别汇总你的数据(均值、标准差等等)。

6.18.2 方案

有三种方法描述基于一些特定变量的分组数据,然后对每一组使用汇总函数(像均值、标准差等等)。

  • ddply() 函数:它比较容易使用,但需要载入 plyr 包。这种方法可能就是你要找的。
  • summaryBy() 函数:它也比较容易使用,然而它需要载入 doBy 包。
  • aggregate() 函数,它比较难使用一点但内置于 R 中。

假设你有以下数据并想求得每一组样本大小、均值的改变、标准差以及均值的标准误,而这里的组别是根据性别和条件指定的:F-placebo, F-aspirin, M-placeboM-aspirin

  1. data <- read.table(header=TRUE, text='
  2. subject sex condition before after change
  3. 1 F placebo 10.1 6.9 -3.2
  4. 2 F placebo 6.3 4.2 -2.1
  5. 3 M aspirin 12.4 6.3 -6.1
  6. 4 F placebo 8.1 6.1 -2.0
  7. 5 M aspirin 15.2 9.9 -5.3
  8. 6 F aspirin 10.9 7.0 -3.9
  9. 7 F aspirin 11.6 8.5 -3.1
  10. 8 M aspirin 9.5 3.0 -6.5
  11. 9 F placebo 11.5 9.0 -2.5
  12. 10 M placebo 11.9 11.0 -0.9
  13. 11 F aspirin 11.4 8.0 -3.4
  14. 12 M aspirin 10.0 4.4 -5.6
  15. 13 M aspirin 12.5 5.4 -7.1
  16. 14 M placebo 10.6 10.6 0.0
  17. 15 M aspirin 9.1 4.3 -4.8
  18. 16 F placebo 12.1 10.2 -1.9
  19. 17 F placebo 11.0 8.8 -2.2
  20. 18 F placebo 11.9 10.2 -1.7
  21. 19 M aspirin 9.1 3.6 -5.5
  22. 20 M placebo 13.5 12.4 -1.1
  23. 21 M aspirin 12.0 7.5 -4.5
  24. 22 F placebo 9.1 7.6 -1.5
  25. 23 M placebo 9.9 8.0 -1.9
  26. 24 F placebo 7.6 5.2 -2.4
  27. 25 F placebo 11.8 9.7 -2.1
  28. 26 F placebo 11.8 10.7 -1.1
  29. 27 F aspirin 10.1 7.9 -2.2
  30. 28 M aspirin 11.6 8.3 -3.3
  31. 29 F aspirin 11.3 6.8 -4.5
  32. 30 F placebo 10.3 8.3 -2.0
  33. ')

6.18.2.1 使用 ddply

  1. library(plyr)
  2. # 给每一组运行长度、均值、标准差等函数
  3. # 每一组依据性别+条件划分
  4. cdata <- ddply(data, c("sex", "condition"), summarise, N = length(change),
  5. mean = mean(change), sd = sd(change), se = sd/sqrt(N))
  6. cdata
  7. #> sex condition N mean sd se
  8. #> 1 F aspirin 5 -3.420 0.8643 0.3865
  9. #> 2 F placebo 12 -2.058 0.5248 0.1515
  10. #> 3 M aspirin 9 -5.411 1.1308 0.3769
  11. #> 4 M placebo 4 -0.975 0.7805 0.3902
6.18.2.1.1 处理缺失值

如果数据中存在 NA 值,需要给每个函数添加 na.rm=TRUE 标记去除缺失值。因为函数 length() 没有 na.rm 选项,所以可以使用 sum(!is.na(…))的方式对非缺失值进行计数。

  1. # 给数据加些 NA 值
  2. dataNA <- data
  3. dataNA$change[11:14] <- NA
  4. cdata <- ddply(dataNA, c("sex", "condition"), summarise,
  5. N = sum(!is.na(change)), mean = mean(change, na.rm = TRUE),
  6. sd = sd(change, na.rm = TRUE), se = sd/sqrt(N))
  7. cdata
  8. #> sex condition N mean sd se
  9. #> 1 F aspirin 4 -3.425 0.9979 0.4990
  10. #> 2 F placebo 12 -2.058 0.5248 0.1515
  11. #> 3 M aspirin 7 -5.143 1.0675 0.4035
  12. #> 4 M placebo 3 -1.300 0.5292 0.3055
6.18.2.1.2 自动汇总函数

不像我们刚才手动地指定想要的值然后计算标准误,这个函数可以自动处理所有的细节。它可以干以下的事情:

  • 寻找均值、标准差和计数
  • 寻找均值的标准误(强调,如果你处理的是被试内变量这可能不是你想要的)
  • 寻找 95% 的置信区间(也可以指定其他值)
  • 重命令结果数据集的变量名,这样更方便后续处理

要使用的话,把函数放你的代码中然后像下面一样调用它。

  1. ## Summarizes data. Gives count, mean, standard
  2. ## deviation, standard error of the mean, and confidence
  3. ## interval (default 95%). data: a data frame.
  4. ## measurevar: the name of a column that contains the
  5. ## variable to be summariezed groupvars: a vector
  6. ## containing names of columns that contain grouping
  7. ## variables na.rm: a boolean that indicates whether to
  8. ## ignore NA's conf.interval: the percent range of the
  9. ## confidence interval (default is 95%)
  10. summarySE <- function(data = NULL, measurevar, groupvars = NULL,
  11. na.rm = FALSE, conf.interval = 0.95, .drop = TRUE) {
  12. library(plyr)
  13. # New version of length which can handle NA's: if
  14. # na.rm==T, don't count them
  15. length2 <- function(x, na.rm = FALSE) {
  16. if (na.rm)
  17. sum(!is.na(x)) else length(x)
  18. }
  19. # This does the summary. For each group's data frame,
  20. # return a vector with N, mean, and sd
  21. datac <- ddply(data, groupvars, .drop = .drop, .fun = function(xx,
  22. col) {
  23. c(N = length2(xx[[col]], na.rm = na.rm), mean = mean(xx[[col]],
  24. na.rm = na.rm), sd = sd(xx[[col]], na.rm = na.rm))
  25. }, measurevar)
  26. # Rename the 'mean' column
  27. datac <- rename(datac, c(mean = measurevar))
  28. datac$se <- datac$sd/sqrt(datac$N) # Calculate standard error of the mean
  29. # Confidence interval multiplier for standard error
  30. # Calculate t-statistic for confidence interval: e.g.,
  31. # if conf.interval is .95, use .975 (above/below), and
  32. # use df=N-1
  33. ciMult <- qt(conf.interval/2 + 0.5, datac$N - 1)
  34. datac$ci <- datac$se * ciMult
  35. return(datac)
  36. }

举个例子使用它(用95%的置信区间)。与之前手动计算这些步骤相比 summarySE() 函数一步搞定:

  1. summarySE(data, measurevar = "change", groupvars = c("sex",
  2. "condition"))
  3. #> sex condition N change sd se ci
  4. #> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
  5. #> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
  6. #> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
  7. #> 4 M placebo 4 -0.975 0.7805 0.3902 1.2419
  8. # 使用 NA'的数据框, 使用 na.rm=TRUE
  9. summarySE(dataNA, measurevar = "change", groupvars = c("sex",
  10. "condition"), na.rm = TRUE)
  11. #> sex condition N change sd se ci
  12. #> 1 F aspirin 4 -3.425 0.9979 0.4990 1.5879
  13. #> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
  14. #> 3 M aspirin 7 -5.143 1.0675 0.4035 0.9873
  15. #> 4 M placebo 3 -1.300 0.5292 0.3055 1.3145
6.18.2.1.3 用零填满空组合

有时候汇总的数据框中存在因子的空组合——意思是因子组合可能存在,但原始数据框里又没有实际出现。它在自动填满有 NA 值的数据框时有用。要做到这一点,当调用ddply()summarySE() 时设置 .drop=FALSE

例子:

  1. # 首先移除所有 Male+Placebo 条目
  2. dataSub <- subset(data, !(sex == "M" & condition == "placebo"))
  3. # 如果我们汇总数据,在本来有 Male+Placebo
  4. # 的地方会存在空行 因为这个组合已经被我们删除了
  5. summarySE(dataSub, measurevar = "change", groupvars = c("sex",
  6. "condition"))
  7. #> sex condition N change sd se ci
  8. #> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
  9. #> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
  10. #> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
  11. # 设置 .drop=FALSE 指定不要扔掉这个组合
  12. summarySE(dataSub, measurevar = "change", groupvars = c("sex",
  13. "condition"), .drop = FALSE)
  14. #> Warning in qt(conf.interval/2 + 0.5, datac$N - 1): 产生
  15. #> 了NaNs
  16. #> sex condition N change sd se ci
  17. #> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
  18. #> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
  19. #> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
  20. #> 4 M placebo 0 NaN NA NA NaN

6.18.2.2 使用summaryBy

使用 summarizeBy() 函数瓦解数据:

  1. library(doBy)
  2. # 给每一组运行长度、均值、标准差等函数
  3. # 每一组依据性别+条件划分
  4. cdata <- summaryBy(change ~ sex + condition, data = data,
  5. FUN = c(length, mean, sd))
  6. cdata
  7. #> sex condition change.length change.mean change.sd
  8. #> 1 F aspirin 5 -3.420 0.8643
  9. #> 2 F placebo 12 -2.058 0.5248
  10. #> 3 M aspirin 9 -5.411 1.1308
  11. #> 4 M placebo 4 -0.975 0.7805
  12. # 重命名 change.length 为 N
  13. names(cdata)[names(cdata) == "change.length"] <- "N"
  14. # 计算平均值的标准误差
  15. cdata$change.se <- cdata$change.sd/sqrt(cdata$N)
  16. cdata
  17. #> sex condition N change.mean change.sd change.se
  18. #> 1 F aspirin 5 -3.420 0.8643 0.3865
  19. #> 2 F placebo 12 -2.058 0.5248 0.1515
  20. #> 3 M aspirin 9 -5.411 1.1308 0.3769
  21. #> 4 M placebo 4 -0.975 0.7805 0.3902

注意,如果你有任何被试间变量,这些标准误值在比对组别差异时就没用了。

6.18.2.2.1 处理缺失值

如果数据中存在 NA 值,你需要添加 na.rm=TRUE 选项。通常你可以在 summaryBy() 函数中设置,但 length() 函数识别不了这个选项。一种解决方式是根据 length() 函数定义一个新的取长度函数去处理NA值。

  1. # 新版的 length 函数可以处理 NA 值,如果 na.rm=T,则不对
  2. # NA 计数
  3. length2 <- function(x, na.rm = FALSE) {
  4. if (na.rm)
  5. sum(!is.na(x)) else length(x)
  6. }
  7. # 给数据添加一些 NA 值
  8. dataNA <- data
  9. dataNA$change[11:14] <- NA
  10. cdataNA <- summaryBy(change ~ sex + condition, data = dataNA,
  11. FUN = c(length2, mean, sd), na.rm = TRUE)
  12. cdataNA
  13. #> sex condition change.length2 change.mean change.sd
  14. #> 1 F aspirin 4 -3.425 0.9979
  15. #> 2 F placebo 12 -2.058 0.5248
  16. #> 3 M aspirin 7 -5.143 1.0675
  17. #> 4 M placebo 3 -1.300 0.5292
  18. # 做些其他事情
6.18.2.2.2 自动汇总函数

注意这里的自动汇总函数与之前的不同,它是通过 summaryBy() 实现的

不像我们刚才手动地指定想要的值然后计算标准误,这个函数可以自动处理所有的细节。它可以干以下的事情:

  • 寻找均值、标准差和计数
  • 寻找均值的标准误
  • 寻找95%的置信区间(也可以指定其他值)
  • 重命令结果数据集的变量名,这样更方便后续处理

要使用的话,把函数放你的代码中然后像下面一样调用它。

  1. ## Summarizes data. Gives count, mean, standard
  2. ## deviation, standard error of the mean, and confidence
  3. ## interval (default 95%). data: a data frame.
  4. ## measurevar: the name of a column that contains the
  5. ## variable to be summariezed groupvars: a vector
  6. ## containing names of columns that contain grouping
  7. ## variables na.rm: a boolean that indicates whether to
  8. ## ignore NA's conf.interval: the percent range of the
  9. ## confidence interval (default is 95%)
  10. summarySE <- function(data = NULL, measurevar, groupvars = NULL,
  11. na.rm = FALSE, conf.interval = 0.95) {
  12. library(doBy)
  13. # New version of length which can handle NA's: if
  14. # na.rm==T, don't count them
  15. length2 <- function(x, na.rm = FALSE) {
  16. if (na.rm)
  17. sum(!is.na(x)) else length(x)
  18. }
  19. # Collapse the data
  20. formula <- as.formula(paste(measurevar, paste(groupvars,
  21. collapse = " + "), sep = " ~ "))
  22. datac <- summaryBy(formula, data = data, FUN = c(length2,
  23. mean, sd), na.rm = na.rm)
  24. # Rename columns
  25. names(datac)[names(datac) == paste(measurevar, ".mean",
  26. sep = "")] <- measurevar
  27. names(datac)[names(datac) == paste(measurevar, ".sd",
  28. sep = "")] <- "sd"
  29. names(datac)[names(datac) == paste(measurevar, ".length2",
  30. sep = "")] <- "N"
  31. datac$se <- datac$sd/sqrt(datac$N) # Calculate standard error of the mean
  32. # Confidence interval multiplier for standard error
  33. # Calculate t-statistic for confidence interval: e.g.,
  34. # if conf.interval is .95, use .975 (above/below), and
  35. # use df=N-1
  36. ciMult <- qt(conf.interval/2 + 0.5, datac$N - 1)
  37. datac$ci <- datac$se * ciMult
  38. return(datac)
  39. }

举个例子使用它(用95%的置信区间)。与之前手动计算这些步骤相反,summarySE() 函数一步搞定:

  1. summarySE(data, measurevar = "change", groupvars = c("sex",
  2. "condition"))
  3. #> sex condition N change sd se ci
  4. #> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
  5. #> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
  6. #> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
  7. #> 4 M placebo 4 -0.975 0.7805 0.3902 1.2419
  8. # 对于含有 NA 值得数据集,使用 na.rm=TRUE
  9. summarySE(dataNA, measurevar = "change", groupvars = c("sex",
  10. "condition"), na.rm = TRUE)
  11. #> sex condition N change sd se ci
  12. #> 1 F aspirin 4 -3.425 0.9979 0.4990 1.5879
  13. #> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
  14. #> 3 M aspirin 7 -5.143 1.0675 0.4035 0.9873
  15. #> 4 M placebo 3 -1.300 0.5292 0.3055 1.3145
6.18.2.2.3 用零填满空组合

有时候汇总的数据框中存在因子的空组合 - 这意思是,因子组合可能存在,但原始数据框里又没有实际出现。它在自动填满有 NA 值的数据框时有用。

这个例子将会用 0 填满缺失的组合:

  1. fillMissingCombs <- function(df, factors, measures) {
  2. # 创建含因子水平组合的列表
  3. levelList <- list()
  4. for (f in factors) {
  5. levelList[[f]] <- levels(df[, f])
  6. }
  7. fullFactors <- expand.grid(levelList)
  8. dfFull <- merge(fullFactors, df, all.x = TRUE)
  9. # 将 measure 变量中的 NA 都替换为 0
  10. for (m in measures) {
  11. dfFull[is.na(dfFull[, m]), m] <- 0
  12. }
  13. return(dfFull)
  14. }

使用例子:

  1. # 首先移除所有 Male+Placebo 条目
  2. dataSub <- subset(data, !(sex == "M" & condition == "placebo"))
  3. # 如果我们汇总数据,在本来有 Male+Placebo
  4. # 的地方会存在空行 因为这个组合已经被我们删除了
  5. cdataSub <- summarySE(dataSub, measurevar = "change", groupvars = c("sex",
  6. "condition"))
  7. cdataSub
  8. #> sex condition N change sd se ci
  9. #> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
  10. #> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
  11. #> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
  12. # 设置 .drop=FALSE 指定不要扔掉这个组合
  13. fillMissingCombs(cdataSub, factors = c("sex", "condition"),
  14. measures = c("N", "change", "sd", "se", "ci"))
  15. #> sex condition N change sd se ci
  16. #> 1 F aspirin 5 -3.420 0.8643 0.3865 1.0732
  17. #> 2 F placebo 12 -2.058 0.5248 0.1515 0.3334
  18. #> 3 M aspirin 9 -5.411 1.1308 0.3769 0.8692
  19. #> 4 M placebo 0 0.000 0.0000 0.0000 0.0000

6.18.2.3 使用 aggregate()

aggregate() 函数比较难用,但它内置于 R,所以不需要安装其他包。

  1. # 对每个目录 (sex*condition) 中的对象计数
  2. cdata <- aggregate(data["subject"], by = data[c("sex", "condition")],
  3. FUN = length)
  4. cdata
  5. #> sex condition subject
  6. #> 1 F aspirin 5
  7. #> 2 M aspirin 9
  8. #> 3 F placebo 12
  9. #> 4 M placebo 4
  10. # 重命名 'subject' 列为 'N'
  11. names(cdata)[names(cdata) == "subject"] <- "N"
  12. cdata
  13. #> sex condition N
  14. #> 1 F aspirin 5
  15. #> 2 M aspirin 9
  16. #> 3 F placebo 12
  17. #> 4 M placebo 4
  18. # 按性别排序
  19. cdata <- cdata[order(cdata$sex), ]
  20. cdata
  21. #> sex condition N
  22. #> 1 F aspirin 5
  23. #> 3 F placebo 12
  24. #> 2 M aspirin 9
  25. #> 4 M placebo 4
  26. # 我们也保留 before 和 after列:
  27. # 得到性别和条件下的平均影响大小 Get the average effect
  28. # size by sex and condition
  29. cdata.means <- aggregate(data[c("before", "after", "change")],
  30. by = data[c("sex", "condition")], FUN = mean)
  31. cdata.means
  32. #> sex condition before after change
  33. #> 1 F aspirin 11.06 7.640 -3.420
  34. #> 2 M aspirin 11.27 5.856 -5.411
  35. #> 3 F placebo 10.13 8.075 -2.058
  36. #> 4 M placebo 11.47 10.500 -0.975
  37. # 合并数据框
  38. cdata <- merge(cdata, cdata.means)
  39. cdata
  40. #> sex condition N before after change
  41. #> 1 F aspirin 5 11.06 7.640 -3.420
  42. #> 2 F placebo 12 10.13 8.075 -2.058
  43. #> 3 M aspirin 9 11.27 5.856 -5.411
  44. #> 4 M placebo 4 11.47 10.500 -0.975
  45. # 得到标准差
  46. cdata.sd <- aggregate(data["change"], by = data[c("sex",
  47. "condition")], FUN = sd)
  48. # 重命名列
  49. names(cdata.sd)[names(cdata.sd) == "change"] <- "change.sd"
  50. cdata.sd
  51. #> sex condition change.sd
  52. #> 1 F aspirin 0.8643
  53. #> 2 M aspirin 1.1308
  54. #> 3 F placebo 0.5248
  55. #> 4 M placebo 0.7805
  56. # 合并
  57. cdata <- merge(cdata, cdata.sd)
  58. cdata
  59. #> sex condition N before after change change.sd
  60. #> 1 F aspirin 5 11.06 7.640 -3.420 0.8643
  61. #> 2 F placebo 12 10.13 8.075 -2.058 0.5248
  62. #> 3 M aspirin 9 11.27 5.856 -5.411 1.1308
  63. #> 4 M placebo 4 11.47 10.500 -0.975 0.7805
  64. # 计算标准误
  65. cdata$change.se <- cdata$change.sd/sqrt(cdata$N)
  66. cdata
  67. #> sex condition N before after change change.sd
  68. #> 1 F aspirin 5 11.06 7.640 -3.420 0.8643
  69. #> 2 F placebo 12 10.13 8.075 -2.058 0.5248
  70. #> 3 M aspirin 9 11.27 5.856 -5.411 1.1308
  71. #> 4 M placebo 4 11.47 10.500 -0.975 0.7805
  72. #> change.se
  73. #> 1 0.3865
  74. #> 2 0.1515
  75. #> 3 0.3769
  76. #> 4 0.3902

如果你有 NA 值想要跳过,设置 na.rm=TRUE:

  1. cdata.means <- aggregate(data[c("before", "after", "change")],
  2. by = data[c("sex", "condition")], FUN = mean, na.rm = TRUE)
  3. cdata.means
  4. #> sex condition before after change
  5. #> 1 F aspirin 11.06 7.640 -3.420
  6. #> 2 M aspirin 11.27 5.856 -5.411
  7. #> 3 F placebo 10.13 8.075 -2.058
  8. #> 4 M placebo 11.47 10.500 -0.975