Criando features de Machine Learning a partir de nomes de cidades

Sillas Gonzaga
Creditas Tech
Published in
8 min readMar 31, 2021

Em muitos modelos de Machine Learning em que os objetos de interesse são pessoas, é comum querer utilizar a cidade como feature de um modelo, visto que pessoas de diferentes cidades tendem a ter diferentes níveis de risco de crédito, propensão a compras, atividade econômica, etc. Contudo, a abordagem que costuma ser mais utilizada, que é manter apenas os valores mais comuns e substituir o restante por um termo genérico como “outros”, tem seus problemas, conforme ilustrado a seguir.

Vamos ilustrar com um simples exemplo. Suponha que temos o objetivo de construir um modelo de default de crédito com as seguintes variáveis:

Amostra do dataset

Temos uma amostra de 10 pessoas com as features de idade, renda e cidade, além da variável resposta binária Default (1 se a pessoa entrou em dívida para com o credor, 0 caso contrário).

Nessa amostra de 10 linhas, existem 7 valores distintos para a variável cidade, mas no dataset como um todo existem 3987 valores. Sabe-se que variáveis categóricas com grandes quantidades de valores distintos são um problema para muitos modelos de Machine Learning, portanto faz-se necessário utilizar algum método para reduzir essa quantidade.

Uma outra dificuldade enfrentada em projetos de cientistas de dados é que esta variável está bem sujinha: note como os nomes de cidades não foram corretamente preenchidos e não seguem um padrão.

A abordagem mais comum é usar uma transformação que mantém os 10 (número arbitrário) valores mais comuns e alterar os restantes para “outros”. Em Python, isto pode ser feito assim:

Output do código acima

Essa estratégia, apesar de popular, tem seus problemas:

  • As cidades que não pertencem ao top 10 são todas categorizadas como sendo a “mesma cidade”, o que reduz a variabilidade da feature. Cidades que têm bons resultados de default foram misturadas com aquelas que têm maus resultados.
  • A distribuição da feature foi significativamente alterada. São Paulo era a principal cidade, agora é a segunda.

Partimos então para a solução, cuja ideia consiste em representar a cidade com features numéricas, evitando a perda de informação. A justificativa para isso é bem simples: o que leva alguém a querer utilizar o nome da cidade como feature é o fato de diferentes cidades possuírem diferentes resultados em relação à variável resposta. Por exemplo, espera-se que São Paulo (SP) tenha uma taxa de default diferente de São Luís (MA). Mas a que se deve isso? Ao fato de as cidades terem nomes diferentes? A provável explicação é que essas cidades têm diferentes contextos socioeconômicos, o que acaba diferenciando os habitantes de suas cidades. Assim, faz todo o sentido buscar features que representam os níveis socioeconômicos dos municípios mais diretamente. O algoritmo para chegar nesta solução será descrito neste post.

Obtendo features socioeconômicas municipais

Para este post, usaremos estas libs de Python:

Dentre a diversidade de fontes de indicadores municipais que podem ser utilizados, este post focará nos dados municipais de PIB, população e IDH.

Para obter os dados do IDHM (Índice de Desenvolvimento Humano Municipal), usamos os dados do Atlas Brasil. Infelizmente, a versão mais recente desse indicador é de 2013, o que pode ser considerado meio antigo. Contudo, se assumirmos o pressuposto de que as cidades mantiveram proporcionalmente os mesmos índices socioeconômicos de lá para cá, é aceitável usar dados com essa defasagem temporal.

Para baixar o dataset do IDHM (e de outros indicadores, que não serão usados aqui), acesse a página de bases de dados do portal Atlas Brasil e clique em Censo Demográfico (1991, 2000 e 2010). Baixe o zip Bases Censo.zip e importe o arquivo Atlas 2013_municipal, estadual e Brasil.xlsx.

Com o arquivo baixado, podemos importá-lo e visualizá-lo utilizando os comandos abaixo em um Jupyter notebook:

Este dataset possui 237 colunas de indicadores de cada município em diferentes anos. Precisamos filtrar o ano mais recente e selecionar apenas a coluna de IDHM, que é a que usaremos neste post. Contudo, sinta-se à vontade para explorar as outras variáveis nesse dataset:

Para obter dados de população e PIB, podemos felizmente contar com uma forma mais pythonica. A lib sidrapy foi criada para facilitar a obtenção de dados da API do SIDRA, um sistema do IBGE que permite o acesso a diversas tabelas disponibilizadas pelo instituto.

No SIDRA existem bases de dados dos mais diversos interesses. Para utilizar a API, você pode buscar qual tabela contém a informação que você deseja. Pessoalmente, acho a interface do site do SIDRA confusa, então segue um breve tutorial de como a utilizar:

1 — No canto superior direito, clique na lupa e digite alguma palavra de interesse, como “população”. Clique em “OK”.

2 — Localize o código da tabela de interesse. No nosso caso, o código é 6579.

3 — Acesse o site de testes da API do Sidra e digite o código da tabela.

4 — Entenda quais parâmetros podem ser usados na consulta a tabela. Por exemplo, para esta tabela, devemos usar o nível territorial “6” para obter dados municipais.

5 — Use a função `sidrapy.get_table()` com os parâmetros aprendidos para baixar os dados:

Fazendo um breve porém importante tratamento de dados, como selecionar apenas as colunas de interesse, remover a primeira linha e renomear as colunas restantes.

Obs.: é possível que a ordem das colunas no dataset mude com o tempo, então, ao rodar o código abaixo, verifique se as colunas retornadas são as mesmas deste post.

O mesmo procedimento é realizado para baixar os dados do PIB municipal:

Podemos então juntar os datasets das features a partir do código dos municípios:

Temos, então, o seguinte dataset de features:

Para separar o nome do município da sigla do estado, usamos o código abaixo:

Fazendo o join

A última etapa deste processo de feature engineering é juntar o dataset de default (df) com o de features municipais (df_municipios). Basta usar as colunas de nome do município como chave no join, certo? Para este dataset de default, do jeito que está, não.

Será necessário implementar algum processo de limpeza da variável cidade do dataset de default. Após alguns testes, chegamos a um procedimento que funciona bem, descrito abaixo:

  1. Um dicionário é criado, onde as chaves são os estados (as siglas) e seus valores são os municípios presentes no dataset.
  2. Os nomes das cidades no dataset passam pela função limpar_nome_cidade() que os padroniza.
  3. Para cada UF presente no dicionário criado em 1., é executado um novo loop para cada cidade, comparando seu nome aos nomes de cidades do dataset do IBGE do mesmo estado (isso é feito para evitar homônimos). Tal comparação é feita usando o algoritmo Levenshtein ratio similarity, que calcula a similaridade entre um par de strings entre 0 a 1. Gerado o vetor de similaridade da cidade Creditas com as cidades do IBGE, é selecionada aquela que possui o maior valor.
  4. O resultado é salvo em um dicionário.

Partimos, então, para o código:

Rodado o código, vamos ver alguns resultados:

Note que a similaridade estimada entre os strings ‘ CARAPICUIBA’ (note o espaço em branco extra) e ‘Carapicuíba’ é de 0.9565217391304348, um match quase perfeito.

Como próximos passos, os resultados são transformados em um dataframe. Para cada valor único do campo cidade do dataset de default, há uma linha indicando a cidade no dataset de features que possui o nome mais similar (e o valor dessa similaridade).

Os resultados acima mostram que o ratio similarity funciona de forma apropriada. Contudo, chega-se a uma pergunta: qual o valor mínimo de similaridade que devemos usar para definir que dois pares de string são similares? Vejamos a distribuição dos percentis da similaridade:

4% dos pares possuem similaridade de pelo menos .77, mas será que esse threshold é suficiente? Vejamos alguns casos:

Analisando os resultados acima empiricamente, pode-se chegar a conclusão de que 0.78 é um threshold aceitável, pois os pares abaixo disso provavelmente não representam a mesma cidade.

Partimos, então, para montar o dataframe contendo a cidade do dataset de default com sua melhor correspondência no dataset de features:

Finalmente, unimos o dataset criado agora com o nosso de interesse:

Agora temos todas as features prontas para serem usadas num modelo de Machine Learning. Enjoy!

Tem interesse em trabalhar conosco? Nós estamos sempre procurando por pessoas apaixonadas por tecnologia para fazer parte da nossa Tripulação! Você pode conferir nossas vagas aqui.

--

--