【问题标题】:Update values in specified columns based on external data table根据外部数据表更新指定列中的值
【发布时间】:2015-09-29 16:21:10
【问题描述】:

我想根据另一个数据表的外部值更新数据表的某些指定列中的值。

我知道如何逐个变量地处理这个变量,但我想要一个更有效的解决方案,我可以自动化,也许使用lapply

更新:我的数据集(相当于下面示例中的mtcars)还有其他我不想更新的列。

对于一个可重现的示例,首先运行这个 sn-p 代码

# Load library
  library(data.table)

# load data
  data(mtcars)

# create another Data Table which we'll use as external reference
# this table shows the number of decimals of those variables we want to modify in mtcars
  var <- c("mpg","disp","hp")
  Decimal <- c(1,3,2)
  DT <- cbind.data.frame(var, Decimal)

# convert into data.table
  setDT(DT)
  setDT(mtcars)

我的代码,逐列更新

mtcars[, mpg  := mpg  / 10 ^ DT[var=="mpg",  Decimal] ]
mtcars[, disp := disp / 10 ^ DT[var=="disp", Decimal] ]
mtcars[, hp   := hp   / 10 ^ DT[var=="hp",   Decimal] ]

这段代码运行良好,并且给出了预期的结果。

期望的结果

mtcars 的第一行以前是这样的:

>     mpg disp  hp
> 1: 21.0  160 110

现在看起来像这样:

>     mpg   disp   hp
> 1: 2.10  0.160 1.10

有没有使用functionlapply等更有效的解决方案?

【问题讨论】:

    标签: r data.table lapply


    【解决方案1】:

    我们还可以将set 用于多个列。由于避免了[.data.table 的开销,因此非常有效。我们遍历 'mtcars' 的列索引,并使用 set 将 'j' 指定的列更改为 value 从计算相应的 'mtcars' 列与 'DT$Decimal' 元素。

    for(j in seq_along(mtcars)){
      set(mtcars, i=NULL, j=j, value=mtcars[[j]]/10^DT[,Decimal][j])
     }
    
    
    head(mtcars)
    #    mpg  disp   hp
    #1: 2.10 0.160 1.10
    #2: 2.10 0.160 1.10
    #3: 2.28 0.108 0.93
    #4: 2.14 0.258 1.10
    #5: 1.87 0.360 1.75
    #6: 1.81 0.225 1.05
    

    编辑:基于 OP 的 cmets,假设如果我们没有对数据集进行子集化并希望保留所有列,同时转换 'var' 中指定的某些列,我们可以遍历 'var' 并使用 @987654326 @ 更改“var”指定的列。在这里,我在转换为data.table 后使用完整的mtcars 数据集。

     for(j in seq_along(var)){
      set(mtcars, i=NULL, j=var[j], value=mtcars[[var[j]]]/10^DT[, Decimal][j])
     }
    
    head(mtcars)
    #    mpg cyl  disp   hp drat    wt  qsec vs am gear carb
    #1: 2.10   6 0.160 1.10 3.90 2.620 16.46  0  1    4    4
    #2: 2.10   6 0.160 1.10 3.90 2.875 17.02  0  1    4    4
    #3: 2.28   4 0.108 0.93 3.85 2.320 18.61  1  1    4    1
    #4: 2.14   6 0.258 1.10 3.08 3.215 19.44  1  0    3    1
    #5: 1.87   8 0.360 1.75 3.15 3.440 17.02  0  0    3    2
    #6: 1.81   6 0.225 1.05 2.76 3.460 20.22  1  0    3    1
    

    【讨论】:

    • 我应该更清楚,@akrun。现在它工作得很好!
    【解决方案2】:

    看起来Map() 会这样做

    library(data.table)
    ## match 'DT$var' to the names of 'mtcars'
    m <- chmatch(levels(DT$var)[DT$var], names(mtcars))
    ## update 'mtcars' with the desired operation
    mtcars[, names(mtcars) := Map("/", .SD, 10 ^ DT$Decimal[m])]
    ## result
    head(mtcars)
    #     mpg  disp   hp
    # 1: 2.10 0.160 1.10
    # 2: 2.10 0.160 1.10
    # 3: 2.28 0.108 0.93
    # 4: 2.14 0.258 1.10
    # 5: 1.87 0.360 1.75
    # 6: 1.81 0.225 1.05
    

    或者,如果您想走得更快一点,我们可以使用 .mapply() 代替 Map() 调用将是

    .mapply(`/`, list(.SD, 10 ^ DT$Decimal[match(DT$var, names(mtcars))]), NULL)
    

    【讨论】:

    • 这需要两倍的对象内存。
    • @Arun - 那么应该是mtcars[, names(mtcars) := Map("/", .SD, DT$Decimal)] 吗?
    • 问题是 lapply 等和 map 首先必须在分配之前返回整个列表。所以我们无法避免内存需求,除非我们使用 for 循环......?
    • 但也许我们可以捕捉到这些并在内部进行 oprimise。好点!
    猜你喜欢
    • 2021-01-14
    • 1970-01-01
    • 2014-10-20
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 2011-06-15
    • 2014-03-10
    相关资源
    最近更新 更多