首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >R-我有一个for循环来识别每一列的每一行中的异常值-如何循环来查看每一列?

R-我有一个for循环来识别每一列的每一行中的异常值-如何循环来查看每一列?
EN

Stack Overflow用户
提问于 2021-01-23 03:43:08
回答 2查看 127关注 0票数 0

我有一个很长的数据集(每pt行数),其中包含多个变量的列。我已经创建了一个for循环来遍历每一行,并根据每个参与者对特定列/变量的结果打印出每个参与者的id。在下面查看列x的示例中,这正确地将Pt6标识为变量x上的异常值。

代码语言:javascript
复制
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,所以我认为它不会单独查看列?

代码语言:javascript
复制
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理解方面的差距。谢谢!

EN

回答 2

Stack Overflow用户

发布于 2021-01-23 04:09:41

让我们从查看您的for-loops开始。通过将结果(均值等)存储在一个变量中,您可以非常容易地优化这些结果,这样就不必重新计算这些结果。到目前为止,这是你的循环中最慢的部分,所以提升将是显着的。在您的第一个代码示例中,如下所示:

代码语言:javascript
复制
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,]

对于您的下一次循环,存储“离群值指示器”的矩阵或仅存储行/列对将是有意义的,例如作为列表。你已经走了大部分路了。在外部循环中循环列是有意义的,因此您可以再次避免在每次迭代时重新计算平均值和标准差

代码语言:javascript
复制
# 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中的许多函数都是矢量化的。所以我们可以大大简化这一过程。矢量化基本上允许我们评估向量输入上的函数,这也可以用于逻辑比较,如<<===等。这允许我们在这种情况下删除行迭代,并极大地简化了代码。例如,第一个代码将简化为

代码语言:javascript
复制
# 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

而后者甚至可以这样做:

代码语言:javascript
复制
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并不会带来真正的性能提升,但对于像这样的较小的调用,它更容易理解。

票数 0
EN

Stack Overflow用户

发布于 2021-01-23 04:55:27

下面的代码计算列的平均值和SD的第一个。然后是µ+/- 2sd的极限。然后使用sapply循环查看哪些列元素在这些限制内。最后,它根据sapply的结果设置id列的子集。

代码语言:javascript
复制
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"
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65851750

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档