Sábado passado foi o Open Data Day e, por pura sorte, acabei tropeçando no projeto dados-abertos-cd do Calango H4cker Clube de Brasília.
O projeto busca, em suas próprias palavras,
Soluções para acesso, exploração e consumo dos Dados Abertos
disponibilizados pela Câmara dos Deputados do Brasil.
Vamos brincar com os dados que esse pessoal incrível colocou online? Seguindo a receita de bolo to próprio projeto vamos usar o módulo suds para criar um cliente e fazer um query nos dados.
from suds.client import Client
url = 'http://www.camara.gov.br/SitCamaraWS/Proposicoes.asmx?wsdl'
client = Client(url, headers={'Content-Type': 'text/xml; charset=UTF-8'})
print(client)
Nunca havia usado o suds antes, mas o objeto suds
parece bem simples
e fácil de usar. Para facilitar a exploração dos dados vamos colocar os
anos de 2011--2014 em um único dataframe do pandas.
import pandas as pd
from suds.client import WebFault
kw = dict(sigla='MPV', numero=None, datApresentacaoIni=None, datApresentacaoFim=None,
idTipoAutor=None, parteNomeAutor=None, siglaPartidoAutor=None, siglaUFAutor=None,
generoAutor=None, codEstado=None, codOrgaoEstado=None, emTramitacao=1)
dfs = []
for ano in range(1995, 2015):
try:
res = client.service.ListarProposicoes(ano=ano, **kw)
props = res['proposicoes']['proposicao']
dfs.append(pd.DataFrame(props))
except WebFault:
print(u"Impossível baixar ano {}".format(ano))
df = pd.concat(dfs, axis=0)
Humm... sem dados antes do ano de 2000 :-(
As duas funções abaixo são apenas "estéticas" uma mostra o dataframe como uma tabela HTML e a outra limpa os dados um pouco.
def to_html(df, css='./data/table.css'):
from IPython.display import HTML
with open(css, 'r') as f:
style = """<style>{}</style>""".format(f.read())
table = dict(style=style, table=df.to_html())
return HTML(u'{style}<div class="datagrid">{table}</div>'.format(**table))
def clean_df(df):
"""Almost clean dataframe."""
columns = {k: tupla[0] for k, tupla in enumerate(df.irow(0))}
df.rename(columns=columns, inplace=True)
df = df.applymap(lambda x: x[1])
df.set_index('id', drop=True, inplace=True)
df['autor1'] = [nome.txtNomeAutor for nome in df['autor1']]
df['regime'] = [unicode(txt[1]) for cod, txt in df['regime']]
df['apreciacao'] = [unicode(apr[1]) for id, apr in df['apreciacao']]
df['orgaoNumerador'] = [unicode(nome[1]) for id, sigle, nome in df['orgaoNumerador']]
df['tipoProposicao'] = [unicode(nome[1]) for id, sigla, nome in df['tipoProposicao']]
df['situacao'] = [unicode(desc[1]) for id, desc, org, princ in df['situacao']]
drop = ['ultimoDespacho', 'qtdAutores', 'numero', 'indGenero', 'qtdOrgaosComEstado', 'datApresentacao']
df.drop(drop, inplace=True, axis=1)
df['ano'] = df['ano'].apply(int)
return df
Esse é o resultado das funções acima:
df = clean_df(df)
textos = df[['txtEmenta', 'txtExplicacaoEmenta']]
df.drop(['txtEmenta', 'txtExplicacaoEmenta'], inplace=True, axis=1)
to_html(df.head())
A primeira informação que descobri explorando esses dados é que o regime, a apreciação, o tipo de proposição e autor (apenas esse eu esperava) são sempre valores únicos.
Todo regime é de urgência, toda proposição está sujeita a apreciação do plenário todos os tipos de proposição são medidas provisórias e o autor é sempre o poder executivo.
def print_unique(col):
for el in df[col].unique().tolist():
print(u'{}: {}'.format(col, el))
print_unique(col='regime')
print_unique(col='apreciacao')
print_unique(col='tipoProposicao')
print_unique(col='autor1')
Como não tenho ainda ideia do que fazer com os dados vamos criar apenas uns gráficos exploratórios. Abaixo temos três histogramas com o número de proposições por ano, o órgão numerador e a situação.
figsize = (7, 2.75)
ax = df[['ano']].apply(pd.value_counts).sort().plot(kind='bar', figsize=figsize)
Vou deixar pra comentar sobre os padrões observados acima em outro post ;-)
ax = df[['orgaoNumerador']].apply(pd.value_counts).plot(kind='barh', figsize=figsize)
Parece que alguém esqueceu de preencher esse dado em algumas entradas... Quase 100 campos vazios.
ax = df[['situacao']].apply(pd.value_counts).plot(kind='barh', figsize=figsize)
Para continuar a exploração dos dados vamos buscar algumas palavras chaves nos textos das propostas.
def find_word(word=u'educação'):
ids = []
for id, row in textos[['txtEmenta', 'txtExplicacaoEmenta']].iterrows():
texto = '\n'.join([row[0], row[1]])
if word in texto:
ids.append(id)
return ids
print('educação: {}\nimposto: {}'.format(len(find_word(word=u'educação')), len(find_word(word='imposto'))))
Temos 8 propostas contendo a palavra educação e o dobro contendo a palavra imposto. (Juro que escolhi aleatoriamente as palavras!)
Caso alguém queira ler os textos basta usar a id
para ler o dataframe.
ids = find_word(word=u'educação')
print(unicode(textos['txtEmenta'].ix[ids[7]]))
Notem que nesse caso eu não escolhi o índice [7]
de forma aleatória,
escolhi um que tem a palavra educação fora do escopo que eu imaginei.
Fiz isso para mostrar que trabalhar com textos é muito complicado! Temos que tomar muito cuidado. Nossa contagem sobre a educação, por exemplo, caiu de 8 para 7 ao olhar os dados de perto.
Caso alguém tenha interesse em mais dados abertos do governo chequem o site http://dados.gov.br/. Para maiores informações sobre o evento do Calango olhem página do evento e o Google Hangout que ocorreu sábado passado:
from IPython.display import YouTubeVideo
YouTubeVideo("8IITQSAHJiQ")
HTML(html)