Skip to content

Latest commit

 

History

History
283 lines (232 loc) · 9.54 KB

metodologia.md

File metadata and controls

283 lines (232 loc) · 9.54 KB

Metodologia

O objetivo é estruturar uma base de dados relacional integra, onde se possa utilizar o SQL - linguagem de consulta estruturada - para inferir conhecimento relativo ao financiamento "legal" dos últimos 15 anos de campanhas políticas no Brasil.

A execução do script ilumina.sh automatiza todo o processo descrito abaixo, incluíndo o download das fontes originais. Edite aí antes a configuração de acesso a seu DB.

Muitos dados da fonte são ignorados, pois o foco são as receitas das doaçoes, não as despesas.


Primeiro, é fundamental corrigir os erros de má-formatação das fontes do TSE, removendo espaços extras, campos nulos e as aspas excessivas inválidas. Cada ano exige uma limpeza distinta, mas em geral utiliza-se:

sed -e "s/\"\ /\"/g; s/\ \"/\"/g; s/\ +/\ /g; s/\"//g; s/#NULO#//g; s/#NULO//g" -i fontes_tse/*.txt

Ver os scripts.


Com o MySQL como Sistema Gerenciador de Banco de Dados Relacional,

CREATE DATABASE tse;

Após analise intuitiva de cada arquivo .CSV (valores separados por vírgulas) disponibilizado no Repositório de Dados Eleitorais do TSE contendo os dados das receitas de Doações para Candidados, Comites e Partidos, a seguinte tabela é proposta;

CREATE TABLE `doacoes` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `uf` varchar(2) NOT NULL,
  `partido` varchar(8) DEFAULT NULL,
  `cargo` varchar(255) DEFAULT NULL,
  `candidato` varchar(255) DEFAULT NULL,
  `numero` int(15) DEFAULT NULL,
  `ano` int(4) DEFAULT NULL,
  `cpf_candidato` varchar(20) DEFAULT NULL,
  `doador_original` varchar(255) DEFAULT NULL,
  `doador` varchar(255) DEFAULT NULL,
  `cpf_doador` varchar(20) DEFAULT NULL,
  `cpf_doador_original` varchar(20) DEFAULT NULL,
  `recurso` varchar(255) DEFAULT NULL,
  `setor_economico` varchar(255) DEFAULT NULL,
  `data` varchar(20) DEFAULT NULL,
  `motivo` varchar(255) DEFAULT NULL,
  `valor` double(12,2) DEFAULT NULL,
  `tipo` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

Como cada arquivo .CSV do TSE tem uma estrutura/layout diferente, é necessária uma adaptação no SQL de importação para cada fonte. Como exemplo, para importar a prestação de contas das receitas dos candidatos de 2016:

use tse;
load data local infile './fontes_tse/2016/receitas_candidatos_prestacao_contas_final_2016_brasil.csv '
  into table doacoes
    fields terminated by ';'
    lines terminated by '\n'
    ignore 1 lines
    (@cod, @descr, @data, @pretador, @seqcand, @uf, @nomeuf, @siglauf, @partido,
    @numero, @cargo, @nome, @cpfcandidato, @cpfvice, @recibo, @numdoc, @cpf,
      @doador, @nomdoadreceita, @uf_doador, @numpart, @numcandd, @codseeco,
        @seteco, @data, @valor, @tipo, @fonte, @especie,
          @descrec, @cpforiginario, @nomedoador, @tipodoro, @setorigi, @nomeorig)
SET
  ano="2016", tipo="candidato",
  uf = @uf,
  nome=@nome,
  cargo=@cargo,
  numero=@numero,
  cpf_doador=@cpf,
  cpf_vice=@cpfvice,
  cpf_candidato=@cpfcandidato,
  doador=@doador, partido=@partido,
  recurso = @tipo,
  motivo = @descrec,
  data=left(@data , 10),
  valor=cast(replace(@valor, ',', '.') AS decimal( 9, 2 ) )

para importar os dados dos comites do .CSV de 2016

use tse;
load data local infile './fontes_tse/2016/receitas_partidos_prestacao_contas_final_2016_brasil.csv'
  into table doacoes
  fields terminated by ';'
    enclosed by '"'
    lines terminated by '\n'
    ignore 1 lines

    (@cod, @descr, @data, @pretador, @seqcand, @uf, @nomeuf, @siglauf, @tipodiretorio, @partido,
    @numero, @numdoc, @cpf, @doador, @nomdoadreceita, @uf_doador, @numero, @numcandd, @codseeco,
        @seteco, @data, @valor, @tipo, @fonte, @especie,
          @descrec, @cpforig, @nomedoador, @tipodoro, @setorigi, @nomeorig)
  SET
  ano="2016", tipo="partido",
  uf = @uf,
  nome = @tipodiretorio,
  cpf_doador = @cpf,
  cpf_doador_original = @cpforig,
  doador = @doador,
  partido = @partido,
  numero = @numero,
  cpf_candidato = @cpf,
  recurso = @tipo,
  motivo = @descrec,
  data=left(@data , 10),
  valor=cast(replace(@valor, ',', '.') AS decimal( 9, 2 ) )

Com a tabela doacoes contendo os dados de todos os CVSs, muitas consultas já podem ser realizadas, mas vamos abstrair as redundancias pra otimizar os possíveis futuros inquéritos.

  • Extrair tabela Candidatos
CREATE TABLE candidatos
  select partido, uf, ano, nome, cargo, numero, cpf_candidato, cpf_vice
    from doacoes
      where tipo="candidato"
      group by partido, uf, nome, cargo, numero, cpf_candidato, cpf_vice, ano;

ALTER TABLE `tse`.`candidatos`
  ADD COLUMN `id` INT NOT NULL AUTO_INCREMENT FIRST,
  ADD PRIMARY KEY (`id`);
  • Extrair tabela Doadores
create table doadores
  select doador, uf, cpf_doador
    from doacoes
      group by doador, uf, cpf_doador;


ALTER TABLE `tse`.`doadores`
  ADD COLUMN `id` INT NOT NULL AUTO_INCREMENT FIRST,
  ADD PRIMARY KEY (`id`);
  • Extrair tabela Comites
create table comites
  SELECT nome, partido, uf
  FROM doacoes where tipo != 'candidato'
    group by nome, partido, uf;

ALTER TABLE `tse`.`comites`
  ADD COLUMN `id` INT NOT NULL AUTO_INCREMENT FIRST,
  ADD PRIMARY KEY (`id`);

Otimizar tabela geral doacoes para apontar para as novas tabelas de doadores, candidatos e comites extraídas.

CREATE INDEX index_doador ON doadores ( doador, uf, cpf_doador );
CREATE INDEX index_doador ON doacoes ( doador, uf, cpf_doador );

ALTER TABLE `tse`.`doacoes`
  ADD COLUMN `candidato_id` INT(7) NULL AFTER `id`,
  ADD COLUMN `doador_id` INT(7) NULL AFTER `candidato_id`,
  ADD COLUMN `comite_id` INT(7) NULL AFTER `doador_id`;

CREATE INDEX index_candidato ON candidatos ( nome, cpf_candidato, numero, partido, ano, cargo );
CREATE INDEX index_candidato ON doacoes ( nome, cpf_candidato, numero, partido, ano, cargo );

# as próximas execuções serão demoradas,
# pode ser necessário estender o tempo de execução.
# SET SESSION MAX_EXECUTION_TIME=20000000;

# atribui doadores
update doacoes d, doadores dd
  set d.doador_id = dd.id
    where dd.doador = d.doador and d.uf=dd.uf and d.cpf_doador=dd.cpf_doador;
# Query OK, 3696309 rows affected (44 min 46.83 sec)

# atribui candidatos
update doacoes d, candidatos c
  set d.candidato_id = c.id
    where c.nome = d.nome and c.cpf_candidato=d.cpf_candidato
      and c.numero=d.numero and c.partido=d.partido
        and c.ano=d.ano and c.cargo=d.cargo;
# Query OK, 2935487 rows affected (30 min 0.58 sec)

# atribui partidos
update doacoes d, comites c
  set d.comite_id = c.id
    where d.partido = c.partido and d.nome=c.nome and c.uf=d.uf;

# atribui chaves-estrangeiras para otimizar referências
ALTER TABLE `tse`.`doacoes`
ADD INDEX `doacoes_ibfk_1_idx` (`candidato_id` ASC),
ADD INDEX `doacoes_ibfk_2_idx` (`comite_id` ASC),
ADD INDEX `doacoes_ibfk_3_idx` (`doador_id` ASC),
ADD CONSTRAINT `doacoes_ibfk_1`
  FOREIGN KEY (`candidato_id`)
  REFERENCES `tse`.`candidatos` (`id`)
ADD CONSTRAINT `doacoes_ibfk_2`
  FOREIGN KEY (`comite_id`)
  REFERENCES `tse`.`comites` (`id`)
ADD CONSTRAINT `doacoes_ibfk_3`
  FOREIGN KEY (`doador_id`)
  REFERENCES `tse`.`doadores` (`id`)

Remove as colunas da tabela doacoes que já foram abstraidas para as respectivas tabelas.

ALTER TABLE `tse`.`doacoes`
  DROP COLUMN `cpf_candidato`,
  DROP COLUMN `cpf_vice`,
  DROP COLUMN `doador`,
  DROP COLUMN `cpf_doador`,
  DROP COLUMN `numero`,
  DROP COLUMN `nome`,
  DROP COLUMN `cargo`,
  DROP COLUMN `partido`,
  DROP COLUMN `tipo`,
  DROP COLUMN `uf`,
  DROP INDEX `index_candidato` ,
  DROP INDEX `index_doador` ;

Cria e define nova coluna em Candidatos, Comites e Doadores com soma geral de valores gastos e quantidade de doações.

ALTER TABLE `tse`.`comites`
ADD COLUMN `valor_total` DOUBLE(12,2) NULL AFTER `uf`;

update comites c,
  (select comite_id, sum(valor) as soma from doacoes group by comite_id) as d
  set c.valor_total = d.soma
    where d.comite_id = c.id;

ALTER TABLE `tse`.`doadores`
ADD COLUMN `valor_total` DOUBLE(12,2) NULL;

update doadores dd,
  (select doador_id, sum(valor) as soma from doacoes group by doador_id) as d
  set dd.valor_total = d.soma
    where d.doador_id = dd.id;

ALTER TABLE `tse`.`candidatos`
ADD COLUMN `valor_total` DOUBLE(12,2) NULL AFTER `cpf_vice`;

update candidatos c,
  (select candidato_id, sum(valor) as soma from doacoes group by candidato_id) as d
  set c.valor_total = d.soma
    where d.candidato_id = c.id;

update candidatos c,
  (select candidato_id, count(*) as qtd from doacoes group by candidato_id) as d
  set c.doacoes_count = d.qtd
    where d.candidato_id = c.id;

update doadores d,
  (select doador_id, count(*) as qtd from doacoes group by doador_id) as c
  set d.doacoes_count = c.qtd
    where c.doador_id = d.id;    
  • Corrige algumas datas automaticamente
ALTER TABLE `tse`.`doacoes`
  ADD COLUMN `quando` DATE NULL AFTER `data`;

update doacoes set quando = STR_TO_DATE(`data`, '%d/%m/%Y')
    WHERE quando is null AND data REGEXP '^[0-9]{1,2}/\[0-9]{2}/\[0-9]{4}$';

update doacoes set quando = str_to_date(data, '%d-%b-%y')
  WHERE quando is null AND data REGEXP '^[0-9]{1,2}-.{3}-[0-9]{2,4}$';
  • Otimiza tabelas após tamanhas mudanças estruturais
  OPTIMIZE TABLE doadores, candidatos, comites, doacoes;

Por fim temos a seguinte estrutura

modelo

Ok. Ufa! A partir disso é possível iniciar quaisquer inquéritos e investigações sólidas a partir de perguntas bem formuladas em SQL.