Используйте внешний, а не expand.grid
Я ищу как можно больше скорости и остаюсь в базе, чтобы делать то, что делает expand.grid
. Я использовал outer
для подобных целей в прошлом для создания вектора; что-то вроде этого:
v <- outer(letters, LETTERS, paste0)
unlist(v[lower.tri(v)])
Бенчмаркинг показал мне, что outer
может быть значительно быстрее, чем expand.grid
, но на этот раз я хочу создать два столбца точно так же, как expand.grid
(все возможные комбо для 2 векторов), но мои методы с outer
так же быстро, как и внешние.
Я надеюсь взять 2 вектора и создать все возможные комбо как два столбца как можно быстрее (я думаю, outer
может быть маршрутом, но широко открытым для любого базового метода.
Здесь метод expand.grid
и outer
.
dat <- cbind(mtcars, mtcars, mtcars)
expand.grid(seq_len(nrow(dat)), seq_len(ncol(dat)))
FOO <- function(x, y) paste(x, y, sep=":")
x <- outer(seq_len(nrow(dat)), seq_len(ncol(dat)), FOO)
apply(do.call("rbind", strsplit(x, ":")), 2, as.integer)
Микрообъектив показывает outer
медленнее:
# expr min lq median uq max
# EXPAND.G 812.743 838.6375 894.6245 927.7505 27029.54
# OUTER 5107.871 5198.3835 5329.4860 5605.2215 27559.08
Я думаю, что мое использование outer
медленное, потому что я не знаю, как использовать outer
, чтобы напрямую создать вектор длины 2, который я могу do.call('rbind'
вместе. Я должен замедлить paste
и медленное раскол. Как это сделать с помощью outer
(или других методов в base
) способом, который быстрее, чем expand grid
?
EDIT:
Добавление результатов микрообъектива.
**
Unit: microseconds
expr min lq median uq max
1 ERNEST 34.993 39.1920 52.255 57.854 29170.705
2 JOHN 13.997 16.3300 19.130 23.329 266.872
3 ORIGINAL 352.720 372.7815 392.377 418.738 36519.952
4 TOMMY 16.330 19.5960 23.795 27.061 6217.374
5 VINCENT 377.447 400.3090 418.505 451.864 43567.334
**
![enter image description here]()
Ответы
Ответ 1
Использование rep.int
:
expand.grid.alt <- function(seq1,seq2) {
cbind(rep.int(seq1, length(seq2)),
c(t(matrix(rep.int(seq2, length(seq1)), nrow=length(seq2)))))
}
expand.grid.alt(seq_len(nrow(dat)), seq_len(ncol(dat)))
В моем компьютере, как в 6 раз быстрее, чем expand.grid
.
Ответ 2
Документация для rep.int
не совсем завершена. Это не просто самый быстрый из наиболее распространенных случаев, потому что вы можете передавать векторы для аргумента times, как в случае с rep
. Вы можете использовать его прямо для обеих последовательностей, сокращая время на 40% или около того над Томми.
expand.grid.jc <- function(seq1,seq2) {
cbind(Var1 = rep.int(seq1, length(seq2)),
Var2 = rep.int(seq2, rep.int(length(seq1),length(seq2))))
}
Ответ 3
@ErnestA имеет отличное решение, достойное ответа на тик!
... он может быть немного быстрее, хотя:
expand.grid.alt2 <- function(seq1,seq2) {
cbind(Var1=rep.int(seq1, length(seq2)), Var2=rep(seq2, each=length(seq1)))
}
s1=seq_len(2000); s2=seq_len(2000)
system.time( for(i in 1:10) expand.grid.alt2(s1, s2) ) # 1.58
system.time( for(i in 1:10) expand.grid.alt(s1, s2) ) # 1.75
system.time( for(i in 1:10) expand.grid(s1, s2) ) # 2.46
Ответ 4
Вы можете создать два столбца отдельно.
library(microbenchmark)
n <- nrow(dat)
m <- ncol(dat)
f1 <- function() expand.grid(1:n, 1:m)
f2 <- function()
data.frame(
Var1 = as.vector(outer( 1:n, rep(1,m) )),
Var2 = as.vector(outer( rep(1,n), 1:m ))
)
microbenchmark( f1, f2, times=1e6 )
# Unit: nanoseconds
# expr min lq median uq max
# 1 f1 70 489 490 559 168458
# 2 f2 70 489 490 559 168597