我有一个行为任务的数据,看起来像这样(假设数据框名为 data):
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
}
。这标志着受试者 8666 的 1960 年 RT 和 5452 的 4551 年作为异常值。
但是,我觉得必须有一种更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
然后将其用作查找表,以选择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)