Спасибо @ mt1022, который помогает подчеркнуть, что реализация combn
в base
R очень медленная (она реализована в R).Таким образом, мы можем использовать подходы из этого Q & A о ускорении combn
, чтобы сделать этот подход более эффективным.Мне не удалось установить gRbase
на мою машину, поэтому я взял код из comb2.int
и включил его в свой подход:
dt[ , {
edge1 = rep(1:.N, (.N:1) - 1L)
i = 2L:(.N * (.N - 1L) / 2L + 1L)
o = cumsum(c(0, (.N-2L):1))
edge2 = i - o[edge1]
.(edge1 = edge1, edge2 = edge2)
}, by = group]
Это существенно ускоряет процесс при увеличении скоростиверсия набора данных OP:
max_g = 1e3
dt = data.table(
group = rep(LETTERS, sample(max_g, 26, TRUE))
)
dt[ , individual := as.character(.I)]
library(microbenchmark)
microbenchmark(
times = 10L,
combn = dt[ , transpose(combn(individual, 2, simplify = FALSE)), by = group],
cj = dt[ , CJ(edge1 = individual, edge2 = individual), by = group
][edge1 < edge2],
fast_combn = dt[ , {
edge1 = rep(1:.N, (.N:1) - 1L)
i = 2L:(.N * (.N - 1L) / 2L + 1L)
o = cumsum(c(0, (.N-2L):1))
edge2 = i - o[edge1]
.(edge1 = edge1, edge2 = edge2)
}, by = group]
)
# Unit: milliseconds
# expr min lq mean median uq max neval
# combn 3075.8078 3247.8300 3905.831 3482.9950 4289.8168 6180.1138 10
# cj 2495.1798 2549.1552 3830.492 4014.6591 4959.2004 5239.7905 10
# fast_combn 180.1348 217.9098 294.235 284.8854 329.5982 493.4744 10
То есть, хотя исходный подход combn
и предложенный с CJ
основаны на принципах «шея и шея» в зависимости от характеристик данных, этот подход далеко илучше работать с большими данными.
Оригинальный подход с помощью combn
Мы можем использовать combn
примерно так:
dt2 = dt[ , transpose(combn(individual, 2, simplify = FALSE)), by = group]
По умолчанию combn
вернет матрицу 2 x n
, где n = choose(.N, 2)
и .N
- размер каждой группы.
simplify = FALSE
вместо этого вернет длину - n
list
кортежей;transpose
преобразует это в длину - 2
list
из n
-кортежей (эффективно).
Затем исправьте имена:
setnames(dt2, c('V1', 'V2'), c('edge1', 'edge2'))