Picture

Oi, eu sou Capi Etheriel.

Web Developer, Web Designer, Web Scraper. Consultor em tecnologias Livres para Web. Contribuidor Drupal, Scrapy, Javascript.

Investigando os supersalários da Unicamp

A noticia de que existem supersalários na Unicamp é antiga, mas voltou a circular recentemente, por conta da decisão do magnífico reitor de publicar a lista nominal – como exige o Tribunal de Contas do Estado. E já que chutaram o vespeiro, resolvi pegar o documento que a Diretoria Geral de Recursos Humanos publica e trabalhar um pouco os dados.

Tirando os dados do PDF e passando para o CSV

Começamos pelo documento, em PDF, péssimo pra trabalhar – mas no arsenal do Jornalista Hacker do Software Livre temos uma ferramenta ideal para isso: o Tabula. Instalar no Ubuntu é razoavelmente simples (pros outros sistemas, leia o README):

  • Instale o Java, se não estiver instalado
  • Baixe o programa do site oficial
  • Descompacte e abra a pasta descompactada no terminal
  • Execute a linha seguinte:
java -Dfile.encoding=utf-8 -Xms256M -Xmx1024M -jar tabula.jar

Fazendo o upload do PDF no Tabula

Seu navegador provavelmente vai abrir automaticamente na página do Tabula mas se isso não acontecer, escreva http://127.0.0.1:8080 na barra de endereços e aperte enter. A tela principal é meio óbvia, simplesmente clique em upload e selecione seu arquivo. Nem encane com o Auto-Detect Tables, não é necessário pra esse caso (e detecta errado neste PDF em particular).

Selecionando as tabelas no Tabula

Desenhe manualmente, na primeira página um retângulo que cubra toda a tabela. Em seguida clique em Repeat this selection, para não precisar selecionar a mesma área em todas as páginas – o Tabula faz isso por você. Finalmente, clique em Download all data, o botão verde.

Exportando os dados no Tabula

Vai demorar um pouco pra processar todas as páginas. No meu laptop, demorou 4 minutos. Mas eventualmente a tela atualizou e mostrou a opção de baixar todos os dados como CSV. Pronto, agora dá pra trabalhar direto com os dados.

Processando os dados com Python e CSVKit

A primeira coisa que eu tive que fazer com os dados em CSV foi trocar a virgula por ponto, pra ficar na notação decimal padrão do Python. Eu fiz isso com um script minúsculo em Python 3:

import csv

with open('./tabula-65cdc698f3beab70fb5daa6de6d0d92276872c35.csv') as inputdata,\
    open('./remuneracao-decimais.csv', 'w') as outputdata:

    datawriter = csv.writer(outputdata, dialect='unix')
    for line in csv.reader(inputdata):
        datawriter.writerow([cell.replace(',', '.') for cell in line])

Agora eu queria ordenar pelo salário bruto e pelo salário líquido, em cópias do arquivo. Eu poderia programar isso com Python puro, mas é tão melhor poder contar com uma solução pronta e empacotada pra gente! Por isso eu resolvi usar o CSVKit, um canivete suíço pra operar CSV na linha de comando.

Com o csvkit, ordenar foi simples, embora demorado. Cada comando abaixo levou uns 4 minutos – afinal, pra ordenar esse arquivo o programa precisa carregar as 140 mil linhas de dados e comparar. Mas como eu não precisei programar, eu fui tomar um suco.

csvsort -c 6 --reverse --no-header-row remuneracao-decimais.csv > remuneracao-por-liquido.csv
csvsort -c 1 --reverse --no-header-row remuneracao-decimais.csv > remuneracao-por-bruto.csv

Se você não tiver um suco para tomar, baixe o arquivo ordenado por salario bruto e o arquivo ordenado por salario liquido.

Pegando os nomes dos servidores

O PDF original não tinha os nomes, mas o número de matrícula dos servidores públicos. No entanto, a Unicamp tem um serviço que permite consultar funcionarios pela matricula. Portanto, foi só questão de escrever um robôzinho pra pegar esses dados usando meu framework favorito, o Scrapy.

Eu fiz um projetinho simples, que me permite automatizar a consulta pela matrícula do funcionário. O processo é meio dificultado porque o site nao me permite fazer muitas consultas em paralelo, então eu tive que limitar a apenas uma consulta a cada 7 segundos. O que dá mais ou menos 10 por minuto, no mínmo 10 minutos pra pegar 100 funcionarios ou quase 2 horas pra pegar os mil funcionarios com maior salário. Eu poderia rotear as consultas por outros servidores, pela rede Tor ou por um serviço especializado. Mas eu tenho tempo, posso ir fazer uma janta.

Pra rodar meu scraper, basta ter o Scrapy instalado, clonar o projeto e rodar:

scrapy crawl dgrh -o supersalarios.csv

Curiosamente, dos 100 primeiros resultados só 14 aparecem no site. O que significa que os outros 86 estão sendo protegidos ou simplesmente não existem. Se você quiser tentar com mais servidores, passe o parâmetro volume=666 para pegar 666 servidores:

scrapy crawl dgrh -a volume=666 supersalarios-top666.csv

Visualizando os dados

Duas curiosidades que me bateram: quantos servidores temos em cada faixa salarial (agrupada por salários mínimos). E qual o gasto total da Unicamp com cada faixa salarial?

Lidar com dados em Python é muito fácil. Pra começar, precisamos dar uma limpada no CSV – pro CSV tudo é string, só uma sequência de letrinhas. Mas nós sabemos que é um float, então vamos converter. E já aproveitamos para deduzir onde agrupar: o valor dividido pelo salario minimo, sem o resto. No Python, // é um operador para divisão sem resto. Então temos:

class Wage(object):
    def __init__(self, data):
        self.value = float(data.strip())
        self.hist_bin = int(self.value // 788)

Agrupar valores costuma render algo parecido com um histograma. Eu resolvi agrupar usando um defaultdict, que me permite usar a sintaxe de dicionário e ter uma lista como valor padrão:

import csv
from collections import defaultdict

hist = defaultdict(list)
with open('./remuneracao-decimais.csv') as datafile:
    reader = csv.reader(datafile)
    for line in reader:
        wage = Wage(line[1])
        hist[wage.hist_bin].append(wage.value)

Finalmente, preparar os dados pra fazer os plots. Duas transformações em dicionários e basta escrever os CSV: gráfico dos gastos e gráfico da contagem de servidores.

plot_sum = {hist_bin: sum(paychecks) for hist_bin, paychecks in hist.items()}
plot_count = {hist_bin: len(paychecks) for hist_bin, paychecks in hist.items()}

with open('sumplot.csv', 'w') as plotfile:
    writer = csv.writer(plotfile)
    writer.writerows(plot_sum.items())

with open('countplot.csv', 'w') as plotfile:
    writer = csv.writer(plotfile)
    writer.writerows(plot_count.items())

Eu fiquei com preguiça de usar o Matplotlib, então eu resolvi usar o Plotly manualmente mesmo.

Servidores por faixa salarial

Gastos por faixa salarial

Fica a pergunta: porque temos tantas pessoas recebendo menos de um salário mínimo?