Создание вектора из строки data.table без использования apply

Допустим, я хочу создать столбец в таблице data.table, в которой значение в каждой строке равно стандартному отклонению значений в трех других ячейках в той же строке. Например, если я сделаю

DT <- data.table(a = 1:4, b = c(5, 7, 9, 11), c = c(13, 16, 19, 22), d = c(25, 29, 33, 37))
DT
   a  b  c  d
1: 1  5 13 25
2: 2  7 16 29
3: 3  9 19 33
4: 4 11 22 37

и я хотел бы добавить столбец, содержащий стандартное отклонение a, b и d для каждой строки, например:

   a  b  c  d abdSD
1: 1  5 13 23 12.86
2: 2  7 16 27 14.36
3: 3  9 19 31 15.87
4: 4 11 22 35 17.39

Конечно, я мог бы написать цикл for или использовать функцию apply для вычисления этого. К сожалению, то, что я действительно хочу сделать, нужно применить к миллионам строк, это не такая простая функция, как вычисление стандартного отклонения, и ее нужно завершить за доли секунды, поэтому мне действительно нужно векторизованное решение. Я хочу написать что-то вроде

DT[, abdSD := sd(c(a, b, d))]

но, к сожалению, это не дает правильного ответа. Есть ли какой-либо синтаксис data.table, который может создать вектор из разных значений в одной строке и сделать этот вектор доступным для функции, заполняющей новую ячейку в этой строке? Любая помощь будет принята с благодарностью. @Arun

Он подан здесь github.com/Rdatatable/data.table/issues/1063

Frank 04.04.2018 05:45
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
127
5

Ответы 5

Я думаю, вам стоит попробовать пакет matrixStats

library(matrixStats)

#sample data
dt <- data.table(a = 1:4, b = c(5, 7, 9, 11), c = c(13, 16, 19, 22), d = c(25, 29, 33, 37))

dt[, `:=`(abdSD = rowSds(as.matrix(.SD), na.rm=T)), .SDcols=c('a','b','d')]
dt

Выход:

   a  b  c  d    abdSD
1: 1  5 13 25 12.85820
2: 2  7 16 29 14.36431
3: 3  9 19 33 15.87451
4: 4 11 22 37 17.38774

Не ответ, но просто пытаюсь показать разницу между использованием apply и решением, предоставленным Prem выше:

Я увеличил выборку данных до 40000 строк, чтобы показать значительную разницу во времени:

library(matrixStats)

#sample data
dt <- data.table(a = 1:40000, b = rep(c(5, 7, 9, 11),10000), c = rep(c(13, 16, 19, 22),10000), d = rep(c(25, 29, 33, 37),10000))
df <- data.frame(a = 1:40000, b = rep(c(5, 7, 9, 11),10000), c = rep(c(13, 16, 19, 22),10000), d = rep(c(25, 29, 33, 37),10000))

t0 = Sys.time()
dt[, `:=`(abdSD = rowSds(as.matrix(.SD), na.rm=T)), .SDcols=c('a','b','d')]
print(paste("Time taken for data table operation = ",Sys.time() - t0))
# [1] "Time taken for data table operation =  0.117115020751953"


t0 = Sys.time()
df$abdSD <- apply(df[,c("a","b","d")],1, function(x){sd(x)})
print(paste("Time taken for apply opertaion = ",Sys.time() - t0))
# [1] "Time taken for apply opertaion =  2.93488311767578"

Использование DT и matrixStats явно выигрывает гонку

Как упоминал @Frank, я мог бы избежать добавления столбца, выполнив by=1:nrow(DT)

DT[, abdSD:=sd(c(a,b,d)),by=1:nrow(DT)]

выход:

   a  b  c  d    abdSD
1: 1  5 13 25 12.85820
2: 2  7 16 29 14.36431
3: 3  9 19 33 15.87451
4: 4 11 22 37 17.38774

если вы добавите столбец row_name, это будет очень просто

DT$row_id<-row.names(DT)

Просто по = row_id, вы получите желаемый результат

DT[, abdSD:=sd(c(a,b,d)),by=row_id]

Результат будет:

   a  b  c  d row_id    abdSD
1: 1  5 13 25      1 12.85820
2: 2  7 16 29      2 14.36431
3: 3  9 19 33      3 15.87451
4: 4 11 22 37      4 17.38774

Если вы хотите удалить row_id, просто добавьте [,row_id:=NULL]

DT[, abdSD:=sd(c(a,b,d)),by=row_id][,row_id:=NULL]

Эта линия получит все, что вы хотите

   a  b  c  d    abdSD
1: 1  5 13 25 12.85820
2: 2  7 16 29 14.36431
3: 3  9 19 33 15.87451
4: 4 11 22 37 17.38774

Вы просто должны делать это по очереди.

data.frame по умолчанию делает это по строкам, data.table по умолчанию, я думаю. Это немного сложно

Надеюсь это поможет

К вашему сведению, вы также можете выполнить by=1:nrow(DT), не создавая новую колонку.

Frank 04.04.2018 14:39

Спасибо, Фрэнк! Мне было интересно, как это сделать. Я сделал by=nrow(DT), и он не работал. Оказывается, 1:nrow(DT)

SunLisa 04.04.2018 17:46

В зависимости от размера ваших данных вы можете захотеть преобразовать данные в длинный формат, а затем рассчитать результат следующим образом:

complexFunc <- function(x) sd(x)

cols <- c("a", "b", "d")
rowres <- melt(DT[, rn:=.I], id.vars = "rn", variable.factor=FALSE)[, 
    list(abdRes=complexFunc(value[variable %chin% cols])), by=.(rn)]
DT[rowres, on=.(rn)]

или если ваша сложная функция имеет 3 аргумента, вы можете сделать что-то вроде

DT[, abdSD := mapply(complexFunc, a, b, d)]

В этом случае векторизовать sd несложно:

vecSD = function(x) {
  n = ncol(x)
  sqrt((n/(n-1)) * (Reduce(`+`, x*x)/n - (Reduce(`+`, x)/n)^2))
}

DT[, vecSD(.SD), .SDcols = c('a', 'b', 'd')]
#[1] 12.85820 14.36431 15.87451 17.38774

Другие вопросы по теме