name = '2015-02-23-open_data_day'
title = "Para um futuro melhor"
import os
from datetime import datetime
from matplotlib import style
from IPython.core.display import HTML
with open('creative_commons.txt', 'r') as f:
html = f.read()
html = '''
<small>
<p> This post was written as an IPython notebook.
It is available for <a href='https://ocefpaf.github.com/python4oceanographers/downloads/notebooks/%s.ipynb'>download</a>
or as a static <a href='https://nbviewer.ipython.org/url/ocefpaf.github.com/python4oceanographers/downloads/notebooks/%s.ipynb'>html</a>.</p>
<p></p>
%s''' % (name, name, html)
%matplotlib inline
from matplotlib import style
style.use('ggplot')
hour = datetime.utcnow().strftime('%H:%M')
comments="true"
date = '-'.join(name.split('-')[:3])
slug = '-'.join(name.split('-')[3:])
metadata = dict(title=title,
date=date,
hour=hour,
comments=comments,
slug=slug,
name=name)
markdown = """Title: {title}
date: {date} {hour}
comments: {comments}
slug: {slug}
{{% notebook {name}.ipynb cells[1:] %}}
""".format(**metadata)
content = os.path.abspath(os.path.join(os.getcwd(), os.pardir, os.pardir, '{}.md'.format(name)))
with open('{}'.format(content), 'w') as f:
f.writelines(markdown)
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)
Suds ( https://fedorahosted.org/suds/ ) version: 0.4 GA build: R699-20100913 Service ( Proposicoes ) tns="http://www.camara.gov.br/SitCamaraWS/Proposicoes" Prefixes (0) Ports (1): (ProposicoesSoap) Methods (11): ListarProposicoes(xs:string sigla, xs:string numero, xs:string ano, xs:string datApresentacaoIni, xs:string datApresentacaoFim, xs:string idTipoAutor, xs:string parteNomeAutor, xs:string siglaPartidoAutor, xs:string siglaUFAutor, xs:string generoAutor, xs:string codEstado, xs:string codOrgaoEstado, xs:string emTramitacao, ) ListarProposicoesTramitadasNoPeriodo(xs:string dtInicio, xs:string dtFim, ) ListarProposicoesVotadasEmPlenario(xs:string ano, xs:string tipo, ) ListarSiglasTipoProposicao() ListarSituacoesProposicao() ListarTiposAutores() ObterProposicao(xs:string tipo, xs:string numero, xs:string ano, ) ObterProposicaoPorID(xs:string idProp, ) ObterVotacaoProposicao(xs:string tipo, xs:string numero, xs:string ano, ) obterHierarquiaProposicao(xs:string idProposicao, ) obterProposicaoPrincipal(xs:string idProposicao, ) Types (0):
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)
ERROR:suds.client:<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:ns0="http://www.camara.gov.br/SitCamaraWS/Proposicoes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <ns1:Body> <ns0:ListarProposicoes> <ns0:sigla>MPV</ns0:sigla> <ns0:ano>1995</ns0:ano> <ns0:emTramitacao>1</ns0:emTramitacao> </ns0:ListarProposicoes> </ns1:Body> </SOAP-ENV:Envelope> ERROR:suds.client:<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:ns0="http://www.camara.gov.br/SitCamaraWS/Proposicoes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <ns1:Body> <ns0:ListarProposicoes> <ns0:sigla>MPV</ns0:sigla> <ns0:ano>1996</ns0:ano> <ns0:emTramitacao>1</ns0:emTramitacao> </ns0:ListarProposicoes> </ns1:Body> </SOAP-ENV:Envelope>
Impossível baixar ano 1995 Impossível baixar ano 1996
ERROR:suds.client:<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:ns0="http://www.camara.gov.br/SitCamaraWS/Proposicoes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <ns1:Body> <ns0:ListarProposicoes> <ns0:sigla>MPV</ns0:sigla> <ns0:ano>1997</ns0:ano> <ns0:emTramitacao>1</ns0:emTramitacao> </ns0:ListarProposicoes> </ns1:Body> </SOAP-ENV:Envelope>
Impossível baixar ano 1997
ERROR:suds.client:<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:ns0="http://www.camara.gov.br/SitCamaraWS/Proposicoes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <ns1:Body> <ns0:ListarProposicoes> <ns0:sigla>MPV</ns0:sigla> <ns0:ano>1998</ns0:ano> <ns0:emTramitacao>1</ns0:emTramitacao> </ns0:ListarProposicoes> </ns1:Body> </SOAP-ENV:Envelope>
Impossível baixar ano 1998
ERROR:suds.client:<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:ns0="http://www.camara.gov.br/SitCamaraWS/Proposicoes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <ns1:Body> <ns0:ListarProposicoes> <ns0:sigla>MPV</ns0:sigla> <ns0:ano>1999</ns0:ano> <ns0:emTramitacao>1</ns0:emTramitacao> </ns0:ListarProposicoes> </ns1:Body> </SOAP-ENV:Envelope>
Impossível baixar ano 1999
ERROR:suds.client:<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:ns0="http://www.camara.gov.br/SitCamaraWS/Proposicoes" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <ns1:Body> <ns0:ListarProposicoes> <ns0:sigla>MPV</ns0:sigla> <ns0:ano>2000</ns0:ano> <ns0:emTramitacao>1</ns0:emTramitacao> </ns0:ListarProposicoes> </ns1:Body> </SOAP-ENV:Envelope>
Impossível baixar ano 2000
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())
nome | tipoProposicao | ano | orgaoNumerador | regime | apreciacao | autor1 | situacao | |
---|---|---|---|---|---|---|---|---|
id | ||||||||
44997 | MPV 20/2001 | Medida Provisória | 2001 | Urgência | Proposição Sujeita à Apreciação do Plenário | Poder Executivo | Transformado em Norma Jurídica | |
45017 | MPV 19/2001 | Medida Provisória | 2001 | Urgência | Proposição Sujeita à Apreciação do Plenário | Poder Executivo | Transformado em Norma Jurídica | |
44915 | MPV 18/2001 | Medida Provisória | 2001 | Urgência | Proposição Sujeita à Apreciação do Plenário | Poder Executivo | Transformado em Norma Jurídica | |
44913 | MPV 17/2001 | Medida Provisória | 2001 | Urgência | Proposição Sujeita à Apreciação do Plenário | Poder Executivo | Transformado em Norma Jurídica | |
44740 | MPV 16/2001 | Medida Provisória | 2001 | Urgência | Proposição Sujeita à Apreciação do Plenário | Poder Executivo | Transformado em Norma Jurídica |
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')
regime: Urgência apreciacao: Proposição Sujeita à Apreciação do Plenário apreciacao: . tipoProposicao: Medida Provisória autor1: Poder Executivo
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'))))
educação: 8 imposto: 16
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]]))
Autoriza o pagamento de subvenção econômica aos produtores da safra 2011/2012 de cana-de-açúcar e de etanol da região Nordeste e o financiamento da renovação e implantação de canaviais com equalização da taxa de juros; dispõe sobre os arranjos de pagamento e as instituições de pagamento integrantes do Sistema de Pagamentos Brasileiro - SPB; altera a Lei nº 12.783, de 11 de janeiro de 2013, para autorizar a União a emitir, sob a forma de colocação direta, em favor da Conta de Desenvolvimento Energético - CDE, títulos da dívida pública mobiliária federal; e dá outras providências. NOVA EMENTA: Autoriza o pagamento de subvenção econômica aos produtores da safra 2011/2012 de cana-de-açúcar e de etanol que especifica e o financiamento da renovação e implantação de canaviais com equalização da taxa de juros; dispõe sobre os arranjos de pagamento e as instituições de pagamento integrantes do Sistema de Pagamentos Brasileiro - SPB; autoriza a União a emitir, sob a forma de colocação direta, em favor da Conta de Desenvolvimento Energético - CDE, títulos da dívida pública mobiliária federal; estabelece novas condições para as operações de crédito rural oriundas de, ou contratadas com, recursos do Fundo Constitucional de Financiamento do Nordeste - FNE; altera os prazos previstos nas Leis nº 11.941, de 27 de maio de 2009, e nº 12.249, de 11 de junho de 2010; autoriza a União a contratar o Banco do Brasil S.A. ou suas subsidiárias para atuar na gestão de recursos, obras e serviços de engenharia relacionados ao desenvolvimento de projetos, modernização, ampliação, construção ou reforma da rede integrada e especializada para atendimento da mulher em situação de violência; disciplina o documento digital no Sistema Financeiro Nacional; disciplina a regularização de áreas ocupadas por entidades de assistência social, de educação ou templos de qualquer culto no Distrito Federal; disciplina a transferência, no caso de falecimento, do direito de utilização privada de área pública por equipamentos urbanos do tipo quiosque, trailer, feira, banca de venda de jornais e de revistas; altera a incidência da Contribuição para o PIS/Pasep e da Cofins na cadeia de produção e comercialização da soja e de seus subprodutos; altera as Leis nºs 12.666, de 14 de junho de 2012, 5.991, de 17 de dezembro de 1973, 11.508, de 20 de julho de 2007, 9.503, de 23 de setembro de 1997, 9.069, de 29 de junho de 1995, 10.865, de 30 de abril de 2004, 12.587, de 3 de janeiro de 2012, 10.826, de 22 de dezembro de 2003, 10.925, de 23 de julho de 2004, 12.350, de 20 de dezembro de 2010, 4.870, de 1º de dezembro de 1965 e 11.196, de 21 de novembro de 2005, e o Decreto nº 70.235, de 6 de março de 1972; revoga dispositivos da Lei nº 12.546, de 14 de dezembro de 2011; e dá outras providências.
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)
This post was written as an IPython notebook. It is available for download or as a static html.