过滤多个R data.table列以消除异常值

时间:2017-07-31 21:22:13

标签: r data.table outliers

我希望消除高于或低于2个标准差的异常值,对于许多具有相似名称的变量(代码中单独指定的变量太多)。

library(data.table)

irisdt <- data.table(iris)
myCols <- grep("Sepal", colnames(irisdt), value=TRUE) 

# This works if I specify one column, 
# but I have too many columns to specify, so need to use grep approach.
irisdt[, Sepal.Length.Outlier := (scale(Sepal.Length) < -2 | scale(Sepal.Length) > 2)]

# This does not work
irisdt[, (myCols) := lapply(myCols, function(x) {(scale(x) < -2 | scale(x) > 2)} )] 

# This partially works, but changes in place
irisdt[, (myCols) := lapply(myCols, function(x) {(scale(irisdt[[x]]) < -2 | scale(irisdt[[x]]) > 2)} )] 
# How do I make new variables, for example "Sepal.Length.Outlier"?

myOutlierCols <- grep(".Outlier", colnames(irisdt), value=TRUE) 

# How do I select rows matching multiple columns (&)? 
irisdt[myOutlierCols=="FALSE"] # does not work
irisdt[, hasOutlier := lapply(myCols, myCols==TRUE)] # does not work
irisdt[hasOutlier=="FALSE"] # relies on line above, which doesn't work

也许一个函数可以采用data.table列并将其除去高于或低于z得分截止值的值。这可以和lapply一起使用。

# This does not work
removeOutliers <- function(myColumn, cutoff = 3) {
  lapply(myColumn, function (x) {
    if (scale(myColumn[[x]]) < -cutoff | scale(myColumn[[x]]) > cutoff) {
      x <- NA #specify individual value instead of column?
    } 
  })
}
removeOutliers(irisdt[,Sepal.Length]) # for testing
trimmedIrisdt <- irisdt[,lapply(.SD, removeOutliers(.SD)), .SDcols = myCols] # could do by = grouping variable

# Once outliers are made NA, this would work:
trimmedIrisdt <- complete.cases(trimmedIrisdt)

1 个答案:

答案 0 :(得分:2)

我想这实现了目标:

irisdt[, keep := 
  as.logical(do.call(pmin, lapply(.SD, function(x) abs(scale(x)) <= 2)))
, .SDcols = myCols]

res = irisdt[(keep), !"keep"]

     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
  1:          5.1         3.5          1.4         0.2    setosa
  2:          4.9         3.0          1.4         0.2    setosa
  3:          4.7         3.2          1.3         0.2    setosa
  4:          4.6         3.1          1.5         0.2    setosa
  5:          5.0         3.6          1.4         0.2    setosa
 ---                                                            
135:          6.7         3.0          5.2         2.3 virginica
136:          6.3         2.5          5.0         1.9 virginica
137:          6.5         3.0          5.2         2.0 virginica
138:          6.2         3.4          5.4         2.3 virginica
139:          5.9         3.0          5.1         1.8 virginica

如果存在分组变量,这也应该可以正常工作。我不知道它的统计稳健性。

工作原理:

  1. 测试每个单元格abs(scale(x)) <= 2
  2. 如果列间的最小结果为TRUE,则保留该行。
  3. 要了解它是如何逐个细胞工作的......

    library(data.table)
    
    mynewCols = paste0(myCols,"_outly")
    irisdt[, (mynewCols) := 
      lapply(.SD, function(x) replace(x, abs(scale(x)) <= 2, NA))
    , .SDcols = myCols]
    

    然后浏览View(irisdt[rowSums(!is.na(irisdt[, ..mynewCols])) > 0])