Olá! Seja bem vindo mais uma vez ao dataset de nascimentos e casamentos do IBGE. A base de dados é composta de 4536 observações e 6 variáveis (estado, ano, faixa etária da mulher, quantidade de nascimentos de bebês do sexo masculino, quantidade de nascimentos de bebês do sexo feminino e quantidade de casamentos. A “key” de cada observação é um conjunto de estado, ano da observação e faixa etária da mulher que casou naquele período ou faixa etária da mãe que teve bebês naquele período. Vamos fazer uma breve revisão, antes de partir para o modelo preditivo.
library(tidyverse)
library(readxl)
casamentos_nascimentosbr<-read_xlsx("C:\\Users\\lferreira\\Downloads\\live university\\Projetos\\casamentos_nascimentosbr.xlsx")
glimpse(casamentos_nascimentosbr)## Observations: 4,536
## Variables: 6
## $ estado <chr> "Acre", "Acre", "Acre", "Acre", "Acre", "Acre",...
## $ ano <dbl> 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003,...
## $ classe <chr> "15 a 19 anos", "20 a 24 anos", "25 a 29 anos",...
## $ nasc_masc <dbl> 3049, 3692, 2143, 1211, 566, 257, 58, 16, 16, 1...
## $ nasc_fem <dbl> 2886, 3267, 2059, 1139, 554, 212, 50, 18, 18, 1...
## $ qtd_casamentos <dbl> 410, 685, 493, 340, 184, 129, 66, 52, 27, 19, 2...
summary(casamentos_nascimentosbr)## estado ano classe nasc_masc
## Length:4536 Min. :2003 Length:4536 Min. : 0.0
## Class :character 1st Qu.:2006 Class :character 1st Qu.: 34.0
## Mode :character Median :2010 Mode :character Median : 496.5
## Mean :2010 Mean : 4809.2
## 3rd Qu.:2013 3rd Qu.: 5316.0
## Max. :2016 Max. :92918.0
## nasc_fem qtd_casamentos
## Min. : 0 Min. : 0
## 1st Qu.: 35 1st Qu.: 140
## Median : 475 Median : 683
## Mean : 4572 Mean : 2976
## 3rd Qu.: 5054 3rd Qu.: 2924
## Max. :87869 Max. :76407
Lembrando que o período das observações vai entre 2003 a 2016. Essa é uma base livre de NA’s!
Vamos nessa análise elaborar um modelo preditivo que possa para cada estado prever o número de nascimentos, dependendo de variáveis, como faixa etária da mulher e quantidade de casamentos.
Vamos criar dois modelos de aprendizagem supervisionada a Regressão Múltipla e Random Forest e decidir qual modelo tem melhor capacidade para prever a quantidade de nascimentos, através de uma das mais famosas medidas de avaliação de desempenho, o SMAPE!(Symmetric Mean Absolute Percentage Error).
Para simplificar, vamos agrupar a quantidade de nascimentos de homens e mulheres em uma variável só!
casamentos_nascimentosbr_ml<-casamentos_nascimentosbr%>%
group_by(estado,classe,ano)%>%
summarise(nasc_masc=sum(nasc_masc),nasc_fem=sum(nasc_fem),qtd_casamentos=sum(qtd_casamentos))%>%
mutate(qtd_nascimentos=nasc_masc+nasc_fem)
#Arrumando as variáveis
casamentos_nascimentosbr_ml<-casamentos_nascimentosbr_ml[,c(1,2,3,6,7)]
casamentos_nascimentosbr_ml$estado<-as.factor(casamentos_nascimentosbr_ml$estado)
casamentos_nascimentosbr_ml$ano<-as.integer(casamentos_nascimentosbr_ml$ano)
#trocando os valores de classe para numero ordinal
casamentos_nascimentosbr_ml$classe<-factor(casamentos_nascimentosbr_ml$classe,levels=c(
"15 a 19 anos","20 a 24 anos","25 a 29 anos","30 a 34 anos",
"35 a 39 anos","40 a 44 anos","Menos de 15 anos","45 a 49 anos","50 anos ou mais"
))Não sei se repararam, mas fiz algo interessante, já que a faixa “menos de 15 anos” possui índice de correlação parecido com as faixas acima de 45 anos, ou seja, sem muito padrão. Para explicar a variável target (Visto em EDA IBGE), coloquei uma próxima da outra no argumento levels. Isso vai ser crucial para o nosso modelo.
casamentos_nascimentosbr_ml%>%
ggplot(aes(x=qtd_casamentos,y=qtd_nascimentos,col=estado))+
geom_point()+
geom_smooth(method ="lm",se=FALSE)+
labs(title = "Correlação entre casamentos e nascimentos",subtitle = "Observações em estado,ano e classe",
caption = "Fonte: IBGE - Estatísticas do Registro Civil (2003-2016)",x="Qtd de Casamentos",
y="Qtd de Nascimentos")+
scale_y_continuous(label=scales::comma)+
scale_x_continuous(label=scales::comma)+
theme_light()library(GGally)## Registered S3 method overwritten by 'GGally':
## method from
## +.gg ggplot2
##
## Attaching package: 'GGally'
## The following object is masked from 'package:dplyr':
##
## nasa
casamentos_nascimentosbr_ml%>%
ggpairs(3:5)Aparentemente sem multicolinearidade entre as variáveis.
Vamos agrupar o dataset em estado. Gosto muito quando vou elaborar vários modelos de uma vez só usar o purrr e o tidyr package! Principalmente por duas grandes e maravilhosas funções, a nest() e a map() para trabalhar com listas!
Vamos agrupar e dar uma olhada na correlação entre quantidade de nascimentos e casamentos para cada estado?
library(purrr)
casamentos_nascimentosbr_ml_nest<-casamentos_nascimentosbr_ml%>%
group_by(estado)%>%nest()
casamentos_nascimentosbr_ml_nest%>%
mutate(cor_pearson = map_dbl(.x=data,.f= ~cor(.$qtd_casamentos,.$qtd_nascimentos)))%>%
ggplot(aes(y=cor_pearson))+
geom_boxplot()+
labs(x="",y="Correlação de Pearson",title = "BoxPlot - Correlação de Pearson",
caption = "Fonte: IBGE - Estatísticas do Registro Civil (2003-2016)")+
theme_dark()+
theme(
axis.text.x = element_blank()
)Uau! Que poder! Correlação excelente de um modo geral, mas vemos um outlier em nosso boxplot. Qual estado será? Vamos descobrir.
casamentos_nascimentosbr_ml_nest%>%
mutate(cor_pearson = map_dbl(.x=data,.f= ~cor(.$qtd_casamentos,.$qtd_nascimentos)))%>%
filter(cor_pearson<.8)## # A tibble: 1 x 3
## estado data cor_pearson
## <fct> <list> <dbl>
## 1 Amapa <tibble [126 x 4]> 0.712
Ainda assim uma correlação de 0.71 é excelente! Bem, vamos dar início à modelagem.
library(rsample) #Criando a partição
set.seed(42)
casamentos_nascimentosbr_ml_split<-casamentos_nascimentosbr_ml_nest%>%
mutate(split = map(.x=data,.f=~initial_split(.x,prop=0.75))) #vamos fazer uma separação entre treinamento e teste com uma relação 75/25
casamentos_nascimentosbr_ml_split<-casamentos_nascimentosbr_ml_split%>%
mutate(train_CV = map(.x=split,.f=~training(.x)),test = map(.x=split,.f=~testing(.x)))
head(casamentos_nascimentosbr_ml_split)## # A tibble: 6 x 5
## estado data split train_CV test
## <fct> <list> <list> <list> <list>
## 1 Acre <tibble [126 x 4~ <split [95/31~ <tibble [95 x ~ <tibble [31 x ~
## 2 Alagoas <tibble [126 x 4~ <split [95/31~ <tibble [95 x ~ <tibble [31 x ~
## 3 Amapa <tibble [126 x 4~ <split [95/31~ <tibble [95 x ~ <tibble [31 x ~
## 4 Amazonas <tibble [126 x 4~ <split [95/31~ <tibble [95 x ~ <tibble [31 x ~
## 5 Bahia <tibble [126 x 4~ <split [95/31~ <tibble [95 x ~ <tibble [31 x ~
## 6 Ceara <tibble [126 x 4~ <split [95/31~ <tibble [95 x ~ <tibble [31 x ~
Beleza, agora na amostra de treinamento train_CV vamos fazer uma validação cruzada criando 5 partições e para cada partição vamos fazer a mesma divisão proporcional entre treinamento e teste.
casamentos_nascimentosbr_ml_split<-casamentos_nascimentosbr_ml_split%>%
mutate(CV_split = map(.x=train_CV,.f=~vfold_cv(.x,v=5)))
#vamos agora dar unnest e trabalhar com um dataset da validação cruzada a parte para facilitar
# a modelagem
casamentos_nascimentosbr_ml_CV<-casamentos_nascimentosbr_ml_split%>%
unnest(CV_split)
head(casamentos_nascimentosbr_ml_CV)## # A tibble: 6 x 3
## estado splits id
## <fct> <list> <chr>
## 1 Acre <split [76/19]> Fold1
## 2 Acre <split [76/19]> Fold2
## 3 Acre <split [76/19]> Fold3
## 4 Acre <split [76/19]> Fold4
## 5 Acre <split [76/19]> Fold5
## 6 Alagoas <split [76/19]> Fold1
#criando uma amostra de treino e uma de teste para cada partição da validação cruzada
casamentos_nascimentosbr_ml_CV<-casamentos_nascimentosbr_ml_CV%>%
mutate(
train = map(splits, ~training(.x)),
validate = map(splits, ~testing(.x))
)
head(casamentos_nascimentosbr_ml_CV)## # A tibble: 6 x 5
## estado splits id train validate
## <fct> <list> <chr> <list> <list>
## 1 Acre <split [76/19]> Fold1 <tibble [76 x 4]> <tibble [19 x 4]>
## 2 Acre <split [76/19]> Fold2 <tibble [76 x 4]> <tibble [19 x 4]>
## 3 Acre <split [76/19]> Fold3 <tibble [76 x 4]> <tibble [19 x 4]>
## 4 Acre <split [76/19]> Fold4 <tibble [76 x 4]> <tibble [19 x 4]>
## 5 Acre <split [76/19]> Fold5 <tibble [76 x 4]> <tibble [19 x 4]>
## 6 Alagoas <split [76/19]> Fold1 <tibble [76 x 4]> <tibble [19 x 4]>
Beleza, tudo certo. Partições criadas, amostras de treinamento e testes prontos. Agora, vamos para a criação do modelo de regressão!
casamentos_nascimentosbr_ml_CV<-casamentos_nascimentosbr_ml_CV%>%
mutate(model = map(.x=train,.f=~lm(qtd_nascimentos~.,data=.x)))
head(casamentos_nascimentosbr_ml_CV)## # A tibble: 6 x 6
## estado splits id train validate model
## <fct> <list> <chr> <list> <list> <list>
## 1 Acre <split [76/19]> Fold1 <tibble [76 x 4]> <tibble [19 x 4]> <S3: l~
## 2 Acre <split [76/19]> Fold2 <tibble [76 x 4]> <tibble [19 x 4]> <S3: l~
## 3 Acre <split [76/19]> Fold3 <tibble [76 x 4]> <tibble [19 x 4]> <S3: l~
## 4 Acre <split [76/19]> Fold4 <tibble [76 x 4]> <tibble [19 x 4]> <S3: l~
## 5 Acre <split [76/19]> Fold5 <tibble [76 x 4]> <tibble [19 x 4]> <S3: l~
## 6 Alagoas <split [76/19]> Fold1 <tibble [76 x 4]> <tibble [19 x 4]> <S3: l~
Vamos ver como ficou nosso R² ajustado, para cada modelo:
library(broom)
casamentos_nascimentosbr_ml_CV%>%
mutate(
rsquad = map_dbl(.x=model,.f=~glance(.x)[["adj.r.squared"]])
)%>%
ggplot(aes(y=rsquad))+
geom_boxplot()+
labs(x="",y="R² Ajustado",title = "BoxPlot - R² Ajustado distribuição por modelo",
caption = "Fonte: IBGE - Estatísticas do Registro Civil (2003-2016)")+
theme_dark()+
theme(
axis.text.x = element_blank()
)Assombroso! Parece que as variáveis do dataset estão conseguindo explicar de forma excelente a variação no fator quantidade de nascimentos. Mas será que o modelo vai ser capaz de prever bem nas amostras de teste da validação cruzada?
Mas antes disso, será que podemos melhorar ainda mais o nosso modelo? Gosto sempre quando trabalho com regressão, pois tento aperfeiçoar o modelo, minimizando o valor do critério de Akaike com a stepAIC() função do MASS package.
library(MASS)##
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
##
## select
casamentos_nascimentosbr_ml_CV<-casamentos_nascimentosbr_ml_CV%>%
mutate(model_otimo = map(.x=train,.f=~(lm(qtd_nascimentos~.,data=.x)%>%
stepAIC(trace=0))
))
casamentos_nascimentosbr_ml_CV%>%
mutate(
rsquad = map_dbl(.x=model_otimo,.f=~glance(.x)[["adj.r.squared"]])
)%>%
ggplot(aes(y=rsquad))+
geom_boxplot()+
labs(x="",y="R² Ajustado",title = "BoxPlot - R² Ajustado distribuição por modelo otimo",
caption = "Fonte: IBGE - Estatísticas do Registro Civil (2003-2016)")+
theme_dark()+
theme(
axis.text.x = element_blank()
)Como o modelo já tinha um nível muito bom, não tivemos uma diferença significativa entre eles.
Mas vamos seguir com ambos os modelos para validação com o SMAPE!
casamentos_nascimentosbr_ml_CV<-casamentos_nascimentosbr_ml_CV%>%
mutate(
validate_actual = map(validate, ~.x$qtd_nascimentos),
validate_predicted = map2(.x = model_otimo, .y = validate, ~predict(.x, .y))
)
head(casamentos_nascimentosbr_ml_CV)## # A tibble: 6 x 9
## estado splits id train validate model model_otimo validate_actual
## <fct> <list> <chr> <lis> <list> <lis> <list> <list>
## 1 Acre <spli~ Fold1 <tib~ <tibble~ <S3:~ <S3: lm> <dbl [19]>
## 2 Acre <spli~ Fold2 <tib~ <tibble~ <S3:~ <S3: lm> <dbl [19]>
## 3 Acre <spli~ Fold3 <tib~ <tibble~ <S3:~ <S3: lm> <dbl [19]>
## 4 Acre <spli~ Fold4 <tib~ <tibble~ <S3:~ <S3: lm> <dbl [19]>
## 5 Acre <spli~ Fold5 <tib~ <tibble~ <S3:~ <S3: lm> <dbl [19]>
## 6 Alago~ <spli~ Fold1 <tib~ <tibble~ <S3:~ <S3: lm> <dbl [19]>
## # ... with 1 more variable: validate_predicted <list>
library(Metrics)
casamentos_nascimentosbr_ml_CV<-casamentos_nascimentosbr_ml_CV%>%
mutate(validate_smape = map2_dbl(
.x=validate_actual,.y= validate_predicted,.f= ~smape(actual = .x, predicted = .y)))
casamentos_nascimentosbr_SMAPE<-casamentos_nascimentosbr_ml_CV%>%
group_by(estado)%>%
summarise(SMAPE = mean(validate_smape))
library(knitr)
kable(casamentos_nascimentosbr_SMAPE)| estado | SMAPE |
|---|---|
| Acre | 0.3887492 |
| Alagoas | 0.5712747 |
| Amapa | 0.2981771 |
| Amazonas | 0.2281959 |
| Bahia | 0.3931730 |
| Ceara | 0.3372935 |
| Distrito Federal | 0.5070146 |
| Espirito Santo | 0.4344515 |
| Goias | 0.4022064 |
| Maranhao | 0.5761041 |
| Mato Grosso | 0.5559720 |
| Mato Grosso do Sul | 0.2549416 |
| Minas Gerais | 0.4231878 |
| Para | 0.4897178 |
| Paraiba | 0.5985835 |
| Parana | 0.3032747 |
| Pernambuco | 0.5397053 |
| Piaui | 0.5579125 |
| Rio de Janeiro | 0.3641744 |
| Rio Grande do Norte | 0.5528111 |
| Rio Grande do Sul | 0.3324656 |
| Rondonia | 0.4961022 |
| Roraima | 0.3739956 |
| Santa Catarina | 0.4287751 |
| Sao Paulo | 0.3742262 |
| Sergipe | 0.4243968 |
| Tocantins | 0.6402341 |
Ops! Tivemos um overfitting, parece que na amostra de treinamento, os fatores conseguem explicar muito bem a variação do fator quantidade de nascimentos, mas para previsão fora da amostra o desempenho ficou muito abaixo do esperado!
Vamos tentar então simplificar o modelo, vamos usar somente as variaveis qtd_casamentos e classe, como variáveis explicativas dessa vez!
casamentos_nascimentosbr_ml_CV<-casamentos_nascimentosbr_ml_CV%>%
mutate(model2 = map(.x=train,.f=~lm(qtd_nascimentos~qtd_casamentos+classe,data=.x)))
casamentos_nascimentosbr_ml_CV<-casamentos_nascimentosbr_ml_CV%>%
mutate(
validate_predicted2 = map2(.x = model2, .y = validate, ~predict(.x, .y))
)
casamentos_nascimentosbr_ml_CV<-casamentos_nascimentosbr_ml_CV%>%
mutate(validate_smape2 = map2_dbl(
.x=validate_actual,.y= validate_predicted2,.f= ~smape(actual = .x, predicted = .y)))
casamentos_nascimentosbr_SMAPE2<-casamentos_nascimentosbr_ml_CV%>%
group_by(estado)%>%
summarise(SMAPE2 = mean(validate_smape2))
kable(casamentos_nascimentosbr_SMAPE2)| estado | SMAPE2 |
|---|---|
| Acre | 0.1595873 |
| Alagoas | 0.2918275 |
| Amapa | 0.1024682 |
| Amazonas | 0.2281959 |
| Bahia | 0.3982810 |
| Ceara | 0.2408246 |
| Distrito Federal | 0.3484189 |
| Espirito Santo | 0.4613920 |
| Goias | 0.3935993 |
| Maranhao | 0.2459672 |
| Mato Grosso | 0.4792189 |
| Mato Grosso do Sul | 0.2568969 |
| Minas Gerais | 0.3032223 |
| Para | 0.3121873 |
| Paraiba | 0.3066841 |
| Parana | 0.3465375 |
| Pernambuco | 0.1713558 |
| Piaui | 0.4822571 |
| Rio de Janeiro | 0.3196625 |
| Rio Grande do Norte | 0.3531862 |
| Rio Grande do Sul | 0.4519925 |
| Rondonia | 0.3262205 |
| Roraima | 0.2561849 |
| Santa Catarina | 0.4305370 |
| Sao Paulo | 0.3216776 |
| Sergipe | 0.3051049 |
| Tocantins | 0.3521932 |
analise_SMAPE<-full_join(casamentos_nascimentosbr_SMAPE,casamentos_nascimentosbr_SMAPE2,by="estado")
analise_SMAPE%>%gather(Modelo,Valor,2:3)%>%
ggplot(aes(x=estado,y=Valor,fill=Modelo))+
geom_col(position = "dodge")+
geom_hline(yintercept = .2,linetype = "dashed",col = "black",size=1)+
coord_flip()Parece que o modelo 2 mais simples, teve um efeito preditivo melhor.
Vamos finalizar fazendo calculo na amostra de teste que separamos no inicio da modelagem.
casamentos_nascimentosbr_ml_reg<-full_join(
casamentos_nascimentosbr_ml_split,
analise_SMAPE,
by="estado")
#facilitar a leitura
casamentos_nascimentosbr_ml_reg<-casamentos_nascimentosbr_ml_reg[,c(1,4,5,7,8)]
head(casamentos_nascimentosbr_ml_reg)## # A tibble: 6 x 5
## estado train_CV test SMAPE SMAPE2
## <fct> <list> <list> <dbl> <dbl>
## 1 Acre <tibble [95 x 4]> <tibble [31 x 4]> 0.389 0.160
## 2 Alagoas <tibble [95 x 4]> <tibble [31 x 4]> 0.571 0.292
## 3 Amapa <tibble [95 x 4]> <tibble [31 x 4]> 0.298 0.102
## 4 Amazonas <tibble [95 x 4]> <tibble [31 x 4]> 0.228 0.228
## 5 Bahia <tibble [95 x 4]> <tibble [31 x 4]> 0.393 0.398
## 6 Ceara <tibble [95 x 4]> <tibble [31 x 4]> 0.337 0.241
#criando o modelo melhor para cada estado
casamentos_nascimentosbr_ml_reg<-casamentos_nascimentosbr_ml_reg%>%
mutate(best_model = if_else(
SMAPE<SMAPE2,"MODEL1","MODEL2"
))
casamentos_nascimentosbr_ml_reg<-casamentos_nascimentosbr_ml_reg%>%
mutate(model =if_else(best_model=="MODEL1",
map(.x=train_CV,.f=~(lm(
qtd_nascimentos~.,data=.x)%>%
stepAIC(trace=0))),
map(.x=train_CV,
.f=~lm(qtd_nascimentos~qtd_casamentos+classe,data=.x))))
casamentos_nascimentosbr_ml_reg<-casamentos_nascimentosbr_ml_reg%>%
mutate(
validate_actual = map(test, ~.x$qtd_nascimentos),
validate_predicted = map2(.x = model, .y = test, ~predict(.x, .y))
)
casamentos_nascimentosbr_ml_reg<-casamentos_nascimentosbr_ml_reg%>%
mutate(SMAPE_final = map2_dbl(
.x=validate_actual,.y= validate_predicted,.f= ~smape(actual = .x, predicted = .y)))
ggplot(casamentos_nascimentosbr_ml_reg,aes(x=estado,y=SMAPE_final))+
geom_col()+
geom_hline(yintercept = .2,linetype="dashed",col="red")+
coord_flip() casamentos_nascimentosbr_ml_reg%>%
count(SMAPE_final<=.2)%>%
kable()| SMAPE_final <= 0.2 | n |
|---|---|
| FALSE | 23 |
| TRUE | 4 |
Bem, amenizamos um pouco a situação, mas ainda temos 23 modelos com um SMAPE maior do que 20%. Inaceitável!
Será que conseguimos algo melhor com o Random Forest?
Vamos manter a mesma partição da validação cruzada usada na regressão. No nosso modelo, para facilitar o processamento vamos deixar o número de árvores em 100. Acredito que já seja um valor razoável para criação de um bom modelo. Para o parâmetro mtry, vamos inserir todas as possibilidades de um a três fatores, usando a função crossing() assim vamos saber qual o parâmetro mtry terá o melhor desempenho.
library(ranger)
casamentos_nascimentosbr_ml_CV2<-casamentos_nascimentosbr_ml_CV[,1:5]
casamentos_nascimentosbr_ml_tune<-casamentos_nascimentosbr_ml_CV2%>%
crossing(mtry=1:3)
#random forest para cada combinação de estado, fold e mtry
casamentos_nascimentosbr_ml_tune<-casamentos_nascimentosbr_ml_tune%>%
mutate(
model = map2(.x=train,.y=mtry,.f= ~ranger(formula=qtd_nascimentos~.,data= .x,
num.trees=100,seed = 42,mtry = .y))
)
casamentos_nascimentosbr_ml_tune<-casamentos_nascimentosbr_ml_tune%>%
mutate(
validate_predicted = map2(.x = model, .y = validate, ~predict(.x, .y)$predictions))
casamentos_nascimentosbr_ml_tune<-casamentos_nascimentosbr_ml_tune%>%
mutate(
validate_actual = map(.x=validate,.f= ~.x$qtd_nascimentos))
casamentos_nascimentosbr_ml_tune<-casamentos_nascimentosbr_ml_tune%>%
mutate(validate_smape = map2_dbl(.x=validate_actual,
.y=validate_predicted, .f= ~smape(actual = .x, predicted = .y)))
casamentos_nascimentosbr_ml_rf<-casamentos_nascimentosbr_ml_tune%>%
group_by(estado,mtry)%>%
summarise(SMAPE = mean(validate_smape))
kable(casamentos_nascimentosbr_ml_rf)| estado | mtry | SMAPE |
|---|---|---|
| Acre | 1 | 0.3589207 |
| Acre | 2 | 0.1748306 |
| Acre | 3 | 0.1395432 |
| Alagoas | 1 | 0.3611932 |
| Alagoas | 2 | 0.1563820 |
| Alagoas | 3 | 0.1311870 |
| Amapa | 1 | 0.4470668 |
| Amapa | 2 | 0.1810180 |
| Amapa | 3 | 0.1005982 |
| Amazonas | 1 | 0.2980661 |
| Amazonas | 2 | 0.1835755 |
| Amazonas | 3 | 0.1430832 |
| Bahia | 1 | 0.3672615 |
| Bahia | 2 | 0.1383488 |
| Bahia | 3 | 0.0892367 |
| Ceara | 1 | 0.3687329 |
| Ceara | 2 | 0.1077914 |
| Ceara | 3 | 0.0720162 |
| Distrito Federal | 1 | 0.3744352 |
| Distrito Federal | 2 | 0.1673408 |
| Distrito Federal | 3 | 0.1406324 |
| Espirito Santo | 1 | 0.4180209 |
| Espirito Santo | 2 | 0.1943504 |
| Espirito Santo | 3 | 0.1405170 |
| Goias | 1 | 0.4384444 |
| Goias | 2 | 0.1527381 |
| Goias | 3 | 0.0843674 |
| Maranhao | 1 | 0.3243044 |
| Maranhao | 2 | 0.2044007 |
| Maranhao | 3 | 0.1757804 |
| Mato Grosso | 1 | 0.4739290 |
| Mato Grosso | 2 | 0.2018041 |
| Mato Grosso | 3 | 0.1653786 |
| Mato Grosso do Sul | 1 | 0.3662247 |
| Mato Grosso do Sul | 2 | 0.1573716 |
| Mato Grosso do Sul | 3 | 0.1266351 |
| Minas Gerais | 1 | 0.3915629 |
| Minas Gerais | 2 | 0.1470130 |
| Minas Gerais | 3 | 0.0876439 |
| Para | 1 | 0.3490172 |
| Para | 2 | 0.1415865 |
| Para | 3 | 0.0867624 |
| Paraiba | 1 | 0.3787655 |
| Paraiba | 2 | 0.1564813 |
| Paraiba | 3 | 0.1243608 |
| Parana | 1 | 0.3713845 |
| Parana | 2 | 0.1646090 |
| Parana | 3 | 0.1115468 |
| Pernambuco | 1 | 0.3895686 |
| Pernambuco | 2 | 0.1673348 |
| Pernambuco | 3 | 0.0910034 |
| Piaui | 1 | 0.3815494 |
| Piaui | 2 | 0.1489806 |
| Piaui | 3 | 0.1238502 |
| Rio de Janeiro | 1 | 0.4123803 |
| Rio de Janeiro | 2 | 0.1659959 |
| Rio de Janeiro | 3 | 0.0811324 |
| Rio Grande do Norte | 1 | 0.3659522 |
| Rio Grande do Norte | 2 | 0.1654939 |
| Rio Grande do Norte | 3 | 0.0978601 |
| Rio Grande do Sul | 1 | 0.4407174 |
| Rio Grande do Sul | 2 | 0.2342263 |
| Rio Grande do Sul | 3 | 0.0786478 |
| Rondonia | 1 | 0.4109574 |
| Rondonia | 2 | 0.2701019 |
| Rondonia | 3 | 0.2176122 |
| Roraima | 1 | 0.3912241 |
| Roraima | 2 | 0.2455535 |
| Roraima | 3 | 0.2264044 |
| Santa Catarina | 1 | 0.4084320 |
| Santa Catarina | 2 | 0.2382095 |
| Santa Catarina | 3 | 0.1536827 |
| Sao Paulo | 1 | 0.4013597 |
| Sao Paulo | 2 | 0.2035089 |
| Sao Paulo | 3 | 0.0833817 |
| Sergipe | 1 | 0.3623651 |
| Sergipe | 2 | 0.1579764 |
| Sergipe | 3 | 0.0964302 |
| Tocantins | 1 | 0.4216540 |
| Tocantins | 2 | 0.1806331 |
| Tocantins | 3 | 0.1585769 |
casamentos_nascimentosbr_ml_rf2<-casamentos_nascimentosbr_ml_tune%>%
group_by(mtry)%>%
summarise(SMAPE = mean(validate_smape))
kable(casamentos_nascimentosbr_ml_rf2)| mtry | SMAPE |
|---|---|
| 1 | 0.3879070 |
| 2 | 0.1780614 |
| 3 | 0.1232545 |
Hum, parece que o parâmetro mtry(3) foi o melhor entre os demais. Vamos continuar com a amostra de teste final.
casamentos_nascimentos_br_rf<-casamentos_nascimentosbr_ml_split[,c(1,4,5)]
casamentos_nascimentos_br_rf<-casamentos_nascimentos_br_rf%>%
mutate(
model = map(.x=train_CV,.f= ~ranger(formula=qtd_nascimentos~.,data= .x,
num.trees=100,seed = 42,mtry = 3))
)
casamentos_nascimentos_br_rf<-casamentos_nascimentos_br_rf%>%
mutate(
validate_predicted = map2(.x = model, .y = test, ~predict(.x, .y)$predictions))
casamentos_nascimentos_br_rf<-casamentos_nascimentos_br_rf%>%
mutate(
validate_actual = map(.x=test,.f= ~.x$qtd_nascimentos))
casamentos_nascimentos_br_rf<-casamentos_nascimentos_br_rf%>%
mutate(validate_smape = map2_dbl(.x=validate_actual,
.y=validate_predicted, .f= ~smape(actual = .x, predicted = .y)))
#comparando os MAPE de Regressão multipla e random forest
smape_ml_models<-full_join(casamentos_nascimentosbr_ml_reg[,c(1,10)],
casamentos_nascimentos_br_rf[,c(1,7)],by="estado")
colnames(smape_ml_models)<-c("estado","smape_regressao","smape_random_forest")
smape_ml_models2<-smape_ml_models%>%
gather(modelo,smape,2:3)
ggplot(smape_ml_models2,aes(x=estado,y=smape,fill=modelo))+
geom_col(position = "dodge")+
geom_hline(yintercept = .2,linetype="dashed",col="red",size=1)+
coord_flip() kable(smape_ml_models)| estado | smape_regressao | smape_random_forest |
|---|---|---|
| Acre | 0.1719814 | 0.1007378 |
| Alagoas | 0.2613108 | 0.1562917 |
| Amapa | 0.1384032 | 0.1333460 |
| Amazonas | 0.2802289 | 0.1339450 |
| Bahia | 0.7389492 | 0.1326222 |
| Ceara | 0.2744310 | 0.0823935 |
| Distrito Federal | 0.4457913 | 0.1407420 |
| Espirito Santo | 0.2810086 | 0.0641352 |
| Goias | 0.4321118 | 0.0691178 |
| Maranhao | 0.2156510 | 0.1449370 |
| Mato Grosso | 0.3324441 | 0.1373863 |
| Mato Grosso do Sul | 0.2798702 | 0.0994778 |
| Minas Gerais | 0.4489222 | 0.1416210 |
| Para | 0.3266229 | 0.0853473 |
| Paraiba | 0.2707645 | 0.1102267 |
| Parana | 0.3571254 | 0.1230340 |
| Pernambuco | 0.1320879 | 0.0910613 |
| Piaui | 0.4885375 | 0.1200653 |
| Rio de Janeiro | 0.4097590 | 0.0680517 |
| Rio Grande do Norte | 0.4838143 | 0.0891332 |
| Rio Grande do Sul | 0.3658414 | 0.1058016 |
| Rondonia | 0.4347397 | 0.1958627 |
| Roraima | 0.1405856 | 0.1474623 |
| Santa Catarina | 0.2438924 | 0.0857926 |
| Sao Paulo | 0.6021793 | 0.2003700 |
| Sergipe | 0.2844346 | 0.0601751 |
| Tocantins | 0.3809025 | 0.1497633 |
smape_ml_models%>%
count(smape_regressao<smape_random_forest)## # A tibble: 2 x 2
## `smape_regressao < smape_random_forest` n
## <lgl> <int>
## 1 FALSE 26
## 2 TRUE 1
smape_ml_models2%>%
filter(modelo == "smape_regressao")%>%
count(smape<=.2)## # A tibble: 2 x 2
## `smape <= 0.2` n
## <lgl> <int>
## 1 FALSE 23
## 2 TRUE 4
smape_ml_models2%>%
filter(modelo == "smape_random_forest")%>%
count(smape<=.2)## # A tibble: 2 x 2
## `smape <= 0.2` n
## <lgl> <int>
## 1 FALSE 1
## 2 TRUE 26
mean(smape_ml_models$smape_random_forest)## [1] 0.1173667
Excelente, exceto pelo estado de Roraima, o modelo de Random Forest foi superior! Mesmo assim a diferença do estado de Roraima foi muito pequena e podemos dizer que tivemos para todos os estados com o modelo de Random Forest um SMAPE menor do que 21% com média de 11,7%!
Como fizemos uma análise exploratória de dados no artigo anterior, descobrimos que para faixas etárias “menos de 15 anos” e para classes acima de 45 anos possuem comportamento muito parecido. Não temos tanto padrão só com número de casamentos, o que faz todo sentido, correto? Precisaríamos de mais variáveis para tentar explicar o número de nascimentos para essas classes. Poderíamos até agrupar essas classes em uma só, para nossa modelagem (ex: Faixas etárias fora do intervalo 15-44). Mas achei melhor deixá-las próximas no argumento levels(). Sem esse conhecimento de “ordem”, se colocássemos o argumento classe, somente com ordem de idade, obteríamos um modelo muito pior, com média do smape em torno de 50%! Por este motivo, sempre acho válido analisar bem o dataset antes para descobrir insights valiosos para modelagem!
Vamos agora importar o dataset de casamentos_nascimentosbr_2017 para incrementar nossa base de dados atual, e ver o quanto nosso modelo acerta para o ano de 2017!
casamentos_nascimentosbr_2017<-read_xlsx("C:\\Users\\lferreira\\Downloads\\live university\\casamentos_nascimentosbr_2017.xlsx")
casamentos_nascimentosbr_2017<- casamentos_nascimentosbr_2017%>%
mutate(
estado = as.factor(estado),
classe = factor(classe,levels = c( "15 a 19 anos","20 a 24 anos","25 a 29 anos","30 a 34 anos",
"35 a 39 anos","40 a 44 anos","Menos de 15 anos","45 a 49 anos","50 anos ou mais"
)),
ano = as.integer(ano)
)
casamentos_nascimentosbr_ml2<-bind_rows(casamentos_nascimentosbr_ml,casamentos_nascimentosbr_2017)
casamentos_nascimentosbr_ml2_nest<-casamentos_nascimentosbr_ml2%>%
group_by(estado)%>%nest()
casamentos_nascimentosbr_ml2_nest<-casamentos_nascimentosbr_ml2_nest%>%
mutate(train = map(.x=data,.f=~.x%>%
filter(ano<2017)),
validate = map(.x=data,.f=~.x%>%
filter(ano>=2017))
)
casamentos_nascimentosbr_ml2_nest<-casamentos_nascimentosbr_ml2_nest%>%
mutate( model = map(.x=train,.f=~ranger(formula=qtd_nascimentos~.,data= .x,
num.trees=100,seed = 42,mtry = 3)))
casamentos_nascimentosbr_ml2_nest<-casamentos_nascimentosbr_ml2_nest%>%
mutate( validate_predicted = map2(.x = model, .y = validate, ~predict(.x, .y)$predictions),
validate_actual = map(.x=validate,.f= ~.x$qtd_nascimentos))
casamentos_nascimentosbr_ml2_nest<-casamentos_nascimentosbr_ml2_nest%>%
mutate(validate_smape = map2_dbl(.x=validate_actual,
.y=validate_predicted, .f= ~smape(actual = .x, predicted = .y)))
casamentos_nascimentosbr_ml2_result<-casamentos_nascimentosbr_ml2_nest[,c(1,8)]
casamentos_nascimentosbr_ml2_result%>%
kable()| estado | validate_smape |
|---|---|
| Acre | 0.3075230 |
| Alagoas | 0.3329709 |
| Amapa | 0.3062975 |
| Amazonas | 0.3999786 |
| Bahia | 0.2596776 |
| Ceara | 0.3059167 |
| Distrito Federal | 0.2922559 |
| Espirito Santo | 0.3011266 |
| Goias | 0.3084185 |
| Maranhao | 0.3512116 |
| Mato Grosso | 0.3725225 |
| Mato Grosso do Sul | 0.3492735 |
| Minas Gerais | 0.2480678 |
| Para | 0.4205127 |
| Paraiba | 0.2303846 |
| Parana | 0.3050680 |
| Pernambuco | 0.3033716 |
| Piaui | 0.2682199 |
| Rio de Janeiro | 0.2337834 |
| Rio Grande do Norte | 0.3042838 |
| Rio Grande do Sul | 0.2649118 |
| Rondonia | 0.2856358 |
| Roraima | 0.3543977 |
| Santa Catarina | 0.2950892 |
| Sao Paulo | 0.2328368 |
| Sergipe | 0.3018810 |
| Tocantins | 0.3072274 |
analise_2017<-casamentos_nascimentosbr_ml2_nest[,c(1,6,7)]%>%
unnest(validate_actual,validate_predicted)%>%
group_by(estado)%>%
summarise(Real = sum(validate_actual),Previsto = sum(validate_predicted))%>%
gather(Tipo,Valor,2:3)
ggplot(analise_2017,aes(x=estado,y=Valor,fill=Tipo))+
geom_col(position = "dodge")+
labs(x="Estado",y="Qtd_Nascimentos",title = "Real x Previsto 2017",subtitle= "Usando o
modelo de Random Forest, mtry = 3, num.trees = 100",
caption = "Fonte: IBGE - Estatísticas do Registro Civil (2003-2016)")+
scale_y_continuous(label=scales::comma)+
coord_flip() casamentos_nascimentosbr_ml2_nest[,c(1,6,7)]%>%
unnest(validate_actual,validate_predicted)%>%
group_by(estado)%>%
summarise(Real = sum(validate_actual),Previsto = sum(validate_predicted))%>%
kable()| estado | Real | Previsto |
|---|---|---|
| Acre | 15485 | 17146.217 |
| Alagoas | 49761 | 49214.526 |
| Amapa | 14190 | 16230.002 |
| Amazonas | 71508 | 81577.706 |
| Bahia | 201243 | 207638.049 |
| Ceara | 125243 | 131107.304 |
| Distrito Federal | 44613 | 45322.897 |
| Espirito Santo | 55999 | 52887.074 |
| Goias | 93016 | 88091.148 |
| Maranhao | 107889 | 114149.652 |
| Mato Grosso | 52258 | 57247.472 |
| Mato Grosso do Sul | 44518 | 45418.918 |
| Minas Gerais | 260630 | 257913.451 |
| Para | 125666 | 147683.171 |
| Paraiba | 56024 | 55034.058 |
| Parana | 156208 | 154528.146 |
| Pernambuco | 129168 | 131462.686 |
| Piaui | 46815 | 44701.125 |
| Rio de Janeiro | 216315 | 214493.788 |
| Rio Grande do Norte | 45926 | 48791.979 |
| Rio Grande do Sul | 140185 | 141701.828 |
| Rondonia | 27543 | 26990.190 |
| Roraima | 9875 | 9611.608 |
| Santa Catarina | 98136 | 95526.083 |
| Sao Paulo | 611097 | 602944.658 |
| Sergipe | 32971 | 33450.128 |
| Tocantins | 24275 | 23472.081 |
Bem próximo da realidade não acha?
Chegamos ao fim! Não criamos o melhor modelo do mundo, mas foi bastante eficiente!
Acredito que possamos explorar mais esses dados na busca de variáveis que ajudam a explicar melhor a quantidade de nascimentos, principalmente para classes fora do arco 15-44 anos.
Mas foi bacana não é?
Obrigado por ter tido paciência para chegar até aqui! Foi uma longa jornada!
Abraços! Sucesso e até a próxima!