Быстро применить & операцию к парам столбцов в R - PullRequest
1 голос
/ 06 мая 2020

Допустим, у меня есть два больших data.tables, и мне нужно попарно объединить их столбцы с помощью операции &. Комбинации продиктованы grid (объединить dt1 column1 с dt2 column2, et c.)

Прямо сейчас я использую mclapply l oop, и скрипт занимает часы, когда я запускаю полный набор данных. Я попытался преобразовать данные в матрицу и использовать векторизованный подход, но это заняло еще больше времени. Есть ли более быстрый и / или более элегантный способ сделать это?

mx1 <- replicate(10, sample(c(T,F), size = 1e6, replace = T)) # 1e6 rows x 10 columns
mx1 <- as.data.table(mx1)
colnames(mx1) <- LETTERS[1:10]

mx2 <- replicate(10, sample(c(T,F), size = 1e6, replace = T)) # 1e6 rows x 10 columns
mx2 <- as.data.table(mx2)
colnames(mx2) <- letters[1:10]

grid <- expand.grid(col1 = colnames(mx1), col2 = colnames(mx2)) # the combinations I want to evaluate

out <- new_layer <- mapply(grid$col1, grid$col2, FUN = function(col1, col2) { # <--- mclapply loop
    mx1[[col1]] & mx2[[col2]]
  }, SIMPLIFY = F)

setDT(out) # convert output into data table
colnames(out) <- paste(grid$col1, grid$col2, sep = "_")

Для контекста, эти данные взяты из матрицы экспрессии генов, где 1 строка = 1 ячейка

Ответы [ 2 ]

0 голосов
/ 10 мая 2020

Покопавшись, я нашел пакет под названием bit, специально разработанный для быстрых логических операций. Преобразование каждого столбца моего data.table из logical в bit дало мне 100-кратное увеличение скорости вычислений.

# Load libraries.
library(data.table)
library(bit)

# Create data set.
mx1 <- replicate(10, sample(c(T,F), size = 5e6, replace = T)) # 5e6 rows x 10 columns
colnames(mx1) <- LETTERS[1:10]

mx2 <- replicate(10, sample(c(T,F), size = 5e6, replace = T)) # 5e6 rows x 10 columns
colnames(mx2) <- letters[1:10]

grid <- expand.grid(col1 = colnames(mx1), col2 = colnames(mx2)) # combinations I want to evaluate

# Single operation with logical matrix.
system.time({
  out <- mx1[, grid$col1] & mx2[, grid$col2]
}) # 26.014s

# Loop with logical matrix.
system.time({
  out <- mapply(grid$col1, grid$col2, FUN = function(col1, col2) {
    mx1[, col1] & mx2[, col2]
  })
}) # 31.914s

# Single operation with logical data.table.
mx1.dt <- as.data.table(mx1)
mx2.dt <- as.data.table(mx2)
system.time({
  out <- mx1.dt[, grid$col1, with = F] & mx2.dt[, grid$col2, with = F] # 26.014s
}) # 32.349s

# Loop with logical data.table.
system.time({
  out <- mapply(grid$col1, grid$col2, FUN = function(col1, col2) {
    mx1.dt[[col1]] & mx2.dt[[col2]]
  })
}) # 15.031s <---- SECOND FASTEST TIME, ~2X IMPROVEMENT

# Loop with bit data.table.
mx1.bit <- mx1.dt[, lapply(.SD, as.bit)]
mx2.bit <- mx2.dt[, lapply(.SD, as.bit)]
system.time({
  out <- mapply(grid$col1, grid$col2, FUN = function(col1, col2) {
    mx1.bit[[col1]] & mx2.bit[[col2]]
  })
}) # 0.383s <---- FASTEST TIME, ~100X IMPROVEMENT

# Convert back to logical table.
out <- setDT(out)
colnames(out) <- paste(grid$col1, grid$col2, sep = "_")
out <- out[, lapply(.SD, as.logical)]

Существуют также специальные функции, такие как sum.bit и ri, которые вы можете использовать для агрегирования данных, не конвертируя их обратно в логические.

0 голосов
/ 07 мая 2020

Это можно сделать напрямую без mapply: просто убедитесь, что аргумент with равен FALSE ie:

 mx1[, grid$col1, with = FALSE] & mx2[, grid$col2, with=FALSE]
...