【问题标题】:Apply if else function looping through 2 vectors to manipulate a data table应用 if else 函数循环遍历 2 个向量来操作数据表
【发布时间】:2019-01-30 03:26:54
【问题描述】:

我想应用一个 ifelse 函数循环遍历 2 个向量,然后根据条件更新数据表的列。我正在寻找一种可以处理大量列的解决方案。

我正在通过一个玩具数据集 mtcars 演示这个问题。

library(data.table)
mtcars <- data.table(mtcars)

现在,我想限制某些列的值,并用定义的限制替换相应列的值。但是下面的代码给了我奇怪的结果。

limitlist <- list(c("hp", 300), c("disp", 450.0))
cols <- sapply(limitlist, "[[", 1)
lims <- sapply(limitlist, "[[", 2)
for (i in length(limitlist)) mtcars[, c(cols) := lapply(.SD, function(x){ifelse(x[i] > lims[i], lims[i], x[i])}), .SDcols = cols]

我想要的输出:

    range(mtcars$hp)
[1]  52 300


    range(mtcars$disp)
[1]  71.1 450.0

我是 data.table 语法的新手,所以可能是一个愚蠢的错误。对此的任何帮助都非常感谢。

【问题讨论】:

  • 为什么不直接做mydata$arr_time &lt;- pmin(2000, mydata$arr_time)
  • @Uwe - 我已经编辑了可重现的示例。不幸的是,c(col) := 也不起作用。
  • @Andrew - 我正在寻找一种可以在大量列上工作的通用解决方案。
  • setDT(mtcars) 抛出错误:无法通过引用将 'mtcars' 转换为 data.table,因为绑定已锁定。 'mtcars' 很可能驻留在已锁定以防止修改其变量绑定的包(或环境)中。尝试将对象复制到当前环境,例如:var
  • @Uwe - 感谢您指出这一点。我已经编辑了问题。

标签: r data.table


【解决方案1】:

对于data.table 的新手来说,这是相当先进的东西。但是,这里还有其他三种变体:

  1. 子集和更新
  2. 更新加入
  3. 使用set()

这些方法仅更新相应列向量中受影响的元素,而到目前为止发布的其他解决方案(Frank'sDan Y's)会替换整个列。如果只需要更换几个元素,可能会提高性能。

请注意,我们使用的是 OP 提供的 limitlist

1。子集和更新

# subsetting and updating
library(data.table)
DT <- data.table(mtcars)
limitlist <- list(c("hp", 300), c("disp", 450.0))
cols <- sapply(limitlist, "[[", 1)
lims <- as.numeric(sapply(limitlist, "[[", 2))
for (i in seq_along(limitlist))
  DT[get(cols[i]) > lims[i], (cols[i]) := lims[i]]
# check ressults
sapply(cols, function(x) {cbind(max(mtcars[, x]), max(DT[[x]]))})
      hp disp
[1,] 335  472
[2,] 300  450

2。更新加入

# update join
library(data.table)
DT <- data.table(mtcars)
limitlist <- list(c("hp", 300), c("disp", 450.0))
cols <- sapply(limitlist, "[[", 1)
lims <- as.numeric(sapply(limitlist, "[[", 2))
for (i in seq_along(limitlist))
  DT[.(lims[i]), on = sprintf("%s>%s", cols[i], "V1"), (cols[i]) := lims[i]]
# check results
sapply(cols, function(x) {cbind(max(mtcars[, x]), max(DT[[x]]))})
      hp disp
[1,] 335  472
[2,] 300  450

3。使用set()

# using `set()`
library(data.table)
DT <- data.table(mtcars)
limitlist <- list(c("hp", 300), c("disp", 450.0))
cols <- sapply(limitlist, "[[", 1)
lims <- as.numeric(sapply(limitlist, "[[", 2))
for (i in seq_along(limitlist))
  set(DT, which(DT[[cols[i]]] > lims[i]), cols[i], lims[i])
# check results
sapply(cols, function(x) {cbind(max(mtcars[, x]), max(DT[[x]]))})
      hp disp
[1,] 335  472
[2,] 300  450

恕我直言,这种变体是最直接的方法。

【讨论】:

    【解决方案2】:

    由于 Dan 的回答没有使用 data.table 语法...

    library(data.table)
    
    # input
    mylist = list(hp = 300, disp = 450)
    DT = data.table(mtcars)
    
    # update
    DT[, names(mylist) := Map(pmin, .SD, mylist), .SDcols=names(mylist)]
    

    【讨论】:

    • 它适用于 data.tables,但它不使用 data.table 的 := 运算符:)
    【解决方案3】:

    应该这样做:

    首先,让你的 limlist 是数字而不是字符:

    lims <- as.numeric(sapply(limitlist, "[[", 2))
    

    然后你可以循环:

    for (i in 1:length(limitlist)) {
        mtcars[[cols[i]]] <- pmin(mtcars[[cols[i]]], lims[i])
    } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-27
      • 2012-08-25
      相关资源
      最近更新 更多