Как применить ту же функцию к каждому указанному столбцу в данных.стол


у меня есть сведения.таблица, с которой я хотел бы выполнить ту же операцию на определенные столбцы. Имена этих столбцов задаются в символьном векторе. В этом конкретном примере я хотел бы умножить все эти столбцы на -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])))]
}

есть ли способ сделать это напрямую, без по петля?

3 56

3 ответа:

это, кажется, работает:

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 говорит вызов, что мы только смотрим на эти столбцы, и позволяет нам использовать .SD на Subset из Data, связанные с теми столбцы.
  • lapply(.SD, ...) работает на .SD, который представляет собой список столбцов (как и все данные.кадры и данные.таблицы.) lapply возвращает список, так что в конце j выглядит так:cols := list(...).

EDIT: вот еще один способ, который, вероятно, быстрее, как упоминал @Arun:

for (j in cols) set(dt, j = j, value = -dt[[j]])

Я хотел бы добавить ответ, когда вы хотите изменить название столбцов, а также. Это очень удобно, если вы хотите вычислить логарифм нескольких столбцов, что часто бывает в эмпирической работе.

cols <- c("a", "b")
out_cols = paste("log", cols, sep = ".")
dt[, c(out_cols) := lapply(.SD, function(x){log(x = x, base = exp(1))}), .SDcols = cols]

обновление: Ниже приведен аккуратный способ сделать это без цикла for

dt[,(cols):= - dt[,..cols]]

это аккуратный способ для легкой читаемости кода. Но что касается производительности, то она остается за решением Фрэнка в соответствии с приведенным ниже результатом microbenchmark

mbm = microbenchmark(
  base = for (col in 1:length(cols)) {
    dt[ , eval(parse(text = paste0(cols[col], ":=-1*", cols[col])))]
  },
  franks_solution1 = dt[ , (cols) := lapply(.SD, "*", -1), .SDcols = cols],
  franks_solution2 =  for (j in cols) set(dt, j = j, value = -dt[[j]]),
  hannes_solution = dt[, c(out_cols) := lapply(.SD, function(x){log(x = x, base = exp(1))}), .SDcols = cols],
  orhans_solution = for (j in cols) dt[,(j):= -1 * dt[,  ..j]],
  orhans_solution2 = dt[,(cols):= - dt[,..cols]],
  times=1000
)
mbm

Unit: microseconds
expr                  min        lq      mean    median       uq       max neval
base_solution    3874.048 4184.4070 5205.8782 4452.5090 5127.586 69641.789  1000  
franks_solution1  313.846  349.1285  448.4770  379.8970  447.384  5654.149  1000    
franks_solution2 1500.306 1667.6910 2041.6134 1774.3580 1961.229  9723.070  1000    
hannes_solution   326.154  405.5385  561.8263  495.1795  576.000 12432.400  1000
orhans_solution  3747.690 4008.8175 5029.8333 4299.4840 4933.739 35025.202  1000  
orhans_solution2  752.000  831.5900 1061.6974  897.6405 1026.872  9913.018  1000

как показано в таблице

performance_comparison_chart

Мой Предыдущий Ответ: Следующее также работает

for (j in cols)
  dt[,(j):= -1 * dt[,  ..j]]