Como votaram os senadores em 2019

dados públicos
R
python
Author

Tiago Mendonça dos Santos

Published

October 22, 2020

Nesse post faremos uma análise dos dados de votações do Senado Federal no ano de 2019. Anteriormente a análise foi feita realizando o download automático de arquivos pdf´s e extraindo as informações das votações diretamente desses arquivos. Como a estrutura de dados foi alterada, aproveitamos a oportunidade para refazer essa análise considerando R e Python. Um ponto de destaque nessa análise é que podemos alternar o mesmo objeto entre as duas linguagens.

Esse post foi feito em parceria com Paulo C. Marques F..

Introdução

Listamos a seguir os pacotes do R utilizados na análise:

library(reticulate)
library(knitr)
library(kableExtra)
library(plotly)
library(factoextra)
library(tidyverse)

Incialmente utilizaremos Python para importar os dados e fazer o processamento inicial. Utilizaremos o objeto sessao para armazenar os dados relativos às sessões do senado, como ano da matéria, tipo da sessão, total de votos etc. O objeto votos armazenará as informações específicas dos votos de cada sessão.

import requests
import pandas as pd

periods = ["20190101/20190228", "20190301/20190430", "20190501/20190630", "20190701/20190831", "20190901/20191031", "20191101/20191231"]

votos = pd.DataFrame()

sessao = pd.DataFrame()

for prd in periods:
    prj = requests.get("http://legis.senado.leg.br/dadosabertos/plenario/lista/votacao/" + prd,
                       headers = {"Accept": "application/json"}).json()
    for v in prj["ListaVotacoes"]["Votacoes"]["Votacao"]:  
        sessao = sessao.append(v, ignore_index = True)
        try:
            df = pd.DataFrame(v["Votos"]["VotoParlamentar"])
            df["CodigoSessao"] = v["CodigoSessao"]
            df["CodigoSessaoVotacao"] = v["CodigoSessaoVotacao"]
            votos = votos.append(df, ignore_index = True)                       
        except:
            print(v["DataSessao"])
            
sessao = sessao.drop(columns = ['Votos'])

A sessão do dia 26-06-2019 apresenta os resultados em branco. Por isso foi desconsiderada.

Nos próximos tópicos apresentaremos as bases de dados obtidas.

Sessão

Nessa base constam informações como ano da matéria, código da matéria, descrição da votação, horário, total de votos etc. Apresentamos algumas informações da base a seguir:

  Unnamed: 0 AnoMateria CodigoMateria CodigoSessao CodigoSessaoLegislativa
1          0       2019        135252        86315                     858
2          1       2019        135250        86315                     858
3          2       2019        135087        86315                     858
4          3       2019        135251        86315                     858
5          4       2017        129807        87328                     858
6          5       2019        135603        87835                     858
  CodigoSessaoVotacao CodigoTramitacao DataSessao DescricaoIdentificacaoMateria
1                5945          2476719 2019-02-26                    MSF 7/2019
2                5944          2476704 2019-02-26                    MSF 5/2019
3                5943          2476697 2019-02-26                    MSF 2/2019
4                5942          2476686 2019-02-26                    MSF 6/2019
5                5946          2477864 2019-03-12                   PEC 25/2017
6                5947          2478375 2019-03-13 PLP 54/2019 (Substitutivo-CD)
                                                                                                                                                                                                                                                                                                                                                                                        DescricaoVotacao
1                                                                                                                                              Submete à consideração do Senado Federal, nos termos do o art. 84, inciso XIV, combinado com o art. 52, inciso III, alínea "d", da Constituição, o nome do Senhor JOÃO MANOEL PINHO DE MELLO, para exercer o cargo de Diretor do Banco Central do Brasil.
2                                                                                                                                                   Submete à consideração do Senado Federal, nos termos do o art. 84, inciso XIV, combinado com o art. 52, inciso III, alínea "d", da Constituição, o nome do Senhor BRUNO SERRA FERNANDES, para exercer o cargo de Diretor do Banco Central do Brasil.
3                                                                                                                                    Submete à apreciação do Senado Federal, nos termos do art. 84, inciso XIV, combinado com o art. 52, inciso III, alínea d, da Constituição Federal, o nome do Senhor ROBERTO DE OLIVEIRA CAMPOS NETO, para exercer o cargo de Presidente do Banco Central do Brasil.
4 Submete à apreciação do Senado Federal, de conformidade com o art. 52, inciso III, alínea "f", da Constituição Federal, combinado com o art. 6º da Lei nº 6.385, de 7 de dezembro de 1976, com redação dada pelo art. 1º, da Lei 10.411, de 26 de fevereiro de 2002, o nome da Senhora FLÁVIA MARTINS SANT´ANNA PERLINGEIRO para exercer o cargo de Diretora da Comissão de Valores Mobiliários – CVM.
5                                                                                                                                                                 Altera os arts. 7º, 23, 24, 37, 40, 201, 203, 208, 227 e 244 da Constituição Federal para incorporar-lhes a nomenclatura “pessoa com deficiência”, utilizada pela Convenção Internacional sobre o Direito das Pessoas com Deficiência.
6                                                                                                                                                                                           Altera a Lei Complementar nº 105, de 10 de janeiro de 2001, e a Lei n° 12.414, de 9 de junho de 2011, para dispor sobre os cadastros positivos de crédito e regular a responsabilidade civil dos operadores.
  HoraInicio NumeroMateria NumeroSessao Resultado Secreta SequencialSessao
1      14:00             7           15         A       S                4
2      14:00             5           15         A       S                3
3      14:00             2           15         A       S                2
4      14:00             6           15         A       S                1
5      14:00            25           20         A       N                1
6      14:00            54           21       NaN       N                1
  SiglaCasa SiglaCasaMateria SiglaMateria TipoSessao TotalVotosAbstencao
1        SF               SF          MSF        DOR                   1
2        SF               SF          MSF        DOR                   1
3        SF               SF          MSF        DOR                   1
4        SF               SF          MSF        DOR                   1
5        SF               SF          PEC        DOR                 NaN
6        SF               SF          PLP        DOR                 NaN
  TotalVotosNao TotalVotosSim DescricaoObjetivoProcesso
1             3            53                       NaN
2             3            51                       NaN
3             6            55                       NaN
4             6            56                       NaN
5           NaN           NaN                Iniciadora
6           NaN           NaN              Substitutivo

Explorando os dados, é possível observar que as matérias mais antigas que entraram em votação são do ano de 2014. Note como um objeto do Python é utilizado dentro do código R a seguir.

py$sessao %>% 
  count(AnoMateria) %>% 
  kable(align = "c") %>% 
  kable_styling("striped", full_width = FALSE)
AnoMateria n
2014 3
2015 5
2016 1
2017 8
2018 3
2019 113

As proposições são apresentadas por siglas.

  • MPV ou MP: Medida Provisória;
  • MSF: Mensagem ao Senado Federal;
  • OFS: Ofício “S”;
  • PDS: Projeto de Decreto Legislativo;
  • PEC: Proposta de Emenda à Constituição;
  • PLC: Projeto de Lei da Câmara;
  • PLN: Projeto de Lei do Congresso Nacional;
  • PLS: Projeto de Lei do Senado;
  • PLV: Projeto de Lei de Conversão;
  • PRC: Projeto de Resolução da Câmara;
  • PRS: Projeto de Resolução do Senado.

A seguir apresentamos as siglas das matérias de acordo com a frequência observada em 2019.

py$sessao %>% 
  count(SiglaMateria, sort = TRUE) %>% 
  ggplot(aes(reorder(SiglaMateria, n), n)) + 
    geom_col(fill = "#fc035a") + 
    labs(x = NULL, y = "número de sessões") + 
    coord_flip() + 
    theme_bw()

É possível observar que grande parte das sessões é composta de mensagem ao Senado Federal (MSF), proposta de emenda à constituição (PEC) e ofício “S” (OFS).

Votos

A seguir apresentamos algumas informações da base de dados de votos dos senadores:

  Unnamed: 0 CodigoParlamentar NomeParlamentar SexoParlamentar SiglaPartido
1          0              5973       Cid Gomes               M          PDT
2          1              4981    Acir Gurgacz               M          PDT
3          2                90      José Serra               M         PSDB
4          3              2331 Rose de Freitas               F      PODEMOS
5          4               345     Flávio Arns               M         REDE
6          5                35  Jader Barbalho               M          MDB
  SiglaUF                                                            Url
1      CE http://www25.senado.leg.br/web/senadores/senador/-/perfil/5973
2      RO http://www25.senado.leg.br/web/senadores/senador/-/perfil/4981
3      SP   http://www25.senado.leg.br/web/senadores/senador/-/perfil/90
4      ES http://www25.senado.leg.br/web/senadores/senador/-/perfil/2331
5      PR  http://www25.senado.leg.br/web/senadores/senador/-/perfil/345
6      PA   http://www25.senado.leg.br/web/senadores/senador/-/perfil/35
                                                                   Foto
1 http://www.senado.leg.br/senadores/img/fotos-oficiais/senador5973.jpg
2 http://www.senado.leg.br/senadores/img/fotos-oficiais/senador4981.jpg
3   http://www.senado.leg.br/senadores/img/fotos-oficiais/senador90.jpg
4 http://www.senado.leg.br/senadores/img/fotos-oficiais/senador2331.jpg
5  http://www.senado.leg.br/senadores/img/fotos-oficiais/senador345.jpg
6   http://www.senado.leg.br/senadores/img/fotos-oficiais/senador35.jpg
  Tratamento Voto DescricaoVoto                       UrlPaginaParticular
1    Senador   LS Licença saúde                                       NaN
2    Senador   LS Licença saúde               https://acirgurgacz.com.br/
3    Senador   LS Licença saúde                                       NaN
4   Senadora   LS Licença saúde https://www.senadorarosedefreitas.com.br/
5    Senador   LS Licença saúde              http://www.flavioarns.com.br
6    Senador   LS Licença saúde              http://jaderbarbalho.com.br/
  CodigoSessao CodigoSessaoVotacao
1        86315                5945
2        86315                5945
3        86315                5945
4        86315                5945
5        86315                5945
6        86315                5945

Incialmente vamos verificar quais os tipos de votos observados no ano de 2019. As possíveis categorias de votos são:

Abstenção:

  • AP: atividade parlamentar
  • P-NRV: presente não registrou voto
  • Não: votou não
  • Votou: normalmente é indicada quando a votação é secreta
  • LS: licença saúde
  • MIS: presente (em Missão)
  • Presidente (Art. 51 RISF): é voto do presidente que o faz
  • NCom: não compareceu
  • LP: licença particular
  • Abstenção:
  • MERC: presente no Mercosul
  • LN: licença nojo

Uma curiosidade, licença nojo é concedida nos casos de falecimento de cônjuge, companheiro, pais, madrasta ou padrasto, filhos, enteados, menor sob guarda ou tutela e irmãos.

É possível verificar que as categorias com maiores frequências são Votou, Sim e P-NRV.

py$votos %>% 
  count(Voto, sort = TRUE) %>% 
  ggplot(aes(reorder(Voto, n), n)) + 
    geom_col(fill = "#fc035a") + 
    labs(x = NULL, y = "frequência") + 
    scale_y_continuous(labels = scales::label_number_auto()) + 
    coord_flip() + 
    theme_bw()

Abaixo apresentamos o top 10 dos senadores de acordo com número de licenças saúde. Os senadores com maior número de lincenças saúde são Mara Gabrilli e Jarbas Vasconcelos. Note como a leitura do código nesse aspecto é altamente interpretável.

py$votos %>% 
  filter(Voto == "LS") %>% 
  count(NomeParlamentar, sort = TRUE) %>% 
  top_n(n = 10) %>% 
  ggplot(aes(reorder(NomeParlamentar, n), n)) + 
    geom_col(fill = "#fc035a") + 
    labs(x = NULL, y = "frequência de Licença Saúde") + 
    coord_flip() + 
    theme_bw()

Além disso, seria interessante verificar quais senadores apresentam a maior frequência de voto P-NRV (presente – não registrou voto). A senadora Maria do Carmo Alves apresenta a maior frequência de votos nessa categoria.

py$votos %>% 
  filter(Voto == "P-NRV") %>% 
  count(NomeParlamentar, sort = TRUE) %>% 
  top_n(n = 10) %>% 
  ggplot(aes(reorder(NomeParlamentar, n), n)) + 
    geom_col(fill = "#fc035a") + 
    labs(x = NULL, y = "frequência de P-NRV") + 
    coord_flip() + 
    theme_bw()

Comportamento dos senadores

Para verificar quais senadores apresentam comportamentos similares em relação aos votos, para as análises a seguir, consideraremos apenas os votos Sim e Não.

py$votos <- py$votos %>% 
              filter(Voto %in% c("Sim", "Não"))

Primeiro verificaremos quais senadores mudaram de partido nesse período. É possível observar que Jorge Kajuru e Marcos do Val fizeram 2 alterações de partido durante o ano de 2019. Já os senadores Flávio Bolsonaro, Juíza Selma e Reguffe mudaram de partido uma única vez nesse período.

tab_mudancas <- py$votos %>% 
  distinct(NomeParlamentar, SiglaPartido) %>% 
  count(NomeParlamentar) %>% 
  filter(n > 1) %>% 
  arrange(desc(n)) 

tab_mudancas %>% 
  kable(align = "c") %>%  
  kable_styling("striped", full_width = FALSE)
NomeParlamentar n
Jorge Kajuru 3
Marcos do Val 3
Flávio Bolsonaro 2
Juíza Selma 2
Reguffe 2

Como o senador pode mudar de comportamento de acordo com o partido, vamos diferenciar esses períodos. Para isso, nos casos de mudança de partido, adicionaremos o nome do partido após o nome do senador.

py$votos <- py$votos %>%
  mutate(Senador = case_when(NomeParlamentar %in% tab_mudancas$NomeParlamentar ~ paste0(NomeParlamentar, " - ", SiglaPartido),
                     TRUE ~ NomeParlamentar))

No gráfico abaixo apresentamos as menores frequências de votos (sim ou não).

py$votos %>% 
  count(Senador, sort = TRUE) %>% 
  top_n(-10) %>% 
  ggplot(aes(reorder(Senador, -n), n)) +
    geom_col(fill = "#fc035a") + 
    labs(y = "número de votos (sim ou não)", x = NULL) + 
    coord_flip() + 
    theme_bw()

É possível observar que os senadores Prisco Bezerra, Paulo Albuquerque, Luiz Pastore e Marcos do Val (Sem registro) apresentaram apenas um voto (sim ou não) nesse período. Para não trabalhar com senadores com número reduzido de votações, consideraremos apenas senadores com mais de 10 votos.

py$votos <- py$votos %>% 
  inner_join(count(py$votos, Senador, name = "NVotos") %>% 
             filter(NVotos > 10)) 

Uma vez que estamos com os dados filtrados e processados, calcularemos a matriz de dissimilaridades. Essa matriz indicará quais senadores apresentam comportamentos similares em termos de votos (sim e não) no ano de 2019. A medida de dissimilaridade entre dois senadores A e B é dada por:

\[d_{A,B} = 1 - \frac{|A \cap B|}{|A \cup B|},\]

em que \(|A \cap B|\) indica o número de votações que os senadores A e B votaram da mesma forma e \(|A \cup B|\) indica o número de votações que os senadores A e B participaram. Assim, senadores que votam da mesma forma em todas as sessões apresentam dissimilaridade igual a 0. Já senadores que sempre votam de forma diferente, apresentam dissimilaridade igual a 1.

Uma questão técnica para esses dados é que nem todos os senadores votaram em todas as sessões. Dessa forma, a dissimilaridade será calculada entre os senadores com base apenas nas sessões nas quais os dois senadores em questão votaram. Além disso, não podemos calcular a dissimilaridade entre dois senadores enquanto estavam em partidos distintos. Por fim, desconsiderou-se Renilde Bulhões por ser suplente do senador Fernando Collor.

O código abaixo executa o processamento necessário e calcula a matriz de dissimilaridades entre os senadores.

dados_wider <- py$votos %>%
  filter(!(Senador %in% c("Marcos do Val - PODEMOS", "Jorge Kajuru - CIDADANIA", 
                          "Reguffe - PODEMOS", "Juíza Selma - PODEMOS", 
                          "Renilde Bulhões"))) %>% 
  select(Voto, CodigoSessao:Senador) %>% 
  mutate(Voto = case_when(Voto == "Sim" ~ 1, TRUE ~ 0)) %>% 
  pivot_wider(names_from = "Senador", values_from = "Voto") 

nomes <- sort(colnames(dados_wider)[-(1:2)])

dissim <- matrix(0, nrow = length(nomes), ncol = length(nomes)) 

dimnames(dissim) <- list(nomes, nomes)

for (i in 1:(nrow(dissim) - 1)) {
  
  for (j in (i + 1):ncol(dissim)) {
    
    aux <- cbind(dados_wider[, colnames(dados_wider) == rownames(dissim)[i]], 
                 dados_wider[, colnames(dados_wider) == rownames(dissim)[j]]) %>% 
            na.omit()
    
    dissim[i, j] <- 1 - sum(aux[,1] * aux[,2]) / (sum(aux[,1]) + sum(aux[,2]) - sum(aux[,1] * aux[,2]))
      
    dissim[j, i] <- dissim[i, j]
    
  }
  
}

Uma vez que temos a matriz de dissimilaridades calculadas, podemos utilizar o método de escalonamento multidimensional para ter uma visualização das dissimilaridades em um gráfico de duas dimensões (para mais detalhes, ver Data Science, Marketing & Business e Introdução à Análise Exploratória de Dados Multivariados do professor Pedro J. Fernandez). Para isso, utilizaremos a função cmdscale.

mds <- cmdscale(dissim)

resultados <- tibble(senador = rownames(mds), 
                     coord1 = mds[,1], 
                     coord2 = mds[,2]) 

head(resultados)
# A tibble: 6 × 3
  senador              coord1  coord2
  <chr>                 <dbl>   <dbl>
1 Acir Gurgacz       -0.106   -0.0655
2 Alessandro Vieira   0.0286   0.0509
3 Alvaro Dias        -0.00362  0.0794
4 Angelo Coronel      0.142   -0.0453
5 Antonio Anastasia   0.121    0.0114
6 Arolde de Oliveira  0.0856   0.0229

Como os dados foram projetados em duas dimensões, podemos utilizar um gráfico de dispersão para entender as relações entre os senadores. Você pode dar dois cliques no nome do partido na legenda para visualizar apenas os senadores do partido em questão ou um apenas um clique para remover o partido em questão da visualização.

É interessante notar como os senadores do PROS apresentam grande dispersão. Em especial, Fernando Collor apresentam um comportamento muito diferente dos demais.

Os senadores Álvaro Dias e Styvenson Valentim do PODEMOS apresentam um comportamento muito similar. Já os senadores Oriovisto Guimarães e Eduardo Girão (também do PODEMOS) apresentam comportamento parecido entre si, mas diferente da dupla anterior do mesmo partido.

resultados <- resultados %>% 
               left_join(py$votos %>% 
                         select(Senador, SiglaPartido) %>% 
                         distinct(), by = c("senador" = "Senador"))

fig1 <- resultados %>% 
  ggplot(aes(coord1, coord2, color = SiglaPartido, 
             text = paste("Senador: ", senador, '<br>Partido:', SiglaPartido))) + 
    geom_hline(yintercept = 0, color = "grey") + 
    geom_vline(xintercept = 0, color = "grey") + 
    geom_point() + 
    labs(x = NULL, y = NULL, color = NULL) + 
    theme_void() 

ggplotly(fig1, tooltip = "text") 

Os senadores podem ser agrupados de acordo com a posição do gráfico anterior. Lembrando que observações mais próximas indicam que os senadores apresentam comportamento similares nas votações. A seguir apresentamos um dendrograma de agrupamento dos senadores. Destacamos a figura em 4 grupos, mas a leitura pode ser feita de forma geral.

cluster <- hclust(as.dist(dissim))

fviz_dend(cluster, 
          cex = 0.5, 
          k = 4,
          main = "Agrupamento dos Senadores",
          color_labels_by_k = TRUE, 
          horiz = TRUE) + 
  theme_void()

Medidas para os Partidos

Ao visualizar as similaridades dos senadores num gráfico bidimensional é imediata a sugestão de uma medida de coesão dos partidos. Para isso, consideraremos a média da distância de cada senador para o centroide do respectivo partido.

É possível observar que o partido menos coeso é o PROS seguido por PSD e MDB. Já os partidos mais uniformes nas votações são o PP e PL. É importante notar que o PL conta apenas com dois senadores (Jorginho Mello e Wellington Fagundes) nessa base filtrada.

resultados %>% 
  split(.$SiglaPartido) %>% 
  map_dbl(~ mean((.$coord1 - mean(.$coord1))^2 + (.$coord2 - mean(.$coord2))^2)) %>% 
  tibble(partido = names(.), 
         dispersao = .) %>% 
  filter(!(partido %in% c("REPUBLICANOS", "PSC", "S/Partido"))) %>% 
  ggplot(aes(reorder(partido, dispersao), dispersao)) +
    geom_col(fill = "#fc035a") + 
    labs(x = NULL, y = "Dispersão") + 
    coord_flip() + 
    theme_bw()

Uma forma de representar o partido é considerar uma visualização com os centroides dos partidos. Verificamos no gráfico abaixo que o PT apresentam um posicionamento afastado dos demais partidos. Já os partidos PP, PL, PSC, MDB e PSD aparentam uma posição muito próxima.

part_cent <- resultados %>% 
  group_by(SiglaPartido) %>% 
  summarise(cent1 = mean(coord1), 
            cent2 = mean(coord2)) 


fig2 <- part_cent %>% 
  ggplot(aes(cent1, cent2, color = SiglaPartido, 
             label = SiglaPartido)) + 
    geom_hline(yintercept = 0, color = "grey") + 
    geom_vline(xintercept = 0, color = "grey") + 
    geom_text() + 
    labs(x = NULL, y = NULL, color = NULL) + 
    theme_void() +
    theme(legend.position = "none")

ggplotly(fig2, tooltip = "text") 

De forma análoga ao que fizemos agrupando os senadores, podemos agrupar os partidos de acordo com as posições dos centroides. Por exemplo, podemos fazer:

dist_part <- as.matrix(dist(part_cent))

dimnames(dist_part) <- list(part_cent$SiglaPartido, 
                            part_cent$SiglaPartido)

hc <- hclust(as.dist(dist_part))

fviz_dend(hc, 
          cex = 0.6, 
          k = 2,
          main = "Agrupamento dos Partidos",
          color_labels_by_k = TRUE, 
          k_colors = c("blue", "red"),
          horiz = TRUE) + 
  theme_void()

Assim, é possível observar dois agrupamentos distintos. É interessante notar como PT e PROS apresentam um comportamento distantes dos demais. Além disso, conforme observado no gráfico de dispersão anterior, os partidos PP e PL apresentam o comportamento mais similiar.

Conclusão

Nesse post utilizamos linguagem Python e R para analisar dados do Senado Federal. Como uma primeira abordagem, pretende-se mostrar possíveis técnicas que podem ser aplicadas nesse contexto. Pretendemos reproduzir e aprofundar a análise para dados da Câmara dos Deputados.

Caso tenha alguma crítica, sugestão ou comentário, me envie uma mensagem!

Crédito da foto no início do post de Edilson Rodrigues/Agência