<jupyter_start><jupyter_text>Machine learning nas eleições de 2020<NAME>24 de outubro de 2021Esta análise também disponível no [GitHub](https://github.com/gabrielacaesar/master-insper-dados-automacao-data-storytelling). Etapa 1 (básica)Antes disso, considerando o modelo apresentado em aula, com regressão logística, responderemos às seguintes perguntas: 1.  O quão bom é o modelo? Ele consegue prever a eleição de um vereador?A meu ver, o modelo não é bom e não consegue prever a eleição de um vereador. Isso porque há problemas na qualidade dos dados usados e outras bases mais relevantes não foram utilizadas. Além disso, vale destacar também que a política é algo que muito volátil e ainda estamos passando por uma pandemia. A vida do brasileiro piorou muito com alto desemprego e alta inflação, o que pode gerar um sentimento de insatisfação de forma generalizada. Embora o vereador possivelmente não seja o principal responsável por isso, a falta de conhecimento da população sobre a atuação de cada ator político e de cada esfera pode afetar o resultado das urnas. Ainda é importante reforçar que as eleições trazem surpresas. Um vídeo compartilhado nas redes sociais com o candidato em um situação constrangedora pode por tudo a perder, ainda que ele apenas tenha sido vítima de uma deepfake.A coluna de 'ST_REELEICAO' é autodeclaratória e não é checada pelo TSE e, por isso, recomenda-se o cruzamento com os dados de 2016 para chegar a essa informação. Essa coluna passou a existir apenas a partir de 2018. A coluna 'VR_BEM_CANDIDATO' é autodeclaratória também e a maioria dos candidatos diz que não tem qualquer patrimônio. Eu já solicitei que o TSE melhore a comunicação da definição de patrimônio (que não é a mesma definição da Receita Federal, que tem critérios definidos e possibilita que boa parte não declare Imposto de Renda). Vale lembrar ainda que mesmo políticos que informam patrimônio milionário costumam omitir bens e não há qualquer ônus posterior. Apesar disso, essa coluna é muito interessante, tem sido utilizada em variados estudos e reportagens e já existe há muitas eleições. O ideal, porém, seria que essa coluna fosse validada com outra base de dados pública de órgãos como Receita Federal e Ministério da Economia. Ainda assim, eu considero que definitivamente essa coluna pode acrescentar algo relevante ao modelo. Veja algumas [sugestões de melhorias enviadas ao TSE no fim de 2020](https://www.gabrielacaesar.com/2020/12/02/2020-12-02-recomendacoes-de-melhoria-nos-dados-do-tse/). 2.  Os resultados encontrados poderiam ser extrapolados para tentarmos prever a eleição de um vereador na eleição de 2022?Não. O modelo precisa ser aprimorado. A próxima eleição de vereador será em 2024. Em 2022, ele pode concorrer a outros cargos. Acho complicado conseguir prever algo para 2022 sem saber o cargo específico e também sem saber uma série de informações da conjuntura política. Por exemplo, <NAME> foi eleito deputado federal para o primeiro mandato pelo MDB de Minas Gerais em 2014. Em 2018, ele foi eleito senador pelo DEM de Minas Gerais. Em 2021, ele foi eleito presidente do Senado e, agora, trocou o DEM pelo PSD. É pré-candidato a presidente. Não seria possível ter previsto em 2014 que Pacheco já estaria na presidência do Senado poucos anos depois. Também não é possível prever que Pacheco seja eleito presidente ou vice-presidente do Brasil já em 2022. Uma série de fatores influenciaram esses acontecimentos. 3. Como você melhoraria este modelo?Outros dados podem ser mais valiosos para criar um modelo que ajude com a predição das eleições de 2022, como dados das eleições anteriores (número de votos, prestação de contas etc), da receita da campanha, se ele ocupa algum cargo na estrutura partidária, se ele ocupou nos últimos anos algum cargo no Poder Executivo etc.  Etapa 2Este notebook tem como objetivo criar um modelo estatístico/matemático e verificar se o patrimônio ou a receita de campanha oferecem mais chances de vitória ao candidato a vereador em municípios de São Paulo com mais de 200 mil eleitores. Esta análise trabalha com arquivos referentes a SÃO PAULO, estado mais populoso e relevante do país.Apenas municípios com mais de 200 mil eleitores podem ter segundo turno e, por serem mais populosos, seus resultados podem ser comparados para avaliar o modelo (o enunciado sugeriu a partir de 100 mil, mas por causa dessa questão do turno eu prefiro adotar essa medida). A proposta do modelo também considera se o candidato foi eleito ou reeleito. A coluna 'ST_REELEICAO' é autodeclaratória e traz dados errados. Por isso, para saber essa informação, também será necessário importar os dados de 2016 e verificar quem foi eleito para, enfim, cruzar com a base de 2020 considerando o CPF do candidato.Os [28 municípios de São Paulo com mais de 200 mil eleitores](https://www.tre-sp.jus.br/imprensa/noticias-tre-sp/2020/Setembro/nas-eleicoes-2020-28-municipios-paulistas-podem-ter-segundo-turno):- São Paulo: 8.986.687- Guarulhos: 872.880- Campinas: 843.433- São Bernardo do Campo: 620.181- Santo André: 568.760- Osasco: 567.361- São José dos Campos: 540.501- Sorocaba: 485.962- Ribeirão Preto: 441.845- Santos: 341.867- São José do Rio Preto: 332.540- Diadema: 329.171- Mogi das Cruzes: 319.826- Jundiaí: 314.875- Mauá: 306.518- Carapicuíba: 291.138- Piracicaba: 290.998- Bauru: 270.749- Barueri: 261.428- São Vicente: 252.146- Itaquaquecetuba: 239.226- Franca: 238.124- Taubaté: 229.200- Limeira: 226.627- Praia Grande: 226.260- Guarujá: 224.819- Suzano: 217.959- Taboão da Serra: 217.479<jupyter_code># importação de bibliotecas
import pandas as pd # para análise de dados
import numpy as np # para machine learning
# definindo colunas dos CSVs para importação
# não vamos importar o CSV inteiro pois é pesado e desnecessário
cand_columns = ['DS_CARGO', 'NM_CANDIDATO', 'SG_PARTIDO', 'NM_UE', 'SQ_CANDIDATO','NR_CPF_CANDIDATO', 'DS_SIT_TOT_TURNO']
bens_columns = ['SQ_CANDIDATO', 'VR_BEM_CANDIDATO']
receita_columns = ['SQ_CANDIDATO', 'VR_RECEITA']
despesa_columns = ['SQ_CANDIDATO', 'VR_DESPESA_CONTRATADA']
# 2020
# importação de arquivos CSV originais do TSE
#### link do TSE: https://www.tse.jus.br/eleicoes/estatisticas/repositorio-de-dados-eleitorais-1
# foram usados os arquivos referentes a SÃO PAULO
# cand - candidatos > 2020 > Candidatos (formato ZIP) > consulta_cand_2020_SP.csv
# bens - candidatos > 2020 > Bens de candidatos (formato ZIP) > bem_candidato_2020_SP.csv
# receita - prestação de contas eleitorais > 2020 > Candidatos (formato zip) > receitas_candidatos_2020_SP.csv
# despesa - prestação de contas eleitorais > 2020 > Candidatos (formato zip) > despesas_contratadas_candidatos_2020_SP.csv
#### parâmetros
# usecols - colunas que desejamos importar; lista foi informada no chunk anterior
# encoding - codificação do arquivo; informado pelo TSE no dicionário de dados
# sep - separador dos dados em columas; neste caso, é ';'. em outros casos pode ser vírgula, tab etc
# dtype - lemos as colunas como string/character/texto para não perder o zero da frente ou de trás
cand = pd.read_csv('dados/consulta_cand_2020_SP.csv', usecols = cand_columns, encoding = 'Latin-1', sep = ';', dtype = str)
bens = pd.read_csv('dados/bem_candidato_2020_SP.csv', usecols = bens_columns, encoding = 'Latin-1', sep = ';', dtype = str)
receita = pd.read_csv('dados/receitas_candidatos_2020_SP.csv', usecols = receita_columns, encoding = 'Latin-1', sep = ';', dtype = str)
despesa = pd.read_csv('dados/despesas_contratadas_candidatos_2020_SP.csv', usecols = despesa_columns, encoding = 'Latin-1', sep = ';', dtype = str)
# queremos apenas os VEREADORES ELEITOS em 2020 
# por isso, já vamos aplicar um filtro abaixo
eleitos_2020 = cand.query('DS_CARGO == "VEREADOR" & DS_SIT_TOT_TURNO == ["ELEITO POR QP", "ELEITO POR MÉDIA"]')
cand.head(2) # primeiras linhas do dataframe
eleitos_2020.head(2)
bens.head(2)
receita.head(2)
despesa.head(2)
# 2016
# cand - candidatos > 2016 > Candidatos (formato ZIP) > consulta_cand_2016_SP.csv
# dados importados apenas para ter os ELEITOS
# e, ao cruzar com os de 2020, os que foram REELEITOS
# também vamos importar apenas as colunas de nosso interesse
cand_columns_2016 = ['DS_CARGO', 'DS_SIT_TOT_TURNO', 'NR_CPF_CANDIDATO', 'NM_UE']
cand_2016 = pd.read_csv('dados/consulta_cand_2016_SP.csv', usecols = cand_columns_2016, encoding = 'Latin-1', sep = ';', low_memory = False)
# nos dados de 2016
# filtando pelos eleitos
# e pelo cargo de vereador
eleitos_2016 = cand_2016.query('DS_CARGO == "VEREADOR" & DS_SIT_TOT_TURNO == ["ELEITO POR QP", "ELEITO POR MÉDIA"]')
# mantemos apenas colunas de interesse após o filtro acima
# a coluna 'ELEICAO_2016' foi criada 
# pois queremos que isso apareça após o cruzamento com os dados de 2020
# estamos colocando o número 1 nessa coluna pois 1 significa SUCESS0, ou seja, ELEITO
eleitos_2016['ELEICAO_2016'] = 1
eleitos_2016 = eleitos_2016[['NR_CPF_CANDIDATO', 'NM_UE', 'ELEICAO_2016']]
eleitos_2016.head(2)
# vamos trocar a vírgula pelo ponto
# e transformar as colunas de VALORES em número, e não texto
bens['VR_BEM_CANDIDATO'] = bens['VR_BEM_CANDIDATO'].str.replace(',', '.').astype(float)
receita['VR_RECEITA'] = receita['VR_RECEITA'].str.replace(',', '.').astype(float)
despesa['VR_DESPESA_CONTRATADA'] = despesa['VR_DESPESA_CONTRATADA'].str.replace(',', '.').astype(float)
# os dados oficiais mostra cada despesa/bem/receita por linha
# queremos saber o total de cada candidato
# por isso, vamos agrupar pela coluna SQ_CANDIDATO
grouped_bens = bens.groupby('SQ_CANDIDATO')['VR_BEM_CANDIDATO'].sum()
grouped_receita = receita.groupby('SQ_CANDIDATO')['VR_RECEITA'].sum()
grouped_despesa = despesa.groupby('SQ_CANDIDATO')['VR_DESPESA_CONTRATADA'].sum()
# cruzando os dados de 2020
# incluindo patrimonio, receita e despesa aos candidatos
# e preenchendo com zero quando houver NA/vazio
eleitos_2020_bens = eleitos_2020.merge(grouped_bens, how = 'left', on = 'SQ_CANDIDATO')
eleitos_2020_bens_receita = eleitos_2020_bens.merge(grouped_receita, how = 'left', on = 'SQ_CANDIDATO')
eleitos_2020_bens_receita_despesa = eleitos_2020_bens_receita.merge(grouped_despesa, how = 'left', on = 'SQ_CANDIDATO')
eleitos_2020_bens_receita_despesa.head(2)
# cruzando os dados de 2016
# incluindo os candidatos eleitos 
# para ver quem tenta a reeleição
# e preenchendo com zero quando houver NA/vazio
# na coluna 'ELEICAO_2016' o zero significa INSUCESSO, ou seja, não eleito em 2016 - derrota ou não participou
eleitos_2020_final = eleitos_2020_bens_receita_despesa.merge(eleitos_2016, how = 'left', on = ['NR_CPF_CANDIDATO', 'NM_UE'])
eleitos_2020_final = eleitos_2020_final.fillna(0)
eleitos_2020_final.head(2)
# queremos apenas os municípios com 2º turno
# faremos o filtro por texto e não por número para facilitar o entendimento do notebook
# as cidades não têm homônimas no estado
municipio_2turno = ["SÃO PAULO", "GUARULHOS", "CAMPINAS", "SÃO BERNARDO DO CAMPO", "<NAME>", 
                    "OSASCO", "SÃO JOSÉ DOS CAMPOS", "SOROCABA", "RIBEIRÃO PRETO", "SANTOS", 
                    "SÃO JOSÉ DO RIO PRETO", "DIADEMA", "MOGI DAS CRUZES", "JUNDIAÍ", "MAUÁ", 
                    "CARAPICUÍBA", "PIRACICABA", "BAURU", "BARUERI", "SÃO VICENTE", "ITAQUAQUECETUBA", 
                    "FRANCA", "TAUBATÉ", "LIMEIRA", "PR<NAME>", "GUARUJÁ", "SUZANO", "TABOÃO DA SERRA"]

# contando quantos são os municípios
len(municipio_2turno)
# filtro considerando a lista de municípios criada acima
eleitos_2020_2turno = eleitos_2020_final.query('NM_UE == @municipio_2turno')

# confirmando que todos os munícipios vieram
len(eleitos_2020_2turno['NM_UE'].unique())
# eliminando as colunas que não têm mais necessárias para esta análise
eleitos_2020_2turno = eleitos_2020_2turno.drop(columns = ['DS_CARGO', 'SQ_CANDIDATO', 'NR_CPF_CANDIDATO', 'DS_SIT_TOT_TURNO'])
eleitos_2020_2turno.head(2)<jupyter_output><empty_output><jupyter_text>Etapa 3**Agora vamos aplicar o código do professor para criar o nosso modelo com regressão logística. Reforço: o código abaixo não foi feito por mim.** Segundo o que eu pesquisei, a regressão logística pode medir o sucesso e o insucesso  de algo. Neste caso poderia ser usado com a coluna 'ELEICAO_2016', que mostra se o prefeito ou reeleito ou não em 2020. Quando ele tiver sido reeleito em 2020, a coluna 'ELEICAO_2016' mostra 1. Caso contrário, 0. Além disso, as colunas 'VR_BEM_CANDIDATO', 'VR_RECEITA' e 'VR_DESPESA_CONTRATADA' podem futuramente contribuir para a análise e seria possível verificar, por exemplo, se os candidatos que gastam mais têm mais chance de ser eleito.Ainda não foi possível saber o suficiente para avaliar e comparar os resultados abaixo. Futuramente, com o aprendizado da aula, poderei fazer isso.- [Referência 1](https://towardsdatascience.com/logistic-regression-from-scratch-with-numpy-da4cc3121ece)- [Referência 2](https://edisciplinas.usp.br/pluginfile.php/3769787/mod_resource/content/1/09_RegressaoLogistica.pdf)- [Referência 3](https://matheusfacure.github.io/2017/02/25/regr-log/)- [Referência 4](https://repositorio.enap.gov.br/bitstream/1/3452/3/Aula%202%20-%20Geraldo%20Goes%20e%20Alexandre%20Ywata%20-%20Introdu%C3%A7%C3%A3o%20%C3%A0%20Regress%C3%A3o%20Log%C3%ADstica.pdf)- [Referência 5](https://medium.com/omixdata/estat%C3%ADstica-an%C3%A1lise-de-regress%C3%A3o-linear-e-an%C3%A1lise-de-regress%C3%A3o-log%C3%ADstica-com-r-a4be254df106)<jupyter_code># 1 significa eleito em 2016 // sucesso
# 0 significa não eleito em 2016 // insucesso
var_resposta = eleitos_2020_2turno['ELEICAO_2016']

var_resposta.head(2)
var_explicativas = eleitos_2020_2turno.drop(columns = ['ELEICAO_2016'])
var_explicativas = pd.get_dummies(var_explicativas)
from sklearn.linear_model import LogisticRegression

logisticRegr = LogisticRegression()

model = logisticRegr.fit(var_explicativas, var_resposta.values.ravel())
predictions = logisticRegr.predict(var_explicativas)
predictions_prob = logisticRegr.predict_proba(var_explicativas)


predictions = pd.DataFrame(predictions, columns=['predito'])

predictions_prob = pd.DataFrame(predictions_prob, columns=["Prob_0", "Prob_1"])
predictions_prob.head()
predictions.head()
resultado_ml = pd.concat([eleitos_2020_2turno.reset_index(drop=True), predictions_prob], axis=1)
resultado_ml<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08501228501228501, 'fraction_numerical': 0.04815724815724816, 'mean_word_length': 5.411341134113411, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 28, 'https://': 9, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '180291', 'license': 'CC0-1.0', 'n_tokens_mistral': 5884, 'n_tokens_neox': 5252, 'n_words': 1626, 'path': 'ml_exercicio_22out2021/exercicio_machine_learning_24out2021_gabriela_caesar.ipynb', 'repo_name': 'gabrielacaesar/master-insper-dados-automacao-data-storytelling', 'size': '14272'}