我有一个很长的数据集(每pt行数),其中包含多个变量的列。我已经创建了一个for循环来遍历每一行,并根据每个参与者对特定列/变量的结果打印出每个参与者的id。在下面查看列x的示例中,这正确地将Pt6标识为变量x上的异常值。
dat <- data.frame(id=c("Pt1","Pt2", "Pt3","Pt4", "Pt5", "Pt6"),
x=c(1,3,3,3,5,31),
y=c(2,9,10,10.5,10.5,11),
z=c(34,34,34,35,68,36))
for (row in 1:nrow(dat)) {
variable <- dat[row, "x"]
id <- dat[row, "id"]
if((variable>(mean(dat$x, na.rm=TRUE)
+ (2*sd(dat$x, na.rm=TRUE))))
|
(variable<(mean(dat$x, na.rm=TRUE)
- (2*sd(dat$x, na.rm=TRUE))))
)
{
print(id)
}}但是,我想单独标识所有参与者,它们是基于每一列的异常值-在示例数据中,它应该标识Pt6 (因为它们的x值)和Pt1 (因为它们的y值)和Pt5 (因为它们的z值)。
我知道我需要嵌套另一个for循环来遍历列,就像下面这样,但它只识别Pt5,所以我认为它不会单独查看列?
for (row in 1:nrow(dat)) {
for (col in 1:ncol(dat))
value <- dat[row, col]
id <- dat[row, "id"]
if((value>(mean(dat[[col]], na.rm=TRUE)
+ (2*sd(dat[[col]], na.rm=TRUE))))
|
(value<(mean(dat[[col]], na.rm=TRUE)
- (2*sd(dat[[col]], na.rm=TRUE))))
)
{
print(id)
}}我对forloop(显然)是个新手--试图摆脱复制粘贴的坏习惯。我试过寻找其他答案,但我不知道如何在这里应用/它们不在R中。谢谢帮助!对不同的方法完全开放(例如,应用基于的方法),但如果可能的话,我非常愿意填补我在forloop理解方面的差距。谢谢!
发布于 2021-01-23 04:09:41
让我们从查看您的for-loops开始。通过将结果(均值等)存储在一个变量中,您可以非常容易地优化这些结果,这样就不必重新计算这些结果。到目前为止,这是你的循环中最慢的部分,所以提升将是显着的。在您的第一个代码示例中,如下所示:
dat <- data.frame(id=c("Pt1","Pt2", "Pt3","Pt4", "Pt5", "Pt6"),
x=c(1,3,3,3,5,31),
y=c(2,9,10,10.5,10.5,11),
z=c(34,34,34,35,68,36))
# Pre-define variables
mu <- mean(dat$x, na.rm = TRUE)
sd2 <- 2 * sd(dat$x, na.rm = TRUE)
upper <- mu + sd2
lower <- mu - sd2
# Create storage
rows <- logical(n <- nrow(dat))
for (row in 1:n) {
variable <- dat[row, "x"]
if(variable > upper || variable < lower)
{
# Set index to true, for row being an "outlier"
rows[row] <- TRUE
}
}
# Print outlier rows
dat[rows,]对于您的下一次循环,存储“离群值指示器”的矩阵或仅存储行/列对将是有意义的,例如作为列表。你已经走了大部分路了。在外部循环中循环列是有意义的,因此您可以再次避免在每次迭代时重新计算平均值和标准差
# Specify columns to iterate over
cols <- names(dat)[-1]
# Storage for outliers
outliers <- list()
for(j in cols){
# Pre-define variables
mu <- mean(dat[, j], na.rm = TRUE)
sd2 <- 2 * sd(dat[, j], na.rm = TRUE)
upper <- mu + sd2
lower <- mu - sd2
# Create storage
rows <- logical(n <- nrow(dat))
for (row in 1:n) {
variable <- dat[row, j]
if(variable > upper || variable < lower)
{
# Set index to true, for row being an "outlier"
rows[row] <- TRUE
}
}
outliers[[j]] <- rows
}
# Print outliers
dat[outliers[['x']], ]
dat[outliers[['y']], ]
dat[outliers[['z']], ]这是一种这样做的方法。但R中的许多函数都是矢量化的。所以我们可以大大简化这一过程。矢量化基本上允许我们评估向量输入上的函数,这也可以用于逻辑比较,如<、<=、==等。这允许我们在这种情况下删除行迭代,并极大地简化了代码。例如,第一个代码将简化为
# Only 1 column
mu <- mean(dat$x)
sd2 <- sd(dat$x) * 2
upper <- mu + sd2
lower <- mu - sd2
rows <- dat$x > upper | datx < lower
# Alternative, cheeky 1 liner:
rows <- abs(dat$x) - (mean(dat$x) + 2 * sd(dat$x)) > 0而后者甚至可以这样做:
outliers <- lapply(dat[, c('x', 'y', 'z')],
function(x)x[abs(x) - (mean(x) + 2 * sd(x)) > 0])
dat[outliers[['x']], ]
dat[outliers[['y']], ]
dat[outliers[['z']], ]其中,我将for-loop替换为对lapply的调用,该调用将遍历dat中的列,并应用指定的function,为每一列返回一个列表。替换for-loop并不会带来真正的性能提升,但对于像这样的较小的调用,它更容易理解。
发布于 2021-01-23 04:55:27
下面的代码计算列的平均值和SD的第一个。然后是µ+/- 2sd的极限。然后使用sapply循环查看哪些列元素在这些限制内。最后,它根据sapply的结果设置id列的子集。
means <- colMeans(dat[-1], na.rm = TRUE)
sds <- apply(dat[-1], 2, sd, na.rm = TRUE)
ci95 <- means + cbind(-2*sds, 2*sds)
out <- sapply(seq_along(dat[-1]), function(i){
v <- dat[-1][[i]]
v < ci95[i, 1] | v > ci95[i, 2]
})
out
# [,1] [,2] [,3]
#[1,] FALSE TRUE FALSE
#[2,] FALSE FALSE FALSE
#[3,] FALSE FALSE FALSE
#[4,] FALSE FALSE FALSE
#[5,] FALSE FALSE TRUE
#[6,] TRUE FALSE FALSE
dat[[1]][rowSums(out) > 0]
#[1] "Pt1" "Pt5" "Pt6"https://stackoverflow.com/questions/65851750
复制相似问题