Neste notebook, vamos analisar dados relativos ao IDEB calculado por município no Brasil. Os dados estão no arquivo
In [51]:
arquivo = "IDEB por Município Rede Federal Séries Finais (5ª a 8ª).xml"
obtido no site dados.gov.br
Como nosso arquivo é um .xml, vamos usar o módulo xml.etree.ElementTree para parsear o conteúdo do arquivo. Vamos abreviar o nome desse módulo por ET.
In [52]:
import xml.etree.ElementTree as ET
Um arquivo XML é um conjunto hierárquico de dados, e portanto a maneira mais natural de representar esses dados é através de uma árvore. Para isso, o módulo ET tem duas classes: a classe ElementTree representa o documento XML inteiro como uma árvore, e a classe Element representa um nó desta árvore. Todas as interações que ocorrem com o documento completo (por exemplo, leitura e escrita no arquivo) são feitas através da classe ElementTree; por outro lado, as interações com um elemento isolado do XML e seus subelementos são feitas através da classe Element.
O método ET.parse retorna uma ElementTree.
In [53]:
tree = ET.parse(arquivo)
Para vermos o elemento raiz da árvore, usamos
In [54]:
root = tree.getroot()
O objeto root, que é um Element, tem as propriedades tag e attrib, que é um dicionário de seus atributos.
In [56]:
root.attrib
Out[56]:
Para acessarmos cada um dos nós do elemento raiz, iteramos nestes nós (que são, também, Elements):
In [57]:
for child in root:
print(child.tag, child.attrib)
Agora que temos uma ideia melhor dos dados a serem tratados, vamos construir um DataFrame do pandas com o que nos interessa. Primeiramente, observamos que somente o último nó nos interessa, já que todos os outros compõem o cabeçalho do arquivo XML. Assim, vamos explorar o nó valores:
In [58]:
valoresIDEB = root.find('valores')
Observe que temos mais uma camada de dados:
In [59]:
valoresIDEB
Out[59]:
In [60]:
valoresIDEB[0]
Out[60]:
Assim, podemos por exemplo explorar os nós netos da árvore:
In [61]:
for child in valoresIDEB:
for grandchild in child:
print(grandchild.tag, grandchild.attrib)
Vamos transformar agora os dados em um DataFrame.
In [62]:
data = []
for child in valoresIDEB:
data.append([float(child[0].text), child[1].text, child[2].text])
In [63]:
data
Out[63]:
Como a biblioteca Pandas está na moda ;) vamos utilizá-la para tratar e armazenar os dados. Mas vamos chamar a biblioteca pandas com um nome mais curto, pd.
In [64]:
import pandas as pd
Inicialmente, criamos um DataFrame, ou seja, uma tabela, com os dados que já temos.
In [65]:
tabelaInicial = pd.DataFrame(data, columns = ["Valor", "Municipio", "Ano"])
In [66]:
tabelaInicial
Out[66]:
Observe que nesta tabela temos dados de 2007 e 2009. Não vamos usar os dados relativos a 2007 por simplicidade.
In [67]:
tabelaInicial = tabelaInicial.loc[0:19]
Na tabelaInicial, os municípios não estão identificados por nome, e sim pelo seu código IBGE. Para lermos o arquivo excel em que temos a tabela dos municípios brasileiros (atualizada em 2014) e seus respectivos códigos de 7 dígitos - os códigos incluem um dígito verificador ao final - usamos o módulo xlrd, que não estará instalado junto com o pandas por definição (você deve instalá-lo manualmente) se quiser executar o comando abaixo. Veja aqui, por exemplo.
In [68]:
dadosMunicipioIBGE = pd.read_excel("DTB_2014_Municipio.xls")
Podemos olhar o tipo de tabela que temos usando o método head do pandas.
In [69]:
dadosMunicipioIBGE.head()
Out[69]:
Como não são todos os dados que nos interessam, vamos selecionar apenas as colunas "Nome_UF" (pois pode ser interessante referenciarmos o estado da federação mais tarde), "Cod Municipio Completo" e "Nome_Município".
In [70]:
dadosMunicipioIBGE = dadosMunicipioIBGE[["Nome_UF", "Cod Municipio Completo", "Nome_Município"]]
Em seguida, precisamos selecionar na tabela completa dadosMunicipioIBGE os dados dos municípios presentes na tabelaInicial contendo os valores calculados do IDEB. Para isso, vamos extrair dos dois DataFrames as colunas correspondentes aos codigos de município (lembrando que nos dadosMunicipioIBGE os códigos contém um dígito verificador que não será utilizado):
In [71]:
listaMunicipiosInicial = tabelaInicial["Municipio"]
listaMunicipios = dadosMunicipioIBGE["Cod Municipio Completo"].map(lambda x: str(x)[0:6])
Observe que usamos acima o método map para transformar os dados numéricos em string, e depois extrair o último dígito.
Agora, ambos listaMunicípiosInicial e listaMunicipios são objetos Series do pandas. Para obtermos os índices dos municípios para os quais temos informação do IDEB, vamos primeiro identificar quais códigos não constam da listaMunicipiosInicial:
In [72]:
indicesMunicipios = listaMunicipios[~listaMunicipios.isin(listaMunicipiosInicial)]
E agora vamos extrair as linhas correspondentes na tabela dadosMunicipioIBGE.
In [73]:
new = dadosMunicipioIBGE.drop(indicesMunicipios.index).reset_index(drop=True)
Por fim, vamos criar uma nova tabela (DataFrame) juntando nome e valor do IDEB calculado na tabelaInicial.
In [74]:
dadosFinais = pd.concat([new, tabelaInicial], axis=1)
A tabela final é
In [75]:
dadosFinais
Out[75]:
Para usarmos gráficos em notebooks, devemos incluir no notebook o comando
% matplotlib inline
ou
% matplotlib notebook
Como isso é geralmente feito usando a primeira célula do notebook, mas no nosso caso não gostaríamos de sacrificar a legibilidade do documento, usamos uma nbextension chamada init_cell para que este comando seja executado na inicialização do notebook (Detalhes)
Primeiramente, vamos importar a biblioteca matplotlib.
In [76]:
import matplotlib.pyplot as plt
Em seguida, vamos substituir os índices da tabela dadosFinais pelos nomes dos municípios listados, já que gostaríamos de fazer um gráfico do valor do IDEB por município.
In [92]:
dadosFinais.set_index(["Nome_Município"], inplace=True)
Finalmente, como nos interessa um gráfico do IDEB por município, só vamos utilizar os dados da coluna "Valor" na tabela dadosFinais (observe que o resultado desta operação é uma Series)
In [82]:
dadosFinais["Valor"]
Out[82]:
Estamos prontos para fazer nosso gráfico.
In [91]:
dadosFinais["Valor"].plot(kind='barh')
plt.title("IDEB por Município (Dados de 2009)")
Out[91]:
O arquivo removeextracode.tpl tem o seguinte conteúdo:
Através da extensão "init_cell" do nbextensions, é possível alterar a ordem de inicialização das células do notebook. Se olharmos os metadados da célula abaixo, veremos que ela está marcada pra ser executada antes de todas as outras células, obtendo-se assim o resultado desejado (esta célula permite que os gráficos da matploblib sejam renderizados dentro do notebook).
In [79]:
%matplotlib inline