У меня есть таблица данных, с которой я хотел бы выполнить ту же операцию с определенными столбцами. Имена этих столбцов даны в векторе символов. В этом конкретном примере я хотел бы умножить все эти столбцы на -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?
r
data.table
Дин МакГрегор
источник
источник
set
сfor-loop
. Я подозреваю, что это будет быстрее.set
.for
цикл сset
такими случаями.set()
кажется быстрее, ~ в 4 раза быстрее для моего набора данных! Удивительный.Я хотел бы добавить ответ, когда вы также хотите изменить название столбцов. Это очень удобно, если вы хотите вычислить логарифм нескольких столбцов, что часто бывает при эмпирической работе.
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]
источник
out_cols
, оставаясь при этомcols
на месте. Итак, вам нужно будет устранить их, либо явно 1) запросив только log.a и log.b: связать a[,.(outcols)]
до конца и повторно сохранить вdt
via<-
. 2) удалить старые колонны с цепочкой[,c(cols):=NULL]
. За решением без цепочки 3)dt[,c(cols):=...]
следуетsetnames(dt, cols, newcols)
ОБНОВЛЕНИЕ: Ниже приведен изящный способ сделать это без цикла.
Это удобный способ облегчить чтение кода. Но что касается производительности, он уступает решению Фрэнка, согласно приведенному ниже результату микробенча
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
как показано в таблице ниже
Мой предыдущий ответ: Следующее также работает
for (j in cols) dt[,(j):= -1 * dt[, ..j]]
источник
dt
состоящим из 3 строк?Ни одно из вышеперечисленных решений не работает с расчетом по группе. Вот лучшее, что у меня есть:
for(col in cols) { DT[, (col) := scale(.SD[[col]], center = TRUE, scale = TRUE), g] }
источник
Чтобы добавить пример создания новых столбцов на основе строкового вектора столбцов. На основании ответа 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)]
источник
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
источник
dt[, cols] <- dt[, cols] * (-1)
dplyr
функции работают сdata.table
s, поэтому вотdplyr
решение, которое также "избегает цикла for" :)dt %>% mutate(across(all_of(cols), ~ -1 * .))
Я протестированные его с помощью кода Орхана (добавление строк и столбцов) , и вы увидите ,
dplyr::mutate
сacross
основном выполняется быстрее , чем большинство других решений и медленнее , чем data.table решения с использованием lapply.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()
Создано 16.10.2020 пакетом REPEX (v0.3.0)
источник