Neste laboratório construiremos classificadores para predizer se um candidato à Câmara Federal dos Deputados será eleito ou não. Ou seja, a nossa tarefa é construir um classificador que diga se um candidato será eleito ou não de acordo com uma série de atributos do candidato.

Os dados foram coletados do TSE, sendo tratados e contemplam informações sobre aproximadamente 4.100 candidatos, os dados podem ser baixados neste link.


Lendo e Filtrando os Dados

observations <- read.csv("~/Documentos/AD2/Lab5/train.csv")
testSamples <- read.csv("~/Documentos/AD2/Lab5/test.csv")

Para esse modelo vamos eliminar variável que explicitamente não parecem ser importantes preditoras como setor_economico_receita e setor_economico_despesa.

formula <- as.formula(situacao_final ~ UF + partido + quantidade_doacoes + quantidade_doadores + total_receita + media_receita + recursos_de_outros_candidatos.comites + recursos_de_partidos + recursos_de_pessoas_físicas + recursos_de_pessoas_juridicas + recursos_proprios + quantidade_despesas + quantidade_fornecedores + idade + sexo + grau + estado_civil + descricao_ocupacao + descricao_cor_raca + despesa_max_campanha)

Há desbalanceamento das classes? Em que proporção? Quais efeitos colaterais o desbalanceamento de classes pode causar no classificador?

for (x in unique(observations$situacao_final)) {
  soma = sum(observations$situacao_final == x)
  print(paste(x, soma))
}
## [1] "nao_eleito 3719"
## [1] "eleito 416"

Sim, há um desbalanceamento das classes, existem 3719 instâncias de nao_eleito e apenas 416 de eleito, ou seja, uma proporção de aproximadamente 88.81% de nao_eleito e 11.19% eleito.

Como existe uma classe majoritária com 88.81% dos dados isso pode enviesar o classificador! Se no treinamento o modelo responder que todos os candidatos não serão eleitos, então, ele terá 88.81% de acurácia, porém, não será um modelo representativo! Esse problema é chamado de Accuracy Paradox.

Existem diversas formas de tentar contornar o Accuracy Paradox, as que iremos tentar nesse lab será:

  1. Mudar a métrica de avaliação de performance do modelo:
    • Accuracy
  2. Reamostragem do dataset:
    • Under-Sampling - Remoção de instâncias
    • Over-Sampling - Adição de instâncias
    • SMOTE - Synthetic Minority Over-Sampling Technique
    • ROSE - Random Over-Sampling Examples
set.seed(1234)

underData <- downSample(x = observations[, -ncol(observations)],
                        y = observations$situacao_final)
colnames(underData)[ncol(underData)] <- "situacao_final"
table(underData$situacao_final)
## 
##     eleito nao_eleito 
##        416        416
overData <- upSample(x = observations[, -ncol(observations)],
                     y = observations$situacao_final)
colnames(overData)[ncol(overData)] <- "situacao_final"
table(overData$situacao_final)
## 
##     eleito nao_eleito 
##       3719       3719
set.seed(10234)
smoteData <- SMOTE(situacao_final ~ ., data = observations)
table(smoteData$situacao_final)
## 
##     eleito nao_eleito 
##       1248       1664
roseData <- ROSE(situacao_final ~ ., data = observations)$data
table(roseData$situacao_final)
## 
## nao_eleito     eleito 
##       2061       2074

É possível observar que as técnicas de reamostragem utilizadas deixaram os dados mais balanceados. Posteriormente serão realizadas comparações de desempenhos dos modelos com os dados balanceados e desbalanceados.

Regressão Logística

Vamos construir modelos de Regressão Logística com os dados balanceados e desbalanceados.

Primeiro vamos construir um classificador treinado com os dados desbalanceados.

control <- trainControl(method = 'cv', number = 5)
logisticUmbalanced <- train(formula,
                            data = observations,
                            method = "glm",
                            family = "binomial",
                            trControl = control)
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
logisticUmbalanced
## Generalized Linear Model 
## 
## 4135 samples
##   20 predictors
##    2 classes: 'eleito', 'nao_eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 3308, 3307, 3309, 3308, 3308 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.9378485  0.6235603
importance <- varImp(logisticUmbalanced, scale=FALSE)
print(importance)
## glm variable importance
## 
##   only 20 most important variables shown (out of 240)
## 
##                                                                              Overall
## descricao_ocupacaoDEPUTADO                                                     4.448
## descricao_ocupacaoAGRICULTOR                                                   2.742
## descricao_ocupacaoINDUSTRIAL                                                   2.724
## despesa_max_campanha                                                           2.600
## sexoMASCULINO                                                                  2.552
## quantidade_doacoes                                                             2.291
## `descricao_ocupacaoCORRETOR DE IMÓVEIS, SEGUROS, TÍTULOS E VALORES`            2.080
## `descricao_ocupacaoPOLICIAL CIVIL`                                             1.984
## idade                                                                          1.974
## `descricao_ocupacaoLOCUTOR E COMENTARISTA DE RÁDIO E TELEVISÃO E RADIALISTA`   1.955
## UFGO                                                                           1.924
## UFSE                                                                           1.884
## UFAP                                                                           1.856
## UFRR                                                                           1.788
## `estado_civilVIÚVO(A)`                                                         1.785
## UFAM                                                                           1.778
## media_receita                                                                  1.748
## partidoPRB                                                                     1.711
## `descricao_ocupacaoVENDEDOR DE COMÉRCIO VAREJISTA E ATACADISTA`                1.710
## `grauSUPERIOR COMPLETO`                                                        1.685
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(logisticUmbalanced, testSamples)

write.csv(output, file = "output-logistic-umbalanced-acc.csv", row.names = FALSE)

O desempenho do modelo com os dados desbalanceados foi 95.45%, isso mostra que os dados de teste no kaggle também estão desbalanceados, então vamos construir os próximos modelos tão genéricos quanto o possível evitando o enviesamento dos modelos.

Vamos construir um modelo de regressão logística com Under-Sampling.

control <- trainControl(method = 'cv', number = 5)
logisticDown <- train(formula,
                            data = underData,
                            method = "glm",
                            family = "binomial",
                            trControl = control)
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
logisticDown
## Generalized Linear Model 
## 
## 832 samples
##  20 predictors
##   2 classes: 'eleito', 'nao_eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 665, 665, 666, 666, 666 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.8558401  0.7116643
importance <- varImp(logisticDown, scale=FALSE)
print(importance)
## glm variable importance
## 
##   only 20 most important variables shown (out of 166)
## 
##                                                                     Overall
## quantidade_doacoes                                                    4.670
## recursos_de_outros_candidatos.comites                                 3.972
## recursos_proprios                                                     3.872
## total_receita                                                         3.869
## recursos_de_pessoas_juridicas                                         3.858
## recursos_de_partidos                                                  3.857
## recursos_de_pessoas_físicas                                           3.851
## media_receita                                                         3.327
## UFMT                                                                  2.819
## descricao_ocupacaoDEPUTADO                                            2.640
## UFPI                                                                  2.278
## `descricao_ocupacaoCORRETOR DE IMÓVEIS, SEGUROS, TÍTULOS E VALORES`   2.119
## descricao_ocupacaoAGRICULTOR                                          2.076
## `descricao_ocupacaoPOLICIAL CIVIL`                                    2.028
## `grauSUPERIOR INCOMPLETO`                                             1.932
## partidoPTN                                                            1.645
## `grauSUPERIOR COMPLETO`                                               1.598
## descricao_ocupacaoECONOMISTA                                          1.570
## `descricao_ocupacaoJORNALISTA E REDATOR`                              1.548
## `descricao_ocupacaoSACERDOTE OU MEMBRO DE ORDEM OU SEITA RELIGIOSA`   1.523
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(logisticDown, testSamples)
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
write.csv(output, file = "output-logistic-down-acc.csv", row.names = FALSE)

O desempenho do modelo com Under-sampling foi de 88.93%.

Vamos construir um modelo de regressão logística com Over-Sampling.

control <- trainControl(method = 'cv', number = 5)
logisticUp <- train(formula,
                            data = overData,
                            method = "glm",
                            family = "binomial",
                            trControl = control)
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
logisticUp
## Generalized Linear Model 
## 
## 7438 samples
##   20 predictors
##    2 classes: 'eleito', 'nao_eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 5952, 5950, 5950, 5950, 5950 
## Resampling results:
## 
##   Accuracy  Kappa    
##   0.941787  0.8835739
importance <- varImp(logisticUp, scale=FALSE)
print(importance)
## glm variable importance
## 
##   only 20 most important variables shown (out of 240)
## 
##                                                                     Overall
## descricao_ocupacaoDEPUTADO                                            7.681
## descricao_ocupacaoAGRICULTOR                                          4.990
## UFAM                                                                  4.971
## UFPI                                                                  4.437
## sexoMASCULINO                                                         4.345
## partidoPTN                                                            4.277
## UFAP                                                                  4.237
## partidoPR                                                             4.237
## UFMS                                                                  4.155
## `grauSUPERIOR COMPLETO`                                               4.115
## UFRR                                                                  4.017
## `estado_civilVIÚVO(A)`                                                3.932
## `grauSUPERIOR INCOMPLETO`                                             3.928
## descricao_ocupacaoINDUSTRIAL                                          3.814
## quantidade_doacoes                                                    3.781
## UFMA                                                                  3.708
## partidoPSD                                                            3.640
## partidoPRB                                                            3.531
## `descricao_ocupacaoCORRETOR DE IMÓVEIS, SEGUROS, TÍTULOS E VALORES`   3.263
## despesa_max_campanha                                                  3.217
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(logisticUp, testSamples)

write.csv(output, file = "output-logistic-up-acc.csv", row.names = FALSE)

O desempenho do modelo com Over-sampling foi de 91.69%.

Vamos construir um modelo de regressão logística com SMOTE.

control <- trainControl(method = 'cv', number = 5)
logisticSmote <- train(formula,
                            data = smoteData,
                            method = "glm",
                            family = "binomial",
                            trControl = control)
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
logisticSmote
## Generalized Linear Model 
## 
## 2912 samples
##   20 predictors
##    2 classes: 'eleito', 'nao_eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 2330, 2329, 2329, 2331, 2329 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.9096773  0.8153227
importance <- varImp(logisticSmote, scale=FALSE)
print(importance)
## glm variable importance
## 
##   only 20 most important variables shown (out of 200)
## 
##                                                                              Overall
## quantidade_doacoes                                                             5.465
## descricao_ocupacaoDEPUTADO                                                     5.397
## media_receita                                                                  3.836
## partidoPTN                                                                     3.725
## `descricao_ocupacaoJORNALISTA E REDATOR`                                       3.380
## `descricao_ocupacaoLOCUTOR E COMENTARISTA DE RÁDIO E TELEVISÃO E RADIALISTA`   3.016
## descricao_ocupacaoAGRICULTOR                                                   2.855
## idade                                                                          2.855
## UFGO                                                                           2.660
## partidoPSC                                                                     2.507
## partidoPR                                                                      2.422
## UFAP                                                                           2.307
## descricao_ocupacaoVEREADOR                                                     2.263
## partidoPHS                                                                     2.257
## quantidade_fornecedores                                                        2.243
## `descricao_ocupacaoPROFESSOR DE ENSINO MÉDIO`                                  2.189
## `grauSUPERIOR COMPLETO`                                                        2.048
## `grauSUPERIOR INCOMPLETO`                                                      1.990
## UFRR                                                                           1.943
## partidoPTB                                                                     1.940
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(logisticSmote, testSamples)
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
write.csv(output, file = "output-logistic-smote-acc.csv", row.names = FALSE)

O desempenho do modelo com SMOTE foi de 89.72%.

Vamos construir um modelo de regressão logística com ROSE.

control <- trainControl(method = 'cv', number = 5)
logisticRose <- train(formula,
                            data = roseData,
                            method = "glm",
                            family = "binomial",
                            trControl = control)
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
logisticRose
## Generalized Linear Model 
## 
## 4135 samples
##   20 predictors
##    2 classes: 'nao_eleito', 'eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 3308, 3308, 3308, 3307, 3309 
## Resampling results:
## 
##   Accuracy  Kappa    
##   0.90689   0.8137829
importance <- varImp(logisticRose, scale=FALSE)
print(importance)
## glm variable importance
## 
##   only 20 most important variables shown (out of 213)
## 
##                                       Overall
## descricao_ocupacaoDEPUTADO              8.179
## total_receita                           8.037
## quantidade_doacoes                      6.443
## recursos_de_pessoas_físicas             5.476
## quantidade_doadores                     5.423
## recursos_de_outros_candidatos.comites   5.352
## sexoMASCULINO                           5.280
## media_receita                           5.178
## quantidade_despesas                     4.293
## UFAP                                    3.865
## descricao_ocupacaoENFERMEIRO            3.757
## partidoPSL                              3.729
## descricao_ocupacaoAGRICULTOR            3.720
## `partidoPC do B`                        3.647
## recursos_de_pessoas_juridicas           3.625
## UFPE                                    3.418
## descricao_ocupacaoINDUSTRIAL            3.410
## UFMA                                    3.352
## `grauSUPERIOR COMPLETO`                 3.275
## `grauSUPERIOR INCOMPLETO`               3.047
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(logisticRose, testSamples)
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
write.csv(output, file = "output-logistic-rose-acc.csv", row.names = FALSE)

O desempenho do modelo com ROSE foi de 88.33%.

Conhecidos os resultados de desempenho dos modelos de regressão logística apresentados, vamos escolher o que se saiu melhor no score do kaggle com os dados balanceados para realizar o tuning e entender melhor o modelo. Logo, a regressão logística com Over-Sampling vai ser eleita.

Árvore de decisão

Modelo com Under-Sampling e métrica de avaliação Accuracy.

trainC <- trainControl(method = 'cv', number = 5)
treeDownACC <- caret::train(formula,
                            data = underData, 
                            method = "rpart",
                            trControl = trainC,
                            metric = 'Accuracy')
treeDownACC
## CART 
## 
## 832 samples
##  20 predictors
##   2 classes: 'eleito', 'nao_eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 666, 666, 664, 666, 666 
## Resampling results across tuning parameters:
## 
##   cp           Accuracy   Kappa    
##   0.006009615  0.9038296  0.8076592
##   0.008413462  0.9074441  0.8148881
##   0.831730769  0.7339788  0.4679575
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.008413462.
importance <- varImp(treeDownACC, scale=FALSE)
print(importance)
## rpart variable importance
## 
##   only 20 most important variables shown (out of 240)
## 
##                                              Overall
## total_receita                                  288.9
## quantidade_despesas                            253.9
## quantidade_fornecedores                        228.1
## recursos_de_pessoas_juridicas                  225.1
## media_receita                                  216.2
## sexoMASCULINO                                    0.0
## UFAP                                             0.0
## `descricao_ocupacaoPRODUTOR AGROPECUÁRIO`        0.0
## `descricao_ocupacaoBOMBEIRO CIVIL`               0.0
## partidoPDT                                       0.0
## `descricao_ocupacaoFOTÓGRAFO E ASSEMELHADOS`     0.0
## descricao_ocupacaoPSICÓLOGO                      0.0
## `descricao_ocupacaoPOLICIAL MILITAR`             0.0
## `grauENSINO MÉDIO COMPLETO`                      0.0
## `grauSUPERIOR INCOMPLETO`                        0.0
## UFMT                                             0.0
## partidoPSL                                       0.0
## partidoPHS                                       0.0
## descricao_ocupacaoESTOFADOR                      0.0
## `descricao_ocupacaoTORNEIRO MECÂNICO`            0.0
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(treeDownACC, testSamples)

write.csv(output, file = "output-tree-down-acc.csv", row.names = FALSE)

Desempenho na competição - 88.93% de score

Modelo com Over-Sampling e métrica de avaliação Accuracy.

trainC <- trainControl(method = 'cv', number = 5)
treeUpACC <- caret::train(formula,
                            data = overData, 
                            method = "rpart",
                            trControl = trainC,
                            metric = 'Accuracy')
treeUpACC
## CART 
## 
## 7438 samples
##   20 predictors
##    2 classes: 'eleito', 'nao_eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 5950, 5950, 5950, 5951, 5951 
## Resampling results across tuning parameters:
## 
##   cp           Accuracy   Kappa    
##   0.003831675  0.9330477  0.8660969
##   0.004302232  0.9255187  0.8510387
##   0.842161871  0.8347336  0.6696030
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.003831675.
importance <- varImp(treeUpACC, scale=FALSE)
print(importance)
## rpart variable importance
## 
##   only 20 most important variables shown (out of 240)
## 
##                                                                                  Overall
## total_receita                                                                   2707.094
## quantidade_despesas                                                             2336.064
## quantidade_fornecedores                                                         2103.171
## media_receita                                                                   2029.231
## recursos_de_pessoas_juridicas                                                   2002.300
## recursos_de_pessoas_físicas                                                       51.782
## idade                                                                             39.192
## quantidade_doadores                                                               38.269
## recursos_proprios                                                                 32.098
## despesa_max_campanha                                                              19.068
## sexoMASCULINO                                                                      7.189
## descricao_ocupacaoCOMUNICÓLOGO                                                     0.000
## `descricao_ocupacaoMOTORISTA DE VEÍCULOS DE TRANSPORTE COLETIVO DE PASSAGEIROS`    0.000
## descricao_ocupacaoTERAPEUTA                                                        0.000
## `descricao_ocupacaoAGENTE DE VIAGEM`                                               0.000
## `descricao_ocupacaoPOLICIAL MILITAR`                                               0.000
## UFAL                                                                               0.000
## descricao_ocupacaoCONTADOR                                                         0.000
## `descricao_ocupacaoTRABALHADOR DE FABRICAÇÃO DE ROUPAS`                            0.000
## `descricao_ocupacaoTÉCNICO DE LABORATÓRIO E RAIOS X`                               0.000
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(treeUpACC, testSamples)

write.csv(output, file = "output-tree-up-acc.csv", row.names = FALSE)

Desempenho na competição - 87.15% de score

Modelo com SMOTE e métrica de avaliação Accuracy.

trainC <- trainControl(method = 'cv', number = 5)
treeSmoteACC <- caret::train(formula,
                            data = smoteData, 
                            method = "rpart",
                            trControl = trainC,
                            metric = 'Accuracy')
treeSmoteACC
## CART 
## 
## 2912 samples
##   20 predictors
##    2 classes: 'eleito', 'nao_eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 2330, 2330, 2329, 2329, 2330 
## Resampling results across tuning parameters:
## 
##   cp           Accuracy   Kappa    
##   0.003205128  0.9210176  0.8396486
##   0.004006410  0.9151798  0.8281372
##   0.814102564  0.7053326  0.3245773
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.003205128.
importance <- varImp(treeSmoteACC, scale=FALSE)
print(importance)
## rpart variable importance
## 
##   only 20 most important variables shown (out of 240)
## 
##                                              Overall
## total_receita                               1035.590
## quantidade_despesas                          898.270
## recursos_de_pessoas_juridicas                880.268
## quantidade_doadores                          818.640
## quantidade_fornecedores                      817.557
## recursos_de_partidos                          44.338
## UFSP                                          18.859
## idade                                         18.700
## recursos_de_pessoas_físicas                   17.732
## quantidade_doacoes                            14.684
## descricao_ocupacaoDEPUTADO                    11.874
## despesa_max_campanha                           8.295
## recursos_de_outros_candidatos.comites          7.674
## `descricao_ocupacaoDONA DE CASA`               0.000
## `descricao_ocupacaoAUXILIAR DE LABORATÓRIO`    0.000
## `descricao_ocupacaoMOTORISTA PARTICULAR`       0.000
## `estado_civilDIVORCIADO(A)`                    0.000
## `descricao_ocupacaoOPERADOR DE COMPUTADOR`     0.000
## descricao_ocupacaoFARMACÊUTICO                 0.000
## `descricao_ocupacaoTÉCNICO EM INFORMÁTICA`     0.000
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(treeSmoteACC, testSamples)

write.csv(output, file = "output-tree-smote-acc.csv", row.names = FALSE)

Desempenho na competição - 89.72% de score

Modelo com ROSE e métrica de avaliação Accuracy.

trainC <- trainControl(method = 'cv', number = 5)
treeRoseACC <- caret::train(formula,
                            data = roseData, 
                            method = "rpart",
                            trControl = trainC,
                            metric = 'Accuracy')
treeRoseACC
## CART 
## 
## 4135 samples
##   20 predictors
##    2 classes: 'nao_eleito', 'eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 3307, 3308, 3308, 3309, 3308 
## Resampling results across tuning parameters:
## 
##   cp          Accuracy   Kappa    
##   0.06695779  0.9105167  0.8210470
##   0.11353712  0.8626216  0.7253755
##   0.67879670  0.7646886  0.5288300
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.06695779.
importance <- varImp(treeRoseACC, scale=FALSE)
print(importance)
## rpart variable importance
## 
##   only 20 most important variables shown (out of 240)
## 
##                                                                                            Overall
## recursos_de_partidos                                                                        1084.0
## quantidade_despesas                                                                         1076.3
## recursos_de_pessoas_juridicas                                                               1074.5
## total_receita                                                                               1007.2
## quantidade_fornecedores                                                                      978.9
## recursos_de_pessoas_físicas                                                                  296.1
## `descricao_ocupacaoTÉCNICO DE MECÂNICA`                                                        0.0
## `descricao_ocupacaoPILOTO DE AVIAÇÃO COMERCIAL, NAVEGADOR, MECÂNICO DE VÔO E ASSEMELHADOS`     0.0
## `descricao_ocupacaoOPERADOR DE COMPUTADOR`                                                     0.0
## descricao_ocupacaoCOZINHEIRO                                                                   0.0
## UFPE                                                                                           0.0
## UFAL                                                                                           0.0
## `descricao_ocupacaoCORRETOR DE IMÓVEIS, SEGUROS, TÍTULOS E VALORES`                            0.0
## descricao_ocupacaoAGRÔNOMO                                                                     0.0
## partidoPSB                                                                                     0.0
## `descricao_ocupacaoMOTORISTA PARTICULAR`                                                       0.0
## descricao_ocupacaoCOMERCIANTE                                                                  0.0
## UFPI                                                                                           0.0
## `descricao_ocupacaoASSISTENTE SOCIAL`                                                          0.0
## descricao_ocupacaoVETERINÁRIO                                                                  0.0
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(treeRoseACC, testSamples)

write.csv(output, file = "output-tree-rose-acc.csv", row.names = FALSE)

Desempenho na competição - 92.68% de score

Conhecidos os resultados de desempenho dos modelos de árvore de decisão apresentados, vamos escolher o que se saiu melhor no score do kaggle com os dados balanceados para realizar o tuning e entender melhor o modelo. Logo, a regressão logística com a técnica de resample ROSE será a eleita.

AdaBoost

Modelo Adaboost com Over-Sampling no dataset, usando a métrica de avaliação Accuracy.

set.seed(100)
trainC <- trainControl(method = 'cv', number = 3)
adaBoostUpACC <- caret::train(formula,
                             data = overData,
                             method = 'adaboost',
                             metric = 'Accuracy',
                             trControl = trainC)
adaBoostUpACC
## AdaBoost Classification Trees 
## 
## 7438 samples
##   20 predictors
##    2 classes: 'eleito', 'nao_eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (3 fold) 
## Summary of sample sizes: 4958, 4959, 4959 
## Resampling results across tuning parameters:
## 
##   nIter  method         Accuracy   Kappa    
##    50    Adaboost.M1    0.9788927  0.9577856
##    50    Real adaboost  0.9770102  0.9540206
##   100    Adaboost.M1    0.9802371  0.9604742
##   100    Real adaboost  0.9768758  0.9537517
##   150    Adaboost.M1    0.9799682  0.9599365
##   150    Real adaboost  0.9776823  0.9553646
## 
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were nIter = 100 and method
##  = Adaboost.M1.
importance <- varImp(adaBoostUpACC, scale=FALSE)
print(importance)
## ROC curve variable importance
## 
##                                       Importance
## total_receita                             0.9670
## quantidade_despesas                       0.9486
## quantidade_fornecedores                   0.9384
## quantidade_doacoes                        0.9213
## media_receita                             0.9195
## quantidade_doadores                       0.9183
## recursos_de_pessoas_juridicas             0.9100
## recursos_de_pessoas_físicas               0.9006
## recursos_de_partidos                      0.8417
## recursos_proprios                         0.7513
## recursos_de_outros_candidatos.comites     0.7418
## descricao_ocupacao                        0.6542
## grau                                      0.6158
## despesa_max_campanha                      0.6085
## sexo                                      0.6012
## descricao_cor_raca                        0.6005
## estado_civil                              0.5829
## idade                                     0.5745
## UF                                        0.5488
## partido                                   0.5352
#plot(importance)

output <- subset(testSamples, select=c(ID))
output$prediction <- predict(adaBoostUpACC, testSamples)

write.csv(output, file = "output-adaboost-up-acc.csv", row.names = FALSE)

Esse modelo obteu um score de 94% nos 50% dos dados de teste da competição.

Tuning dos modelos elegidos

Primeiro realizaremos a avaliação dos modelos elegidos com as métricas de acurácia, precision, recall e f-measure.

Essas métricas são definidas em termos de Verdadeiros Positivos (TP), Verdadeiros Negativos (TN) Falsos Positivos (FP) e Falsos Negativos (FN).

Acurácia = (TP + TN) / (TP + TN + FP + FN) Nos diz a proporção de observações corretamente classificadas.

Precision = TP / (TP + FP) Diz respeito a quantas das observaçoes preditas como positivas são realmente positivas.

Recall = TP / (TP + FN) Diz respeito a quantas das observaçoes positivas foram corretamente classificadas.

F-measure = 2 * (Precision * Recall) / (Precision + Recall) O F1 score or F-measure é uma média harmônica das métricas precision e recall, quanto mais próximo de 1 o valor de F1 score melhor e quanto mais próximo de 0 pior.

Modelo de regressão logística com Over-Sampling.

control <- trainControl(method = 'cv', number = 5)
logisticUp <- train(formula,
                            data = overData,
                            method = "glm",
                            family = "binomial",
                            trControl = control)
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type =
## ifelse(type == : prediction from a rank-deficient fit may be misleading
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
c_observation <- observations
c_observation$predicao <- predict(logisticUp, c_observation)

TP <- c_observation %>% filter(situacao_final == "eleito", predicao == "eleito") %>% nrow()
TN <- c_observation %>% filter(situacao_final == "nao_eleito" , predicao == "nao_eleito" ) %>% nrow()
FP <- c_observation %>% filter(situacao_final == "nao_eleito" , predicao == "eleito") %>% nrow() 
FN <- c_observation %>% filter(situacao_final == "eleito", predicao == "nao_eleito" ) %>% nrow()

accuracy <- (TP + TN)/(TP + TN + FP + FN) 
precision <- TP / (TP + FP)
recall <- TP / (TP + FN)
f_measure <- 2*(precision * recall) / (precision + recall)

accuracy
## [1] 0.9376058
precision
## [1] 0.6242138
recall
## [1] 0.9543269
f_measure
## [1] 0.7547529

Podemos observar com as novas métricas de avaliação do modelo que a F1 score foi moderada 75%. Contudo, nesse modelo com regressão logística existem problemas com Falsos Negativos, já que a métrica precision foi 62%, essas informações são importantes para posteriores propostas de tuning do modelo.

Agora vamos observar a importância das variáveis no modelo de regressão logística.

importance <- varImp(logisticUp, scale=FALSE)
print(importance)
## glm variable importance
## 
##   only 20 most important variables shown (out of 240)
## 
##                                                                     Overall
## descricao_ocupacaoDEPUTADO                                            7.681
## descricao_ocupacaoAGRICULTOR                                          4.990
## UFAM                                                                  4.971
## UFPI                                                                  4.437
## sexoMASCULINO                                                         4.345
## partidoPTN                                                            4.277
## UFAP                                                                  4.237
## partidoPR                                                             4.237
## UFMS                                                                  4.155
## `grauSUPERIOR COMPLETO`                                               4.115
## UFRR                                                                  4.017
## `estado_civilVIÚVO(A)`                                                3.932
## `grauSUPERIOR INCOMPLETO`                                             3.928
## descricao_ocupacaoINDUSTRIAL                                          3.814
## quantidade_doacoes                                                    3.781
## UFMA                                                                  3.708
## partidoPSD                                                            3.640
## partidoPRB                                                            3.531
## `descricao_ocupacaoCORRETOR DE IMÓVEIS, SEGUROS, TÍTULOS E VALORES`   3.263
## despesa_max_campanha                                                  3.217

Podemos observar que as 5 variáveis mais importantes são:

  1. Se a decrição da ocupação do candidato é DEPUTADO, O fato de que um candidato já ter sido eleito também é um bom indicador, pois, direciona a ideia de que o candidato possui cabos eleitorais bem consagrados. já tinha sido reportada como um dos principais indicadores de uma alta quantidade de votos em campanhas eleitorais, pelo fato de que ela indica um grande investimento do candidato em propaganda eleitoral, que é convertida em votos.
  2. O fato do Brasil possuir uma forte atuação na área de agropecuária com grandes latifúndios, uma boa parte dos deputados eleitos fazem parte da bancada ruralista. Normalmente, esse grupo de proprietários de terra possuem bastante capital para investir em campanhas políticas e possuem grandes cabos eleitorais.
  3. Se o candidato é do estado do Amazonas, a importância dessa variável parecer ser uma mera coincidência, vamos observar o p-valor dessa variável.
  4. Se o candidato é do estado do Piauí, a importância dessa variável parecer ser uma mera coincidência, vamos observar o p-valor dessa variável.
  5. O fato do sexo Masculino ser uma variável importante reflete o cenário político brasileiro da pouca representatividade feminina na política, atualmente, apenas 10% dos deputados eleitos são do sexo Feminino.

Uma proposta de melhora nesse modelo seria a utilização da Penalized Logistic Regression.

Modelo de Árvore de Decisão com ROSE.

trainC <- trainControl(method = 'cv', number = 5)
treeRoseACC <- caret::train(formula,
                            data = roseData, 
                            method = "rpart",
                            trControl = trainC,
                            metric = 'Accuracy')

c_observation <- observations
c_observation$predicao <- predict(treeRoseACC, c_observation)

TP <- c_observation %>% filter(situacao_final == "eleito", predicao == "eleito") %>% nrow()
TN <- c_observation %>% filter(situacao_final == "nao_eleito" , predicao == "nao_eleito" ) %>% nrow()
FP <- c_observation %>% filter(situacao_final == "nao_eleito" , predicao == "eleito") %>% nrow() 
FN <- c_observation %>% filter(situacao_final == "eleito", predicao == "nao_eleito" ) %>% nrow()

accuracy <- (TP + TN)/(TP + TN + FP + FN) 
precision <- TP / (TP + FP)
recall <- TP / (TP + FN)
f_measure <- 2*(precision * recall) / (precision + recall)

accuracy
## [1] 0.9322854
precision
## [1] 0.636
recall
## [1] 0.7644231
f_measure
## [1] 0.6943231

Podemos observar com as novas métricas de avaliação do modelo que a F1 score foi moderada 69%. Contudo, nesse modelo com árvore de decisão existem problemas com Falsos Positivos, já que a métrica precision foi 63% e problemas com falsos negativos pelo fato da métrica precision ter sido 76%.

Agora vamos observar a importância das variáveis no modelo de árvore de decisão.

importance <- varImp(treeRoseACC, scale=FALSE)
print(importance)
## rpart variable importance
## 
##   only 20 most important variables shown (out of 240)
## 
##                                                                              Overall
## recursos_de_partidos                                                          1084.0
## quantidade_despesas                                                           1076.3
## recursos_de_pessoas_juridicas                                                 1074.5
## total_receita                                                                 1007.2
## quantidade_fornecedores                                                        978.9
## recursos_de_pessoas_físicas                                                    296.1
## `descricao_ocupacaoTÉCNICO DE ELETRICIDADE, ELETRÔNICA E TELECOMUNICAÇÕES`       0.0
## partidoPSC                                                                       0.0
## quantidade_doacoes                                                               0.0
## UFAL                                                                             0.0
## partidoPCB                                                                       0.0
## `descricao_ocupacaoTRABALHADOR DE CONSTRUÇÃO CIVIL`                              0.0
## descricao_ocupacaoPEDAGOGO                                                       0.0
## partidoPCO                                                                       0.0
## `descricao_ocupacaoATOR E DIRETOR DE ESPETÁCULOS PÚBLICOS`                       0.0
## `descricao_ocupacaoTÉCNICO DE ENFERMAGEM E ASSEMELHADOS (EXCETO ENFERMEIRO)`     0.0
## `descricao_ocupacaoSERVIDOR PÚBLICO FEDERAL`                                     0.0
## partidoPDT                                                                       0.0
## UFRO                                                                             0.0
## `descricao_ocupacaoMECÂNICO DE MANUTENÇÃO`                                       0.0

Podemos observar com esse modelo que existem apenas 5 variáveis consideradas importantes.

  1. Recurso de Partidos - ter recebido muitos recursos do partido dá o indicativo que o candidato tem importância no cenário político e/ou com muitos cabos eleitorais.
  2. Quantidade Despesas - quanto mais um candidato investe em campanha com propaganda, mais o investimento pode ser convertido em votos.
  3. Recursos de Pessoas Jurídicas - a representatividade dessa variável tem apoio real, pela interação entre as variáveis Total Receita e Quantidade Doadores.
  4. Total Receita - a representatividade dessa variável já foi discutida no modelo de regressão logística.
  5. Média receita - podemos afirmar de maneira razoável que existe uma correlação entre essa variável e Total Receita.
  6. Quantidade Fornecedores - a importância dessa variável parece ser uma mera coincidência.

Modelo Adaboost com Over-Sampling no dataset.

c_observation <- observations
c_observation$predicao <- predict(adaBoostUpACC, c_observation)

TP <- c_observation %>% filter(situacao_final == "eleito", predicao == "eleito") %>% nrow()
TN <- c_observation %>% filter(situacao_final == "nao_eleito" , predicao == "nao_eleito" ) %>% nrow()
FP <- c_observation %>% filter(situacao_final == "nao_eleito" , predicao == "eleito") %>% nrow() 
FN <- c_observation %>% filter(situacao_final == "eleito", predicao == "nao_eleito" ) %>% nrow()

accuracy <- (TP + TN)/(TP + TN + FP + FN) 
precision <- TP / (TP + FP)
recall <- TP / (TP + FN)
f_measure <- 2*(precision * recall) / (precision + recall)

accuracy
## [1] 1
precision
## [1] 1
recall
## [1] 1
f_measure
## [1] 1
importance <- varImp(adaBoostUpACC, scale=FALSE)
print(importance)
## ROC curve variable importance
## 
##                                       Importance
## total_receita                             0.9670
## quantidade_despesas                       0.9486
## quantidade_fornecedores                   0.9384
## quantidade_doacoes                        0.9213
## media_receita                             0.9195
## quantidade_doadores                       0.9183
## recursos_de_pessoas_juridicas             0.9100
## recursos_de_pessoas_físicas               0.9006
## recursos_de_partidos                      0.8417
## recursos_proprios                         0.7513
## recursos_de_outros_candidatos.comites     0.7418
## descricao_ocupacao                        0.6542
## grau                                      0.6158
## despesa_max_campanha                      0.6085
## sexo                                      0.6012
## descricao_cor_raca                        0.6005
## estado_civil                              0.5829
## idade                                     0.5745
## UF                                        0.5488
## partido                                   0.5352

Vamos testar um modelo com Random Forest e com a técnica de resample SMOTE.

trainC <- trainControl(method = 'cv', number = 5)
rfSmote <- caret::train(formula,
                        data = smoteData, 
                        method = "rf",
                        trControl = trainC)
rfSmote
## Random Forest 
## 
## 2912 samples
##   20 predictors
##    2 classes: 'eleito', 'nao_eleito' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 2331, 2329, 2329, 2329, 2330 
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy   Kappa    
##     2   0.8901055  0.7721615
##   121   0.9505437  0.8994855
##   240   0.9481417  0.8945399
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 121.
output <- subset(testSamples, select=c(ID))
output$prediction <- predict(rfSmote, testSamples)

write.csv(output, file = "output-rf-smote.csv", row.names = FALSE)

c_observation <- observations
c_observation$predicao <- predict(rfSmote, c_observation)

TP <- c_observation %>% filter(situacao_final == "eleito", predicao == "eleito") %>% nrow()
TN <- c_observation %>% filter(situacao_final == "nao_eleito" , predicao == "nao_eleito" ) %>% nrow()
FP <- c_observation %>% filter(situacao_final == "nao_eleito" , predicao == "eleito") %>% nrow() 
FN <- c_observation %>% filter(situacao_final == "eleito", predicao == "nao_eleito" ) %>% nrow()

accuracy <- (TP + TN)/(TP + TN + FP + FN) 
precision <- TP / (TP + FP)
recall <- TP / (TP + FN)
f_measure <- 2*(precision * recall) / (precision + recall)

accuracy
## [1] 0.947763
precision
## [1] 0.6582278
recall
## [1] 1
f_measure
## [1] 0.7938931

O modelo teve um desempenho de 89.13% nos testes públicos no kaggle. Além disso, podemos observar que o F1-score foi de 79.38%, que é relativamente alto, o recall foi 100% indicando que o modelo testado não atestou nenhum falso negativo o que é muito bom. Contudo, a métrica precision foi de 65.82%, indicando que o modelo possui problemas com falsos positivos.