Data.table过滤掉每列中的特定值(按列变化)

时间:2014-03-19 12:25:33

标签: r data.table

我有data.table个测量值,每列都有一个较低的检测限,(可能还有检测上限)

set.seed(1)
dt <- data.table(id=1:5, A=rnorm(5), B=rnorm(5, mean=2), C=rnorm(5,mean=-1))
setkey(dt, id)
# "randomly" disperse upper an lower limits to measurement columns
dt[3,A := -5]
dt[2,B := -3]
dt[5,B := 7]
dt[1,C := -10]
dt
   id          A         B           C
1:  1 -0.6264538  1.179532 -10.0000000
2:  2  0.1836433 -3.000000  -0.6101568
3:  3 -5.0000000  2.738325  -1.6212406
4:  4  1.5952808  2.575781  -3.2146999
5:  5  0.3295078  7.000000   0.1249309

我想在NA的每一列中过滤(设置为dt)输出值,这些值与另一个data.table中列出的测量下限和上限完全匹配:

limits <- data.table(measurement=LETTERS[1:3], lower=c(-5,-3,-10), 
                     upper=c(NA, 7, NA))
setkey(limits, measurement)
limits
   measurement lower upper
1:           A    -5    NA
2:           B    -3     7
3:           C   -10    NA

我的预期输出是:

dt
   id          A        B          C
1:  1 -0.6264538 1.179532         NA
2:  2  0.1836433       NA -0.6101568
3:  3         NA 2.738325 -1.6212406
4:  4  1.5952808 2.575781 -3.2146999
5:  5  0.3295078       NA  0.1249309

我无法为此构建一个很好的解决方案,所以目前我正在使用一个笨拙的for循环来完成工作:

for (i in 1:nrow(dt)) {
  for (j in 2:ncol(dt)) {
    if (is.na(dt[i, j, with=F])) {
      next
    } else if (dt[i, j, with=F] == limits[names(dt)[j]][, lower]) {
      dt[i, j := NA_real_, with=F]
    } else if (is.na(limits[names(dt)[j]][, upper])) {
      next
    } else if (dt[i, j, with=F] == limits[names(dt)[j]][, upper]) {
      dt[i, j := NA_real_, with=F] 
    } else {
      next
    }   
  }
}

但必须有更好更好的东西吗?我对apply limits的每一行data.table dt的每一栏{{1}}进行了游戏,但没有取得任何成功。

2 个答案:

答案 0 :(得分:6)

首先,我按照以下方式转置您的limiits data.table:

require(reshape2)
require(data.table)
limits = dcast.data.table(melt(limits, id=1), variable ~ measurement)

#    variable  A  B   C
# 1:    lower -5 -3 -10
# 2:    upper NA  7  NA

然后,您可以匹配i的相应列,并使用NA将这些匹配替换为set,如下所示:

for (i in 2:ncol(dt)) {
    set(dt, i=which(dt[[i]] %in% limits[[i]]), j=i, value=NA_real_)
}

#    id          A        B          C
# 1:  1 -0.6264538 1.179532         NA
# 2:  2  0.1836433       NA -0.6101568
# 3:  3         NA 2.738325 -1.6212406
# 4:  4  1.5952808 2.575781 -3.2146999
# 5:  5  0.3295078       NA  0.1249309

答案 1 :(得分:2)

这是另一种选择:

dt[, 2:length(dt) := lapply(
  2:length(dt), 
  function(x) ifelse(.SD[[x]] %in% limits[x - 1, c(lower, upper)], NA, .SD[[x]])
) ]

由于limits中的行与dt中的列的顺序相同,因此您只需遍历列:

   id          A        B          C
1:  1 -0.6264538 1.179532         NA
2:  2  0.1836433       NA -0.6101568
3:  3         NA 2.738325 -1.6212406
4:  4  1.5952808 2.575781 -3.2146999
5:  5  0.3295078       NA  0.1249309