web-dev-qa-db-ja.com

data.tableの列をループし、それらの列を変換します

DTという名前の列を持つdata.tableRFと、下線_が含まれる多くの列があります。下線付きのすべての列をループして、そこからRF列を減算したいと思います。しかし、私は立ち往生しています。 :=data.table演算子のRHSのすべてが、動的変数では機能しないようです。

これが私のDTと必要な出力(ハードコードされた)です:

library(data.table)
DT <- data.table(RF  = 1:10,
                 S_1 = 11:20,
                 S_2 = 21:30)
#Desired output
DT[ , S_1 := S_1 - RF]
DT[ , S_2 := S_2 - RF]
DT
      RF S_1 S_2
 [1,]  1  10  20
 [2,]  2  10  20
 [3,]  3  10  20
...

ただし、これをより柔軟にする必要があります。つまり、名前に「_」が含まれるすべての列をループして、RFを減算します。

#1. try: Does not work; Interestingly, the i on the LHS of := is interpreted as the column i, but on the RHS of
#:= it is interpreted as 2 and 3, respectively
for (i in grep("_", names(DT))){
  DT[ , i:= i - 1, with=FALSE]
}
DT
          RF  S_1 S_2
 [1,]  1   1   2
 [2,]  2   1   2
 [3,]  3   1   2
...

#2. try: Work with parse and eval
for (i in grep("_", names(DT), value=TRUE)){
  DT[ , eval(parse(text=i)):= eval(parse(text=i)) - RF]
}
#Error in eval(expr, envir, enclos) : object 'S_1' not found

それを行う方法のヒントは素晴らしいでしょう。

編集:質問を投稿するとすぐに、私は自分自身に思いました:なぜあなたはそもそも:=演算子を使っているのですか、そして確かに、私はそうする必要がないことに気づきました。これは機能し、ループは必要ありません。

DT[, grep("_", names(DT)), with=FALSE] - DT[, RF]

そのために残念。ただし、:=演算子を使用したアプローチが機能しない理由にまだ関心があるため、質問は開いたままにしておきます。だから多分誰かがそこで私を助けることができます。

28
Christoph_J

あなたは2回目の試みで正しい軌道に乗っていました。これは、substituteを使用して、'j'DT[ , j ]引数として渡される式を作成するアプローチです。

for (i in grep("_", names(DT), value=TRUE)){
    e <- substitute(X := X - RF, list(X = as.symbol(i)))
    DT[ , eval(e)]
}
DT
#     RF S_1 S_2
# [1,]  1  10  20
# [2,]  2  10  20
# [3,]  3  10  20
# [4,]  4  10  20
# [5,]  5  10  20

または今(1年後)with=FALSE:=のLHSに適用されますok:

for (i in grep("_", names(DT), value=TRUE))
    DT[, i:=get(i)-RF, with=FALSE]

またはwith=FALSEは、LHSを記号ではなく式にすることで回避できます。

for (i in grep("_", names(DT), value=TRUE))
    DT[, (i):=get(i)-RF]
14
Josh O'Brien

質問を投稿した後に残念ながら発見した回避策は次のとおりです。

DT[, grep("_", names(DT)), with=FALSE] - DT[, RF]

これは、保持したい追加の列がある、より複雑な設定でも機能しますが、追加の労力が必要です。

library(data.table)
DT <- data.table(RF  = 1:10,
                 S_1 = 11:20,
                 S_2 = 21:30,
                 addCol = rnorm(10)) #Column that should not be subtracted by RF, but still kept in DT

DT <- cbind(DT[, grep("_", names(DT)), with=FALSE] - DT[, RF], addCol = DT[, addCol])
4
Christoph_J

質問と回答をありがとうございます。 私は同様のタスクのために一時変数の助けを借りてソリューションを使用していました。

_varnames <- grep("_", names(DT), value=TRUE)
for (i in varnames) {
  DT[, ".tmp"] <- DT[, i, with = F]
  DT[, i := .tmp - RF, with = F]
  if (i == tail(varnames, 1)) DT[, ".tmp"] <- NULL
}
_

唯一のリスクは、既存の変数_.tmp_を上書きすることです。

すごい。 set()は強力です。

_varnames <- grep("_", names(DT), value=TRUE)
set(DT, j = varnames, value = DT[, varnames, with = F] - DT[, RF])
_
4
djhurio