ipeaGIT / enderecopadrao

Pacote de R para padronizar endereços brasileiros
https://ipeagit.github.io/enderecopadrao/
Other
0 stars 0 forks source link

Otimizar código deduplicando o vetor antes de aplicar as REGEX #3

Closed lucasmation closed 6 months ago

lucasmation commented 6 months ago

@rafapereirabr, @dhersz , ta bacana, mas um pouco lento, seria bom melhorar a performance

Sugiro: (1) primeiro agregar/deduplicar o vetor. (2) aplicar o filtro (REGEX) na base deduplicada. (3) fazer um merge para recompor as observações do vetor original. Isso deve acelerar bem.

Motivaçao: na base do CPF tem 193 observações com logradouro não nulo. Mas apenas 19.8 milhões de logradouros únicos (pre harmonização). Este problema padrão deve ser comum para várias bases de endereços.

Segue um proto-código para fazer isso:

padronizar_logradouro_inner  <- function(vetor){
    #colocar aqui o conteudo atual da funcao padronizar_logradouro
}

padronizar_logradouro  <- function(vetor){
    dt <- data.table(vetor=vetor)]
    dtU <- dt[ ,.N,vetor]
    dtU[,vetor_limpo:= padronizar_logradouro_inner(vetor)]

   #merge and return the unduplicated and cleaned vector
   dtU[dt,on=vetor]$vetor_limpo %>% return
}

talvez tenha uma forma mais eficiente de fazer esta funcao externa, sem precisar do DT. Mas com DT deve funcionar (nao testei, servidor ta travado rodando a funcao original)

EDIT: so para ilustar os ganhos:

tic()
> end[,logradouro_padrao:=padronizar_logradouros(logradouro)]
> toc()
# 6507.54 (108min),

tic()
logr2 <- end[,.(N=sum(N)),logradouro][order(-N),.(logradouro,logradouro_padrao=padronizar_logradouros(logradouro),N)]
toc()
# 901.93 (15min) sec elapsed
dhersz commented 6 months ago

Boa! Vou incorporar isso nas funções. Eu já fazia essa deduplicação numa etapa de pré-processamento das bases que usei como teste, não sei por que não incluí nas funções direto hehe

dhersz commented 6 months ago

Ok, provavelmente não fiz isso porque nenhuma base que tava usando como teste tem tantas observações duplicadas assim.

Testei aqui com a RAIS: das 3~ milhões de observações, tirei uma amostra de 200k. Desses 200k, 188k são únicos. Os ganhos ficam muito baixos (padronizar_logradouros2() é a otimizada):

bench::mark(padronizar_logradouros(hehe), padronizar_logradouros2(hehe), iterations = 5)
#> # A tibble: 2 × 13
#>   expression                         min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory     time           gc      
#>   <bch:expr>                    <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list>     <list>         <list>  
#> 1 padronizar_logradouros(hehe)     16.2s    16.4s    0.0609     9.2MB        0     5     0      1.37m <chr>  <Rprofmem> <bench_tm [5]> <tibble>
#> 2 padronizar_logradouros2(hehe)    15.9s    16.1s    0.0621    21.7MB        0     5     0      1.34m <chr>  <Rprofmem> <bench_tm [5]> <tibble>

Testando com uma amostra de 1 milhão de observações, 867k únicas:

bench::mark(padronizar_logradouros(hehe), padronizar_logradouros2(hehe), iterations = 5)
#> # A tibble: 2 × 13
#>   expression                         min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory     time           gc      
#>   <bch:expr>                    <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list>     <list>         <list>  
#> 1 padronizar_logradouros(hehe)     1.25m    1.31m    0.0128    45.8MB  0.00319     4     1      5.22m <chr>  <Rprofmem> <bench_tm [5]> <tibble>
#> 2 padronizar_logradouros2(hehe)    1.11m    1.12m    0.0148   100.5MB  0           5     0      5.63m <chr>  <Rprofmem> <bench_tm [5]> <tibble>

Como esperado, quanto maior o número de duplicatas, maior a economia de tempo. Testei com a função padronizar_municipios() também, que recebe muito mais valores duplicados, e o tempo de processamento pra 3~ milhões de registros foi de 10s pra 0.3s, 30~ vezes mais rápido.

Edit: exemplo com a padronizar_estados():

bench::mark(padronizar_estados(rais$uf), padronizar_estados2(rais$uf), iterations = 5)
#> # A tibble: 2 × 13
#>   expression                        min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory     time           gc      
#>   <bch:expr>                   <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list>     <list>         <list>  
#> 1 padronizar_estados(rais$uf)     7.68s    9.14s     0.114     829MB    0.228     5    10     43.95s <chr>  <Rprofmem> <bench_tm [5]> <tibble>
#> 2 padronizar_estados2(rais$uf) 243.48ms 245.51ms     2.06      173MB    0.826     5     2      2.42s <chr>  <Rprofmem> <bench_tm [5]> <tibble>
dhersz commented 6 months ago

Feito https://github.com/ipeaGIT/enderecopadrao/commit/a63cd823c2d9558000636030a04b5dcb173de83b.