使用 apply() 及其相关项替换循环以应用离群值测试(例如)

Using apply() and its correlaries to replace loops to apply outlier test (e.g.)

我有来自行为任务的数据,看起来像这样(假设数据框名为数据):

data <- data.frame(subject = c(rep(8666, 6), rep(5452, 6)), RT = c(714, 877, 665, 854, 1092, 1960, 770, 4551, 1483, 1061, 755, 1090))
data
subject  RT
8666      714
8666      877
8666      665
8666      854
8666     1092
8666     1960
5452      770
5452     4551
5452     1483
5452     1061
5452      755
5452     1090

也就是说,对于这个问题,我正在研究一系列主题和反应时间。 (总共有 183 个受试者,每个受试者有 156 次试验。)使用 reshape 的 cast() 函数,我为每个受试者计算了一个值,我想用它来排除某些试验。

outl <- function(x) {
    2.5 * mad(x) + median(x)
    }
melteddata <- melt(data, id.vars="subject", measure.vars = "RT")
outliers <- cast(melteddata, subject ~ ., outl)
colnames(outliers)[2] <- "outlier"

输出如下:

  subject    outlier
1    5452   2235.635
2    8666   1517.844
...

现在,我通常这样做的方法是编写一个循环,针对每个唯一的受试者编号,将他们的 RT 与该受试者的离群值进行比较:

data$outliers <- 0
for(subject in unique(data$subject)) {
    temp <- data[data$subject == subject,]
    temp$outliers <- ifelse(temp$RT > outliers[outliers$subject == subject,]$outlier, 0, 1)
    data[data$subject == subject,]$outliers <- temp$outliers
    }

... 将 1960 年的 RTs 标记为异常值,主题 8666 和 4551 的 5452。

但是,我觉得必须有更 R 的方法来做到这一点。感觉 apply() 应该可以做同样的事情,当然这需要很长时间才能 运行 作为一个循环。有什么建议么?

编辑: 我意识到我可以使用 library(plyr) 包中的 ddply() 而不是使用 melt() 和 cast():

library(plyr)
outliers <- ddply(data, .(subject), summarize, median = median(RT), mad = mad(RT), outlier = median(RT) + 2.5 * mad(RT))

试一试。将异常值数据框转换为命名向量:

out <- outliers$outlier
names(out) <- outliers$subject

然后将其用作 table 到 select RT 列小于主题离群值的所有数据行的查找:

data[data$RT < out[as.character(data$subject)], ]

as.character 是必需的,因为主题 ID 是整数,并且您不想获取 out 的第 8666 个元素。

编辑 以添加 dplyr 解决方案:

group_by(data, subject) %>% summarize(outlier = 2.5 * mad(RT) + median(RT)) -> outliers
merge(data, outliers)
filter(data, RT < outlier)