WorryFree Computers   »   [go: up one dir, main page]

Engenharia e Análise de Dados com Spark nas Eleições 2022

Usando Python e Spark na análise dos bens dos candidatos

João Pedro
11 min readOct 1, 2022
Photo by Gustavo Leighton on Unsplash

Introdução

Anos eleitorais no Brasil são sempre conturbados. Em eleições presidenciais, ainda mais. Os ânimos se exaltam e a população se divide em defesa do seus candidatos. Eventualmente essa divisão se torna em raiva, e as brigas passam a azedar o almoço de domingo com a família.

No centro do debate sobre a polarização, as redes sociais são apontadas como as principais culpadas. A acusação afirma: BigTecs utilizaram uma quantidade colossal de dados de usuários para treinar algoritmos que, por terem sido utilizados de forma não ética, temperaram o ódio entre os discordantes.

Apesar da má fama que recentemente rodeia essa dupla (Política e BigData), não podemos negar sua importância.

Mas onde o Spark entra nesse papo?

O Apache Spark é uma das principais ferramentas para processamento de dados no contexto do BigData, usada por grandes companhias (como o facebook) para transformar volumes colossais de informação de forma eficiente e distribuída. Neste post, aprenderemos mais sobre ela.

Aproveitando o clima eleitoral, iremos pautar nossos exercícios em uma temática de interesse social. Faremos uma análise sobre os bens declarados dos candidatos deste ano (2022), informação disponível publicamente no portal de dados abertos do governo.

Ou seja, além de aprender um pouco mais sobre engenharia de dados, ainda sairemos mais informados sobre a realidade dos políticos brasileiros.

Disclaimer

1. O intuito deste trabalho é meramente aprender a utilizar o Apache Spark. Embora os dados utilizados tenham teor político, não há intenção de fazer qualquer tipo apologia, seja positiva ou negativa, a qualquer partido ou candidato.

2. O autor do trabalho declara não ter vínculo algum com nenhum partido político ou candidato.

Preparando o Ambiente

O que é necessário para executar esse projeto:

  • Apache Spark instalado na sua máquina.
  • Python 3.6+ com a biblioteca Pyspark

Usaremos os dados de bens declarados dos candidatos de 2022 disponíveis no site de dados abertos do governo.

O código estará disponível no GitHub.

Introdução ao Apache Spark

Apache Spark é uma framework open source de processamento de dados otimizada para lidar com grandes volumes. Ela é capaz de alcançar um grande poder de processamento através de inúmeras otimizações no processo de consulta aos dados, como o paradigma da lazy-evaluation, e da capacidade de operar de maneira distribuída, permitindo escalonamento horizontal.

Com ela, é possível construir de simple queries em bases de dados relacionais, com consultas SQL triviais, até complexas Pipelines sobre bases noSQL com algoritmos de machine learning.

Apesar de ser uma ferramenta extremamente complexa em implementação, como veremos no decorrer do post, ela é bem simples de usar. Use sua linguagem preferida, defina as consultas com SQL puro ou com funções da biblioteca e deixe o Spark cuidar do resto.

O spark foi construído utilizando a linguagem Scala, mas pode ser usado a partir de clientes no R, Java ou Python.

Ao meu ver, as principais vantagens do Spark são:

  • Múltiplas conexões: De arquivos brutos no disco (csv, parquet), passando por bancos relacionais, noSQL, serviços de streaming, sendo cloud ou local, ele se conecta com muita coisa!
  • Fácil de iniciar: Não é necessário entender profundamente a ferramenta para fazer o simples! As queries são fáceis de definir, especialmente se você já sabe SQL ou é familiar com o Pandas do Python.
  • Distribuído (ou não): Otimize a consulta a grandes conjuntos de dados com uma poderosa arquitetura distribuída ou utilize no modo standalone para projetos menores.
  • Open source: Totalmente grátis e transparente, o que é sempre bom :)

Implementação

Este será um projeto rápido e simples, onde usaremos o Spark no Python para responder algumas perguntas interessantes sobre os dados coletados.

Vamos usar uma abordagem bem prática, explicando os conceitos conforme as queries são construídas.

Passos iniciais e Lazy Evaluation

Todo programa Spark é aberto com o instanciamento de uma Spark Session. Este o objeto é nossa conexão com o serviço do Spark, ou seja, usaremos ele para tudo.

Se nenhum erro pipocou na sua tela, então significa que tudo correu bem.

O próximo passo é ler o arquivo do disco, e aqui as coisas começam a ficar interessantes. A sintaxe é bem simples, veja abaixo.

Definimos o tipo de arquivo que será lido e alguns metadados sobre a operação. ‘InferSchema=True’ indica que o spark terá que tentar se virar para descobrir os nomes e tipos das colunas, porquê eu não to afim de dizer isso pra ele. Header indica que o csv possui uma linha de cabeçalho. E o separator (sep) diz qual caractere é usado para limitar os valores no arquivo.

Neste exemplo, estamos carregando arquivos do disco local, mas poderíamos muito bem nos conectar a qualquer base de dados suportada pelo Spark, como MongoDB ou Postgres, e a sintaxe não muda muito, apenas as configurações.

Dito isto, vale a pena tentar visualizar o conjunto de dados carregado:

Ué, cadê os dados?

Pois é, esta é a hora de discutirmos a filosofia central do Spark — Lazy Evaluation (Avaliação preguiçosa, em tradução literal). Em resumo, o Spark não irá acessar o disco e carregar qualquer dado em memória até que você especifique exatamente o que você quer deles.

Como assim Spark 😭😭😭😭?

Até o momento, nós apenas especificamos onde os dados estão e informações sobre sua formatação, mas não fizemos nenhuma consulta sobre ele, não perguntamos quantas linhas existem, a soma dos valores declarados ou quantos candidatos existem, então pra quê desperdiçar tempo acessando o disco (algo demorado) e carregando dados em memória se nenhum cálculo vai ser feito sobre eles?

E pior ainda, imagine que o Spark faça todo esse trabalho, carregue todos os dados em memória, mas nos desejamos ver apenas a primeira linha…

O Spark adiará o acesso ao disco o máximo que ele conseguir, e só irá acessar a origem dos dados quando for definida uma ACTION, como show, collect, count ou take. Vamos usar a operação .show(10) para visualizar as primeiras linhas do nosso conjunto.

A saída mostra as 10 primeiras linhas do conjunto de dados.

ACTIONS? como assim 😭😭😭😭?

No Spark, as operações sobre os dados são divididas em Transformations e Actions. Transformations, como o nome sugere, são transformações feitas sobre os dados (selecionar campos, mudar tipos, agrupar dados, etc), enquanto actions são operações que retornam os dados (mostrar dados, contar dados, obter dados, etc).

Primeiras queries e planos de execução

Vamos continuar com as consultas.

O conjunto de dados que estamos usando armazena todos os valores como strings, ou seja, precisamos converter o campo VR_BEM_CANDIDATO, o valor do bem do candidato, para float. Além disso, para melhorar a visualização, vamos selecionar apenas as colunas SQ_CANDIDATO, DS_TIPO_BEM_CANDIDATO e VR_BEM_CANDIDATO do nosso conjunto.

Veja abaixo como isso é feito…

Para fazer a maior parte das operações do Spark no Python, precisamos importar as funções do módulo SQL. Saiba mais sobre as funções básicas disponíveis no pySpark.

A saída:

Essa é a estrutura geral de uma consulta Spark no Python, um conjunto de transformações aplicadas sequencialmente que terminam com uma ação.

Quando uma pipeline de operações é definida, o Spark irá montar um plano de execução, basicamente se planejando sobre como os passos serão executados.

Planos são um pouco complicados, vou tentar dar uma ideia geral sem muito aprofundamento…

Vamos ver o plano de execução da consulta anterior.

Para isso, precisamos definir a query sem a ação .show() ao fim, pois ela causaria a execução do plano.

Ok, está um pouco confuso

Para entender um pouco melhor, vamos adicionar um passo na nossa consulta e filtrar apenas os valores superiores a 10mil reais.

Explicando o plano abaixo

O Entendimento do plano acima ainda não é a coisa mais trivial do mundo, mas vamos focar em alguns pontos específicos.

Cada item do plano acima representa uma etapa de execução.

A questão interessante é que, quando nós definimos nossa query, o filtro dos bens com valor acima de 10 mil reais veio por último. Entretanto, quando analisamos o plano de execução acima, vemos que o filtro foi colocado na segunda etapa, não na última.

O Spark o empurrou para mais perto do começo.

Isso se chama predicate pushdown, que basicamente consiste em tentar empurrar filtros para o começo da query evitando processar desnecessariamente linhas que serão descartadas ao final do resultado.

Além disso, também podemos notar que algumas de nossas operações foram compactadas em um único passo. Na etapa 2, o spark realiza o cast para float e o filtro ao mesmo tempo.

Essas são algumas das muitas otimizações de plano que o Spark é capaz de fazer.

Otimização de planos é uma grande vantagem do paradigma lazy-evaluation, pois permite que o Spark se planeje melhor antes de realizar a execução pra valer.

Agregações e partições

Agora que entendemos um pouco mais sobre as consultas em si, vamos partir para perguntas mais interessantes. Vamos calcular o total de bens declarados por todos os candidatos.

Juntando todos os bens de todos os candidatos, temos um total de ~23 Bilhões.

A discussão sobre agregações no Spark vai ser um pouco mais teórica. Internamente, os dados são guardados no Spark no formato RDD (Resilient Distributed Dataset), que nada mais é que a divisão de um conjunto de dados em fatias menores que podem ser distribuídas para diversas máquinas.

Fonte — https://sparkbyexamples.com/spark-rdd-tutorial/

Esta é a estrutura que permite ao Spark utilizar o paralelismo de máquinas distribuídas, pois cada uma pode ficar responsável por processar uma parte dos dados.

Entretanto, no caso de operações agregadoras, como no caso de somas, os resultados não podem ser meramente calculados isoladamente nas máquinas, eles precisam ser totalizados em algum ponto central.

Esse tipo de operação é chamado de Wide Operation ou Shuffle Operation, pois são necessárias várias partições interagindo juntas para totalizar o resultado. Mais sobre no link.

É importante ter em mente que esse tipo de operação pode ser muito custosa.

Joins e Spark SQL

A nossa tabela de bens consultados precisa ser unida à tabela da lista de todos candidatos para que possamos fazer queries mais complexas, utilizando informações como o cargo a qual ele concorre e seu partido.

Para isso, precisamos carregar os dois datasets e fazer um Join entre eles. Felizmente, isso é uma operação muito simples.

O primeiro passo é ‘carregar’ os arquivos de bens e de candidatos.

Importante lembrar: O spark só vai executar suas operações quando uma action for chamada, então por mais que eu esteja chamando de ‘carregar os arquivos’, tenha em mente que até o momento NADA foi feito.

E então definir o join entre as tabelas. O campo usado para o join é o SQ_CANDIDATO, ‘chave’ da tabela de candidatos.

E ver as primeiras linhas do resultado

Com essa tabela seremos capazes de fazer queries muito mais elaboradas, como veremos mais a frente, mas agora vamos focar nos Joins.

Joins invariavelmente relembram SQL, pois eles são uma operação importantíssima das bases relacionais.

Até esse momento, as queries foram montadas usando a inteface funcional do python, com cada etapa sendo representada por uma função (comumente chamado de chaining).

Entretanto, o Spark também possui uma interface em puro SQL!

Ou seja, se você não sabe programar em nenhuma das linguagens suportadas pelo Spark, ainda pode fazer uso dele se souber SQL.

E o melhor de tudo, não existe diferença PRÁTICA entre definir as queries na interface pura do python ou na interface SQL pois, por baixo dos panos, o Spark as trata da mesma maneira. Ou seja, fica puramente ao seu critério qual irá usar.

Vamos ver abaixo.

E o resultado é o mesmo …

Juntando tudo nas queries finais

Agora que já exploramos os principais conceitos do Spark, está na hora de pôr a mão na massa de verdade!

Pra consolidar o conhecimento, vamos fazer uma série de queries respondendo questões interessantes sobre os dados.

Tentei trazer queries de diversas complexidades e temáticas, de forma a aprender sobre várias funcionalidades da ferramenta. Um bom exercício é tentar fazer perguntas que eu não abordei aqui, mas que você considera importante.

Vamos à lista de perguntas.

Valor total em bens declarados pelos candidatos

Valor total em bens declarados por UF

Top 5 tipos de bens com maior valor declarado

Quantidade de candidatos APTOS concorrendo a cada cargo

Top 10 candidatos mais ricos

Candidato mais rico de cada cargo concorrido

Essa aqui é mais complicada…
precisa usar uma window function com Over para determinar as posições de riqueza por grupo.

Total de bens declarados dos candidatos a Deputado Federal por partido

Conclusão

A capacidade de obter e processar dados em larga escala é geralmente relacionado às grandes companhias de tecnologia do mercado, como Google, Facebook ou Netflix. Entretanto, as ferramentas de Big Data podem ser igualmente úteis para organizações públicas.

A capacidade de processamento do Spark pode agilizar a montagem de relatórios e dashboards públicos , trazendo mais celeridade e transparência aos órgãos, o que facilita o controle por parte da população.

Os exemplos usados no post exploram um potencial caso de uso: aumentar a transparência de contas dos candidatos.

Naturalmente, foi usado um conjunto pequeno de dados, com intuito de aprender sobre a ferramenta, mas em cenários reais, com um volume muito maior, o uso do Spark realmente faz diferença.

Eu sempre acho interessante abordar ferramentas em um potencial contexto de aplicação. Tentei explorar os principais aspectos do Spark, sem muito aprofundamento nos detalhes, mais para dar uma ideia do poder da ferramenta, espero que tudo tenha ficado claro.

Por fim, uma população informada vota melhor, porquê o voto consciente depende de um eleitor informado.

Obrigado por ler! ;)

Referências

Código disponível no repositório do GitHub
github.com/jaumpedro214/analise_bens_deputados_fed
Dados usados no projeto
https://dados.gov.br/dataset/candidatos-2022

[1] CHAMBERS, Bill; ZAHARIA, Matei. Spark: The definitive guide: Big data processing made simple. “ O’Reilly Media, Inc.”, 2018.
[2] KARAU, Holden et al. Learning spark: lightning-fast big data analysis. “ O’Reilly Media, Inc.”, 2015.
[3] Agência Senado. Eleição este ano terá mais de 28 mil candidatos; veja os números. Disponível no link
[4] Ian Pointer em InfoWorld. What is Apache Spark? The big data platform that crushed Hadoop.
[5] SparkByExamples. Spark SQL window functions. Disponível no link.
[6] Apache Spark Official Documentation. RDD Programming Guide. Disponível no link.
[7] Laurent Leturgez no Medium. Spark’s Logical and Physical plans … When, Why, How and Beyond.
[8] Anjana Sudhir no Medium. Spark Query Plans for Dummies.
[9] Datastax Documentation. Using Spark predicate push down in Spark SQL queries.
[10] saurav omar no Medium. Wide Vs Narrow dependencies in Apache Spark.
[11] Engineering at Meta. Apache Spark @Scale: A 60 TB+ production use case.

--

--

João Pedro

Bachelor of IT at UFRN. Graduate of BI at UFRN — IMD. Strongly interested in Machine Learning, Data Science and Data Engineering.