如何在纵向数据集中winsorize(或删除单变量异常值)
我试图弄清楚如何在纵向数据集中对个人分组的观察结果进行排序 我从如何从变量的平均值中删除大于2个标准差的数据开始。作者还有用地展示了如何在类别中实现这一点 我的用例略有不同:我有一个纵向数据集,我想删除那些随着时间的推移系统地显示为异常值的个体。我不想剔除受试者的极端观察结果,而是想将这些个体完全排除在外(整理数据),或者用切割值替换底部和顶部的2.5%(winsorizing,请参阅:) 例如,我的长格式数据可能如下所示:如何在纵向数据集中winsorize(或删除单变量异常值),r,R,我试图弄清楚如何在纵向数据集中对个人分组的观察结果进行排序 我从如何从变量的平均值中删除大于2个标准差的数据开始。作者还有用地展示了如何在类别中实现这一点 我的用例略有不同:我有一个纵向数据集,我想删除那些随着时间的推移系统地显示为异常值的个体。我不想剔除受试者的极端观察结果,而是想将这些个体完全排除在外(整理数据),或者用切割值替换底部和顶部的2.5%(winsorizing,请参阅:) 例如,我的长格式数据可能如下所示: name time points MJ 1 998 MJ
name time points
MJ 1 998
MJ 2 1000
MJ 3 998
MJ 4 3000
MJ 5 998
MJ 5 420
MJ 6 999
MJ 7 998
Lebron 1 9
Lebron 2 1
Lebron 3 3
Lebron 4 900
Lebron 5 4
Lebron 5 4
Lebron 6 3
Lebron 7 8
Kobe 1 2
Kobe 2 1
Kobe 3 4
Kobe 4 2
Kobe 5 1000
Kobe 5 4
Kobe 6 7
Kobe 7 9
Larry 1 2
Larry 2 1
Larry 3 4
Larry 4 2
Larry 5 800
Larry 5 4
Larry 6 7
Larry 7 9
如果我想消除个体(name
)中点的极端观察,我的代码是:
do.call(rbind,by(df,df$name,function(x) x[!abs(scale(x$points)) > 2,]))
但我真正想做的是排除极端的个体(在本例中,MJ
)。我该怎么做呢
(请注意——在这里插入关于如何不删除异常值的所有警告。这只是一个稳健性测试!)我可以这样做:
means <- ddply(df, .(name), summarize, mean=mean(points))$mean
means <- mean(means)
upperBound <- 2
outlierTest <- ddply(df, .(name), summarize, outlier=ifelse(sum(points) / means > upperBound,
TRUE, FALSE))
keep <- outlierTest$name[!outlierTest$outlier]
df <- df[df$name %in% keep, ]
意味着这可能不适合您的数据,但我将尝试一个通用的解决方案,让您开始思考。我建议使用稳健的统计数据,如中位数和中位数绝对贬值(MAD)来定义离群值。首先,您可以查看每个个体的异常点比例(与所有点相比):
让df
成为您的数据帧
library(plyr)
med <- median(df$points)
md <- mad(df$points)
outlier.factor <- 2
daply(df, .(name), function(x) {sum(abs(x$points - m) > md * outlier.factor) / nrow(x)})
因此,MJ
的所有分数都是异常值,而12.5%是所有其他个体的异常值
现在可以使用阈值选择要删除的个人。例如,对于正态分布的数据,您预计约有4.55%会落在中位数±2 x MAD的范围之外。我只使用dplyr:
test <- read.csv("test.csv", header=TRUE)
library(dplyr)
test <- test %.%
group_by(name) %.%
mutate(mean_points=mean(points))
cut_point_top <- quantile(test$mean_points, 0.95)
cut_point_bottom <- quantile(test$mean_points, 0.05)
test <- test %.%
group_by(name) %.%
mutate(outlier_top = (mean_points >= cut_point_top),
outlier_bottom = mean_points <= cut_point_bottom) %.%
filter(!outlier_top & ! outlier_bottom)
测试好了,作为第一步,您需要定义汇总统计信息,根据该统计信息,您可以将某人识别为异常值。这是什么统计数据,以及它与其他值之间的距离应该被视为异常值?是的,这真的不应该是一个问题,除非我们不知道你将使用什么度量来定义MJ是极端的。@JoshO'Brien感谢你指出这一点;我将对我的问题作出澄清。最初的目标是进行95%的Winsorization,其中底部2.5%的个体和顶部2.5%的个体的观察值将替换为切割值。(见:)。然而,一个只“修剪”这些观察值的答案同样有用,因为问题在于识别而不是去除?我喜欢你的答案,但你如何用切点替换顶部和底部的2.5%?谢谢。我已经修改了我的答案来处理这种情况。第二个解决方案将更容易使用pmin
和pmax
有关如何使用pmin或pmax进行winsorizing的想法?我想到的是pmax(pmin(点,分位数,.95)),分位数(点,.05))。但接下来的技巧是将其作为新列插入数据帧中。思想?
test <- read.csv("test.csv", header=TRUE)
library(dplyr)
test <- test %.%
group_by(name) %.%
mutate(mean_points=mean(points))
cut_point_top <- quantile(test$mean_points, 0.95)
cut_point_bottom <- quantile(test$mean_points, 0.05)
test <- test %.%
group_by(name) %.%
mutate(outlier_top = (mean_points >= cut_point_top),
outlier_bottom = mean_points <= cut_point_bottom) %.%
filter(!outlier_top & ! outlier_bottom)
test <- test %.%
group_by(name) %.%
mutate(outlier_top = (mean_points >= cut_point_top),
outlier_bottom = mean_points <= cut_point_bottom)
test$points <- ifelse(test$outlier_top, cut_point_top,
ifelse(test$outlier_bottom, cut_point_bottom, test$points))