Как применить одну и ту же функцию к каждому указанному столбцу в таблице данных.
У меня есть таблица данных, с которой я хотел бы выполнить одну и ту же операцию в определенных столбцах. Имена этих столбцов указаны в символьном векторе. В этом конкретном примере я хотел бы умножить все эти столбцы на -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?
Ответы
Ответ 1
Это работает:
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
, S
ubset D
ata, связанных с этими столбцами.
-
lapply(.SD, ...)
работает на .SD
, который представляет собой список столбцов (например, все data.frames и data.tables). lapply
возвращает список, поэтому в конце j
выглядит как cols := list(...)
.
РЕДАКТИРОВАТЬ. Здесь еще один способ, который, вероятно, быстрее, поскольку @Arun упомянул:
for (j in cols) set(dt, j = j, value = -dt[[j]])
Ответ 2
Я хотел бы добавить ответ, если вы хотите также изменить имя столбцов. Это очень удобно, если вы хотите вычислить логарифм из нескольких столбцов, что часто бывает в эмпирической работе.
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]
Ответ 3
ОБНОВЛЕНИЕ: Следующее - отличный способ сделать это без цикла for
dt[,(cols):= - dt[,..cols]]
Это удобный способ для удобства чтения кода. Но что касается производительности, то она остается за решением Frank согласно приведенному ниже результату микробенчмарка.
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]]
Ответ 4
Кажется, что ни одно из перечисленных выше решений не работает с расчетом по группам Следующее - лучшее, что я получил:
for(col in cols)
{
DT[, (col) := scale(.SD[[col]], center = TRUE, scale = TRUE), g]
}
Ответ 5
library(data.table)
(dt <- data.table(a = 1:3, b = 1:3, d = 1:3))
Hence:
a b d
1: 1 1 1
2: 2 2 2
3: 3 3 3
Whereas (dt*(-1)) yields:
a b d
1: -1 -1 -1
2: -2 -2 -2
3: -3 -3 -3
Ответ 6
Добавить пример для создания новых столбцов на основе строкового вектора столбцов. На основании ответа 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)]