Tabelas de proporções mais rápidas em R
-
11-12-2019 - |
Pergunta
Estou criando tabelas de proporções baseadas em um objeto xts.Como isso faz parte de um programa grande que (infelizmente) requer cerca de 10 ^ 6 loops, cria um grande gargalo e eu gostaria de acelerá-lo.
Este é um exemplo do que comecei:
library(quantmod)
test.xts <- xts(sample(seq(1,5, by=.5), 50, replace=T), as.Date(1:50))
system.time(for(i in 1:10000){
prop.table(table(test.xts))
})
>user system elapsed
19.86 0.00 18.58
Já mudei o xts para uma matriz e isso resultou em um aumento significativo de velocidade.Menciono apenas que é originalmente um xts caso esteja faltando alguma coisa no xts que aceleraria isso além dos ganhos que já vi ao convertê-lo para uma matriz.
test.mat <- as.matrix(test.xts)
system.time(for(i in 1:10000){
prop.table(table(test.mat))
})
>user system elapsed
2.78 0.00 2.90
Mas eu realmente gostaria que fosse o mais rápido possível, então espero que outras pessoas tenham sugestões para melhorias futuras.Espero que haja uma abordagem óbvia que estou ignorando.
Uma informação adicional é que a saída dessas tabelas é finalmente mesclada com uma saída semelhante de um período de tempo diferente, portanto, as dimensões precisam permanecer nomeadas.(Ou seja, preciso ser capaz de combinar a proporção de um valor '10' no tempo 1 com a proporção de '10' no tempo 2).
Qualquer ajuda é muito apreciada.
Solução
table()
cria implicitamente um fator que é caro.No seu caso você pode economizar muito (mais de 10x) usando tabulate()
já que você já tem números inteiros:
a <- tabulate(test.mat)
names(a) <- seq_along(a)
a / sum(a)
1 2 3 4 5 6 7 8 9 10
0.16 0.14 0.08 0.14 0.08 0.16 0.02 0.06 0.10 0.06
horários:
system.time(for(i in 1:10000){
a <- tabulate(test.mat)
names(a) <- seq_along(a)
a/sum(a)
})
user system elapsed
0.208 0.002 0.210
seu tempo para comparação:
system.time(for(i in 1:10000) prop.table(table(test.mat)))
user system elapsed
3.373 0.028 3.402
Outras dicas
PARA desenvolver o comentário de Joran, usando tabulate()
diretamente pode ser mais rápido.Ele tem três peculiaridades dignas de nota:
- Ele lida apenas com números inteiros e trunca decimais.
- Ele ignora silenciosamente todos os valores negativos e zeros.
- Ele cria um compartimento para todos os valores 1:n, mesmo se houver contagens zero
Ver ?tabulate
para detalhes.
Com essa ressalva, aqui está uma função que proporciona uma aceleração de aproximadamente 9x:
prop2 <- function(x){
x <- tabulate(x)
out <- x/sum(x)
names(out) <- seq_along(out)
return(out)
}
Teste de velocidade:
library(rbenchmark)
test.mat <- as.matrix(test.xts)
f1 <- function() prop.table(table(test.mat))
benchmark(f1(), prop2(test.mat),
replications = 1000,
columns = c("test", "relative", "elapsed"),
order = "relative")
#------
test relative elapsed
2 prop2(test.mat) 1.0 0.10
1 f1() 9.1 0.91
Confirme que a saída é a mesma:
> prop.table(table(test.mat))
test.mat
1 2 3 4 5 6 7 8 9 10
0.04 0.02 0.20 0.12 0.08 0.10 0.06 0.14 0.12 0.12
> prop2(test.mat)
1 2 3 4 5 6 7 8 9 10
0.04 0.02 0.20 0.12 0.08 0.10 0.06 0.14 0.12 0.12