96

特定の列に対して同じ操作を実行したい data.table があります。これらの列の名前は文字ベクトルで与えられます。この特定の例では、これらすべての列に -1 を掛けたいと思います。

いくつかのおもちゃのデータと、関連する列を指定するベクトル:

library(data.table)
dt <- data.table(a = 1:3, b = 1:3, d = 1:3)
cols <- c("a", "b")

今、私はこのようにして、文字ベクトルをループしています:

for (col in 1:length(cols)) {
   dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
}

forループなしで直接これを行う方法はありますか?

4

7 に答える 7

164

これはうまくいくようです:

dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols]

結果は

    a  b d
1: -1 -1 1
2: -2 -2 2
3: -3 -3 3

ここにはいくつかのトリックがあります:

  • には括弧があるため、結果は「cols」という名前の新しい変数ではなく、で(cols) :=指定された列に割り当てられます。cols
  • .SDcolsこれらの列のみを見ていることを呼び出しに伝え、それらの列に関連付けられた ata.SDSサブセットであるを使用できるようにします。D
  • lapply(.SD, ...)これは.SD、列のリストです (すべての data.frames および data.tables と同様)。lapplyはリストを返すので、最終的jには のようになりcols := list(...)ます。

編集:@Arunが述べたように、おそらくより高速な別の方法があります:

for (j in cols) set(dt, j = j, value = -dt[[j]])
于 2013-05-30T21:59:05.963 に答える
1

列の文字列ベクトルに基づいて新しい列を作成する例を追加します。Jflyの回答に基づく:

dt <- data.table(a = rnorm(1:100), b = rnorm(1:100), c = rnorm(1:100), g = c(rep(1:10, 10)))

col0 <- c("a", "b", "c")
col1 <- paste0("max.", col0)  

for(i in seq_along(col0)) {
  dt[, (col1[i]) := max(get(col0[i])), g]
}

dt[,.N, c("g", col1)]
于 2019-02-04T10:29:06.647 に答える
0

dplyr関数はdata.tables で機能するため、dplyr「for ループを回避する」ソリューションもここにあります:)

dt %>% mutate(across(all_of(cols), ~ -1 * .))

orhan のコード (行と列の追加) を使用してベンチマークを行ったところ、他のほとんどのソリューションよりも実行速度が速く、lapply を使用dplyr::mutateacrossた data.table ソリューションよりも遅いことがわかります。

library(data.table); library(dplyr)
dt <- data.table(a = 1:100000, b = 1:100000, d = 1:100000) %>% 
  mutate(a2 = a, a3 = a, a4 = a, a5 = a, a6 = a)
cols <- c("a", "b", "a2", "a3", "a4", "a5", "a6")

dt %>% mutate(across(all_of(cols), ~ -1 * .))
#>               a       b      d      a2      a3      a4      a5      a6
#>      1:      -1      -1      1      -1      -1      -1      -1      -1
#>      2:      -2      -2      2      -2      -2      -2      -2      -2
#>      3:      -3      -3      3      -3      -3      -3      -3      -3
#>      4:      -4      -4      4      -4      -4      -4      -4      -4
#>      5:      -5      -5      5      -5      -5      -5      -5      -5
#>     ---                                                               
#>  99996:  -99996  -99996  99996  -99996  -99996  -99996  -99996  -99996
#>  99997:  -99997  -99997  99997  -99997  -99997  -99997  -99997  -99997
#>  99998:  -99998  -99998  99998  -99998  -99998  -99998  -99998  -99998
#>  99999:  -99999  -99999  99999  -99999  -99999  -99999  -99999  -99999
#> 100000: -100000 -100000 100000 -100000 -100000 -100000 -100000 -100000

library(microbenchmark)
mbm = microbenchmark(
  base_with_forloop = for (col in 1:length(cols)) {
    dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
  },
  franks_soln1_w_lapply = dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols],
  franks_soln2_w_forloop =  for (j in cols) set(dt, j = j, value = -dt[[j]]),
  orhans_soln_w_forloop = for (j in cols) dt[,(j):= -1 * dt[,  ..j]],
  orhans_soln2 = dt[,(cols):= - dt[,..cols]],
  dplyr_soln = (dt %>% mutate(across(all_of(cols), ~ -1 * .))),
  times=1000
)

library(ggplot2)
ggplot(mbm) +
  geom_violin(aes(x = expr, y = time)) +
  coord_flip()

reprex パッケージ(v0.3.0)により 2020-10-16 に作成

于 2020-10-16T11:25:22.813 に答える