如何从数据框中删除超出特定变量范围的记录? [R]

How to remove records from dataframe that fall outside variable-specific ranges? [R]

我有一个数据框和一个要应用于数据的预测模型。但是,我想过滤掉模型可能不太适用的记录。为此,我有另一个数据框,其中包含每个变量在训练数据中观察到的最小值和最大值。我想从我的新数据中删除那些一个或多个值超出指定范围的记录。

为了明确我的问题,我的数据可能是这样的:

  id   x       y     
 ---- ---- --------- 
   1    2     30521  
   2   -1      1835  
   3    5     25939  
   4    4   1000000  

这是我的第二个 table,带有最小值和最大值,可能看起来像:

  var   min    max   
 ----- ----- ------- 
  x       1       5  
  y       0   99999  

在此示例中,我想在我的数据中标记以下记录:2(低于 x 的最小值)和 4(高于 y 的最大值)。

我如何在 R 中轻松地做到这一点?我有一种预感,有一些聪明的 dplyr 代码可以完成这项任务,但我不知道它会是什么样子。

不是很优雅,但总之:

df <- read.table(header=T, text="  id   x       y     
   1    2     30521  
   2   -1      1835  
   3    5     25939  
   4    4   1000000 ") 
df
ranges <- read.table(header=T, text="  var   min    max   
  x       1       5  
  y       0   99999")

ranges <- ranges[match(ranges[,1], names(df)[-1]), ] # sort ranges, if necessary
matrixStats::rowAnys(
  !sapply(seq_along(df)[-1], function(x) {
    df[,x]>=ranges[x-1,2] & df[,x]<=ranges[x-1,3]
  })
) -> df$flag
df$flag
# [1] FALSE  TRUE FALSE  TRUE

类似 dplyr 的东西:

library(dplyr)
df <- read.table(text = "  id   x       y     
           1    2     30521  
           2   -1      1835  
           3    5     25939  
           4    4   1000000  ", header = TRUE)


dfilte <- read.table(text = "  var   min    max
  x       1       5  
  y       0   99999  ", header = TRUE)


df  %>% mutate(flag_x = x %in% dfilte[1, -1],
               flax_y = y %in% dfilte[2, -1])

产生此输出:

  id  x       y flag_x flax_y
1  1  2   30521  FALSE  FALSE
2  2 -1    1835  FALSE  FALSE
3  3  5   25939   TRUE  FALSE
4  4  4 1000000  FALSE  FALSE

你有这样的数据:

df = data.frame(x=c(2,-1,5,4,7,8), y=c(30521, 1800, 25000,1000000, -5, 10))
limits = data.frame("var"=c("x", "y"), min=c(1,0), max=c(5,99999))

您可以将 sweep 函数与运算符 '>''<' 一起使用,非常简单!

sweep(df, 2, limits[, 2], FUN='>') & sweep(df, 2, limits[, 3], FUN='<')
####          x     y
#### [1,]  TRUE  TRUE
#### [2,] FALSE  TRUE
#### [3,] FALSE FALSE
#### [4,]  TRUE FALSE
#### [5,] FALSE FALSE
#### [6,] FALSE  TRUE

TRUE 位置告诉您要为每个变量保留哪些观测值。 它应该适用于任意数量的变量

之后如果你需要全局标志(至少在一列中有标志)你可以运行这个简单的行(res是之前的输出)

apply(res, 1, all)
#### [1]  TRUE FALSE FALSE FALSE FALSE FALSE

我认为你的问题很适合在 base R:

中使用 cut 函数
df$to.remove <- is.na(cut(df$x, breaks = ranges[1,][,-1])) | 
                is.na(cut(df$y, breaks = ranges[2,][,-1]))

#  id  x       y to.remove
#1  1  2   30521     FALSE
#2  2 -1    1835      TRUE
#3  3  5   25939     FALSE
#4  4  4 1000000      TRUE

is.na(...) 将为您提供一个逻辑向量,其中超出指定范围的值为 TRUE。最后,您应用 |,即 or 运算符来决定必须删除哪些。

要清理数据,您只需执行以下操作:

df <- df[!df$to.remove,]

编辑

我刚刚注意到(从您的评论中)您的数据框包含的变量不仅仅是 xy。在这种情况下,您可以定义一个名为 f 的函数,并对数据框中的任意多个变量执行以下操作。

f <- function(x, xrange, y, yrange) {
(is.na(cut(x, breaks = xrange)) | is.na(cut(y, breaks = yrange)))}

res <- f(df$x, ranges[1,][-1], df$y, ranges[2,][-1])

数据

df <- structure(list(id = 1:4, x = c(2L, -1L, 5L, 4L), y = c(30521L, 
1835L, 25939L, 1000000L)), .Names = c("id", "x", "y"), class = "data.frame", row.names = c(NA, 
-4L))

ranges <- structure(list(var = structure(1:2, .Label = c("x", "y"), class = "factor"), 
    min = c(1L, 0L), max = c(5L, 99999L)), .Names = c("var", 
"min", "max"), class = "data.frame", row.names = c(NA, -2L))

不太理解您想要的输出,但这适用于任何范围和任何数量的数据:

> df

  id  x       y
1  1  2   30521
2  2 -1    1835
3  3  5   25939
4  4  4 1000000


#I transpose your filter data frame so its easier to work with.
> dfFilter

    x     y
min 1     0
max 5 99999

然后您可以根据 dfFilter:

中的范围应用过滤器
#Flag original dataframe with values between the minimum x and maximum x 

   df$flag_x=ifelse(df$x > min(dfFilter$x) & df$x < max(dfFilter$x), "yes","no")


#Flag original dataframe with values between the minimum y and maximum y

   df$flag_y=ifelse(df$y > min(dfFilter$y) & df$y < max(dfFilter$y), "yes","no")

所以输出看起来像这样:

  id  x       y flag_x flag_y
1  1  2   30521    yes    yes
2  2 -1    1835     no    yes
3  3  5   25939     no    yes
4  4  4 1000000    yes    yes

当然,您可以更改此过滤器或对其进行任何数学运算,以便获得所需的输出(例如 x-2 的最小值:min(dfFilter$x)-2)。

希望有用。