library(reticulate)
library(knitr)
library(kableExtra)
library(plotly)
library(factoextra)
library(tidyverse)
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:
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
= ["20190101/20190228", "20190301/20190430", "20190501/20190630", "20190701/20190831", "20190901/20191031", "20191101/20191231"]
periods
= pd.DataFrame()
votos
= pd.DataFrame()
sessao
for prd in periods:
= requests.get("http://legis.senado.leg.br/dadosabertos/plenario/lista/votacao/" + prd,
prj = {"Accept": "application/json"}).json()
headers for v in prj["ListaVotacoes"]["Votacoes"]["Votacao"]:
= sessao.append(v, ignore_index = True)
sessao try:
= pd.DataFrame(v["Votos"]["VotoParlamentar"])
df "CodigoSessao"] = v["CodigoSessao"]
df["CodigoSessaoVotacao"] = v["CodigoSessaoVotacao"]
df[= votos.append(df, ignore_index = True)
votos except:
print(v["DataSessao"])
= sessao.drop(columns = ['Votos']) sessao
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.
$sessao %>%
pycount(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.
$sessao %>%
pycount(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.
$votos %>%
pycount(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.
$votos %>%
pyfilter(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.
$votos %>%
pyfilter(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.
$votos <- py$votos %>%
pyfilter(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.
<- py$votos %>%
tab_mudancas 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.
$votos <- py$votos %>%
pymutate(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).
$votos %>%
pycount(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.
$votos <- py$votos %>%
pyinner_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.
<- py$votos %>%
dados_wider 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")
<- sort(colnames(dados_wider)[-(1:2)])
nomes
<- matrix(0, nrow = length(nomes), ncol = length(nomes))
dissim
dimnames(dissim) <- list(nomes, nomes)
for (i in 1:(nrow(dissim) - 1)) {
for (j in (i + 1):ncol(dissim)) {
<- cbind(dados_wider[, colnames(dados_wider) == rownames(dissim)[i]],
aux colnames(dados_wider) == rownames(dissim)[j]]) %>%
dados_wider[, na.omit()
<- 1 - sum(aux[,1] * aux[,2]) / (sum(aux[,1]) + sum(aux[,2]) - sum(aux[,1] * aux[,2]))
dissim[i, j]
<- dissim[i, j]
dissim[j, i]
}
}
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.
<- cmdscale(dissim)
mds
<- tibble(senador = rownames(mds),
resultados 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"))
<- resultados %>%
fig1 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.
<- hclust(as.dist(dissim))
cluster
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.
<- resultados %>%
part_cent group_by(SiglaPartido) %>%
summarise(cent1 = mean(coord1),
cent2 = mean(coord2))
<- part_cent %>%
fig2 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:
<- as.matrix(dist(part_cent))
dist_part
dimnames(dist_part) <- list(part_cent$SiglaPartido,
$SiglaPartido)
part_cent
<- hclust(as.dist(dist_part))
hc
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