【问题标题】:Loop through datatable & alter values meeting a specific condition循环遍历数据表并更改满足特定条件的值
【发布时间】:2021-09-10 21:55:42
【问题描述】:

我正在尝试创建一个将数据表和变量作为参数的函数。数据表将始终有 4 列(第一列是日期列,其他 3 列是数字),但行数会有所不同。该变量是一个整数,设置为截止值。该函数的目标是输出数据表,其中数字列中的所有值都大于大于变量的第一个数字。这是正在测试的数据表的 sn-p

#datatable dt
> dput(dt[30:40])
structure(list(a = structure(c(18517, 18524, 18531, 18538, 18545, 
18552, 18559, 18566, 18573, 18580, 18587), class = "Date"), b = c(14L, 
16L, 18L, 21L, 23L, 26L, 29L, 32L, 35L, 39L, 42L), c = c(9L, 
10L, 12L, 14L, 16L, 18L, 21L, 23L, 26L, 29L, 32L), d = c(4L, 
5L, 6L, 8L, 9L, 11L, 13L, 16L, 18L, 20L, 23L)), row.names = c(NA, 
-11L), class = c("data.table", "data.frame"))
> dt[30:40]
             a  b  c  d
 1: 2020-09-12 14  9  4
 2: 2020-09-19 16 10  5
 3: 2020-09-26 18 12  6
 4: 2020-10-03 21 14  8
 5: 2020-10-10 23 16  9
 6: 2020-10-17 26 18 11
 7: 2020-10-24 29 21 13
 8: 2020-10-31 32 23 16
 9: 2020-11-07 35 26 18
10: 2020-11-14 39 29 20
11: 2020-11-21 42 32 23

这是我想出的功能:

cutoff <-  21 #some integer
checkDT <- function(dt, cutoff){
  columns <- c('b','c','d')
  for (i in columns){
    for (j in dt[,..columns]){
      if(is.infinite(min(j[which(j > cutoff)]))){
       dt <- dt
      }else{
       dt[i > min(j[which(j > cutoff)]), `:=` (i = NA)]
      }
     }
   return(dt)
  }
}

这将输出一个数据表,其中第五列 i 全部为 NA。如果我将此语句用于特定列而不是预期的输出,但我试图让函数执行此操作以摆脱某些代码行。

if(is.infinite(min(dt$b[which(dt$b > cutoff)]))){
    dt <- dt
  } else{
    dt[b > min(dt$b[which(dt$b > cutoff)]), `:=`(b = NA)] 
  }
> dt[30:40]
             a  b  c  d
 1: 2020-09-12 14  9  4
 2: 2020-09-19 16 10  5
 3: 2020-09-26 18 12  6
 4: 2020-10-03 21 14  8
 5: 2020-10-10 23 16  9
 6: 2020-10-17 NA 18 11
 7: 2020-10-24 NA 21 13
 8: 2020-10-31 NA 23 16
 9: 2020-11-07 NA 26 18
10: 2020-11-14 NA 29 20
11: 2020-11-21 NA 32 23

这是截止值为 21 的预期输出:

             a  b  c  d
 1: 2020-09-12 14  9  4
 2: 2020-09-19 16 10  5
 3: 2020-09-26 18 12  6
 4: 2020-10-03 21 14  8
 5: 2020-10-10 23 16  9
 6: 2020-10-17 NA 18 11
 7: 2020-10-24 NA 21 13
 8: 2020-10-31 NA 23 16
 9: 2020-11-07 NA NA 18
10: 2020-11-14 NA NA 20
11: 2020-11-21 NA NA 23

【问题讨论】:

  • 如果您能给我们这个dt 对象的样本会有所帮助,也许您可​​以使用data.table(...) 或仅dput(head(dt)) 以编程方式构建它?
  • 很抱歉,鉴于行数可能会有所不同,我不确定这是否会有所帮助。
  • 很好的编辑,谢谢@lagn91!
  • 你能给我们一些你的预期输出吗?当我尝试按原样运行您的代码时,我得到一个空输出,并且我无法解析“输出数据表,其中数字列中的所有值都大于大于变量的第一个数字”。您是对表格进行子集化,还是将某些值替换为 NA?
  • @Dubukay 我已经为显示的示例数据集添加了预期的输出。我没有对表格进行子集化,我正在尝试用 NA 替换每列大于第一个大于截止值的任何值。

标签: r data.table


【解决方案1】:

这是使用lapply.SDcols 的另一种方式。

checkDT <- function(dt1, cutoff) {
  columns <- c('b','c','d')
  dt1[, (columns) := lapply(.SD, function(x) 
          replace(x, x > x[x > cutoff][1], NA)), .SDcols = columns][]
}

checkDT(dt, 21)

#             a  b  c  d
# 1: 2020-09-12 14  9  4
# 2: 2020-09-19 16 10  5
# 3: 2020-09-26 18 12  6
# 4: 2020-10-03 21 14  8
# 5: 2020-10-10 23 16  9
# 6: 2020-10-17 NA 18 11
# 7: 2020-10-24 NA 21 13
# 8: 2020-10-31 NA 23 16
# 9: 2020-11-07 NA NA 18
#10: 2020-11-14 NA NA 20
#11: 2020-11-21 NA NA 23

【讨论】:

    【解决方案2】:

    我在这里简化了很多你的符号
    在 data.table 中,您不必在括号内再次使用 dt$
    which() 不是必需的,因为可以直接使用逻辑向量来指示要保留哪些行。
    关键是使用get函数将文本翻译成列名
    我只是使用了 suppressWarnings 来消除无限警告,
    在这种情况下,代码不会替换任何内容,这就是您想要的。

    checkDT <- function(dt, cutoff) {
      columns <- c('b', 'c', 'd')
      for (i in columns) {
        suppressWarnings(dt[get(i) > min(dt[get(i) > cutoff, get(i)]), (i) := NA]) 
      }
      dt[]
    }
    

    checkDT(dt, cutoff) 给出了想要的结果

    【讨论】:

    • 谢谢!我之前没有遇到过get 函数。这要简单得多,并且完全符合我的要求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-19
    • 2015-03-30
    • 1970-01-01
    • 2016-03-24
    • 2021-06-26
    • 2020-11-22
    • 1970-01-01
    相关资源
    最近更新 更多