Limpando Dados do OpenStreetMap - Região Metropolitana de São Paulo

Vagner Sanches Vasconcelos

Resumo:

Neste projeto são utilizados técnicas de tratamento e análise de dados da base do projeto OpenStreetMap especificamente da Região Metropolitana de São Paulo (RMSP); para isso foi utilizado a linguagem de programação Python e o banco de dados MongoDB.

1.0) INTRODUÇÃO

Basicamente o processo de análise de dados parte de uma questão ou um problema ao qual deseja-se a resposta; contudo, para chegar a ela, antes é necessário passar pela fase de preparação ou tratamento dos dados (data wrangling ou data munging), que envolve: obtenção (gathering), extração (extracting), limpeza (cleaning) e armazenamento (storing) dos dados.
Após a fase de preparação ocorre efetivamente a análise dos dados, onde estes são explorados de forma a buscar as respostas as questões/problemas que deseja-se responder; ao fim desta análise, conclusões são desenvolvidas e finalmente apresentadas em relatórios.
Segundo o NYT, o processo de preparação dos dados é uma tarefa que ocupa entre 50 e 80% do tempo do analista.

1.1) Questões de Pesquisa

Todas as questões abaixo se referem aos dados que serão carregados no banco de dados MongoDB.

1.1.1) Qual o tamanho da base?

1.1.2) Qual o número de usuários únicos na base?

1.1.3) Quais os 3 usuário com maior número de contribuições?

1.1.4) Qual o número de nós e caminhos encontrado na base?

1.1.5) Qual o número de nós do tipo "Ponto de Ônibus"?

2.0) TRATAMENTO DOS DADOS

2.1) Obtenção dos Dados

Os dados para realização deste projeto foram obtidos da fonte secundária OpenStreetMap, que é um projeto de produção colaborativa de dados geoespaciais abertos, no qual qualquer pessoa pode editar o mapa e os dados são redistribuídos sob a licença ODbL.
A RMSP reúne 39 municípios do estado de São Paulo, sendo a maior região metropolitana do Brasil, e uma das dez mais populosas do mundo. RNA

2.2) Extração dos Dados

A delimitação da RMSP pode ser acessada no OpenStreetMap; contudo, o portal MapZen já possui a base de dados dela pré-selecionada e pronta para download, em vários formatos, sendo o utilizado neste trabalho o Raw OpenStreetMap datasets (XML); os dados do MapZen são oriundos do OpenStreetMap. Neste link estão os dados da RMSP, num arquivo compactado (bz2) de 50MB, que após descompactado gera o arquivo sao-paulo_brazil.osm com 764MB. A documentação deste arquivo pode ser acessada aqui.

2.3) Limpeza dos Dados

Esta etapa foi realizada seguindo as melhores práticas de limpeza de dados (blueprint) conforme Bradshaw.

2.3.1) Auditoria dos Dados

Conforme já apresentado, o conjunto de dados deste trabalho possui 764MB; para facilitar o processo de auditoria, começaremos o trabalho analisando uma pequena amosta da base, assim a iteração na investigação será feita mais rapidamente; após validado este processo, ele será aplicado em toda a base.
A amostra da base foi obtida por meio do programa AmostraBase.ipynb, sendo que seu tamanho ficou em 77,5MB.

a) Auditanto os tipos de vias

Inicialmente serão auditados os tipos de vias, tais como: rua; avenida; alameda; praça, etc. Conforme documentação, os elementos node e way utilizam a tag addr:street com a finalidade de registrar o tipo de via com seu respectivo nome.
O trecho da base de dados abaixo ilustra o exemplo da Rua João Boemer.

In [ ]:
<node changeset="45082900" id="4602264559" lat="-23.5312944" lon="-46.6132484" timestamp="2017-01-11T18:12:06$
                <tag k="addr:city" v="São Paulo" />
                <tag k="addr:housenumber" v="1117" />
                <tag k="addr:postcode" v="03018-000" />
                <tag k="addr:street" v="Rua João Boemer" />
</node>

Para obtenção dos nomes das vias e seus respectivos tipos, foi utilizado o módulo de expressões regulares (re) do Python; sendo este instalado com o seguinte comando: pip install regex. </p> O programa TiposDeVias.ipynb busca os tipos de vias encontrados na base bem como suas quantidades; e conforme a sua saída, apresentada abaixo, na amostra da base foram encontrados 29 tipos de via, distribuídos em 2353 vias, sendo Rua o tipo de maior frequência com 1530 registros, seguido por Avenida com 694 registros. Contudo, temos por exemplo, os registros rua, R. e Av. que respectivamente devem ser os dois registros mais encontrados, mas por desejo dos usuários foram lançados desta forma.

(Nome da via: Quantidade de registros): </p> Acesso: 1 , Alameda: 26, Alfonso: 1, Av.: 2, Avenida: 694, Calçadão: 1, Complexo: 1, Conselheiro: 1, Dr.: 1, Estrada: 27, Guarara: 1, Ladeira: 1, Largo: 8, Marginal: 1, Passeio: 1, Praça: 21, R.: 3, Rocha: 1, Rodoanel: 1, Rodovia: 11, Rua: 1530, rua: 1, sao: 1, Tavares: 1, Travessa: 9, Via: 3, Viaduto: 1, Viela: 1, Vila: 2

Fazendo buscas manuais pelos tipos de vias na base de dados, ex.Alfonso - conforme trecho do arquivo abaixo - se observa que o usuário não lançou um tipo de via para esta; isso aconteceu para outros casos, tais como: Tavares, Rocha, Guarara, Dr. e Conselheiro

In [ ]:
                <tag k="name" v="Q Pizza" />
                <tag k="amenity" v="fast_food" />
                <tag k="addr:city" v="São Paulo" />
                <tag k="addr:street" v="Alfonso Bovero" />
                <tag k="addr:housenumber" v="724" />

Com os tipos de via reconhecidos, o próximo passo foi auditá-los, identificando todos os tipos de via estranhos presente na base; o programa AuditaTiposDeVias.ipynb realiza essa identificação e modifica os tipos de vias, conforme exemplo abaixo:

   Av. Francisco Nóbrega Barbosa => Avenida Francisco Nóbrega Barbosa
   R. Bergamo => Rua Bergamo

b) Auditanto os tags

A próxima auditoria realizada foi em relação a todos os tags; o programa AuditaTags.ipynb, examina cada um deles verificando e classificando eventuais desvios, sendo estes:
lower: Para tags válidas com somente letras minúsculas;
lower_colon: Para tags válidas com dois-pontos em seus nomes;
problemchars: Para tags com caracteres problemáticos; e
other: Para todos os casos que não se enquadram nas 3 outras categorias.

In [ ]:
{'lower': 139602, 'lower_colon': 10452, 'other': 120, 'problemchars': 1}

Conforme a saída deste programa, apresenta acima, 121 tags apresentaram desvios.

Ainda com relação aos tags, o programa ContagemDosTags.ipynb realiza a identificação e a contagem destes; abaixo segue a saída do programa, com os tags identificados bem como suas quantidades.

{'member': 6841, 'nd': 458477, 'node': 338061, 'osm': 1, 'relation': 1010, 'tag': 150175, 'way': 46939}

c) Auditanto os usuários da base

Agora serão auditados os usuários da base, o programa AuditaUsuarios.ipynb reconhece as identificações dos usuários na base. Abaixo é apresentado o trecho inicial da saída do programa, a qual apresenta que foram identificados 1233 usuários e mostrando suas identificações (uid).

In [ ]:
1233
set(['102069', '1385426', '1886529', '1866562', '2678586', '967832', '152074', '1425865', '2032261', '526341', ...

d) Auditanto os CEPs

Os CEPs foram auditados inicialmente pela métrica de qualidade da validade; verificando se esses estão de acordo com o padrão de 8 digitos definidos pelos Correios. O programa AuditaCEP.ipynb utiliza expressões regulares para validar os CEPs.
Abaixo são apresentados alguns problemas encontrados na base de dados:

06719500 => com problema
09691000 => com problema
09890 070 => com problema

Conforme saídas acima, basicamente os problemas encontrados foram: i) CEPs sem a presença do hífem; e ii) No lugar do hífem foi digitado um espaço.

Uma outra métrica utilizada para auditoria dos CEPs foi a acurácia, isto é, até que ponto os CEPs seguem os gold standard, neste caso a base de dados dos Correios . Para isso foi utilizado o pacote python-cep que basicamente é um screen-scraper do site dos Correios.

O programa AuditaCEP-rev2.ipynb lê os CEPs da base de dados e busca no site dos Correios se este existe.
Abaixo são apresentados um exemplo de um CEP que existe e os três CEPs que não existem na base de dados dos Correios.

04094-050 04094050
{'bairro': u'Parque Ibirapuera',
'cep': u'04094-050',
'localidade': u'S\xe3o Paulo/SP',
'logradouro': u'Avenida Pedro \xc1lvares Cabral'}

CEP 09771-000 Nao existe
CEP 09898-003 Nao existe
CEP 03711-000 Nao existe

2.4) Armazenamento dos Dados

O armazenamento dos dados foi realizado no MongoDB, que é um banco de dados orientados a documentos do tipo JSON; assim, para subir os dados do OpenStreetMap, que são arquivos XML, para o MongoDB é necessário realizar a conversão de XML para JSON, o que foi realizado com o programa Xml2Json-rev2.ipynb.
Além de fazer a conversão, este programa realiza a padronização dos tipos de via conforme feito no programa AuditaTiposDeVias.ipynb mais especificamente na função update_name.
A saída deste programa foi um arquivo JSON de 86,6MB, isto é, maior que o arquivo XML que o originou (77,5MB). Abaixo é apresentado uma linha deste arquivo de saída.

In [ ]:
{"created": {"changeset": "16953747",
             "user": "fbello",
             "version": "3", 
             "uid": "79543", 
             "timestamp": "2013-07-14T19:50:01Z"},
 "type": "node",
 pos": [-23.5594684, -46.7072388],
 "id": "573640"} 

O arquivo JSON foi carregado no MongoDB utilizando o comando mongoimport -d sample-osm -c rmsp --file sample.json - no prompt do sistema operacional - no qual: sample-osm, é nome da base da dados; e rmsp é o nome da coleção.

3) ANÁLISE DOS DADOS

Qual o tamanho da base?

show dbs

(0.203GB)

Qual o número de documentos da base?

db.rmsp.find().count()

385000

Qual o número de usuários únicos na base?

db.rmsp.distinct('created.user').length

1233

Quais os 3 usuários com maior número de contribuições?

In [ ]:
db.rmsp.aggregate([{"$group": {"_id":"$created.user","count":{"$sum":1}}},{"$sort":{"count":-1}},{"$limit":3}
 
{ "_id" : "Bonix-Mapper", "count" : 171080 }
{ "_id" : "AjBelnuovo", "count" : 21330 }
{ "_id" : "cxs", "count" : 19876 }

Qual o número de nós e caminhos encontrado na base?

Número de nós (node):

db.rmsp.find({"type":"node"}).count() 

338059

Número de caminhos (way):

 db.rmsp.find({"type":"way"}).count()

46935

Qual o número de nós com o nome de "Ponto de Ônibus"?

db.rmsp.find({name:{$regex:"ponto de [oôó]nibus",$options: "$i"}}).count()

4

Quais as 10 amenities que mais apareceram?

db.rmsp.aggregate([{"$match":{"amenity":{"$exists":1}}},{"$group":{"_id":"$amenity","count":{"$sum":1}}},{"$sort":{"count":-1}},{"$limit":10}])


{ "_id" : "parking", "count" : 303 } { "_id" : "fuel", "count" : 186 } { "_id" : "restaurant", "count" : 128 } { "_id" : "school", "count" : 117 } { "_id" : "bank", "count" : 103 } { "_id" : "fast_food", "count" : 71 } { "_id" : "place_of_worship", "count" : 69 } { "_id" : "pharmacy", "count" : 52 } { "_id" : "pub", "count" : 34 } { "_id" : "hospital", "count" : 30 }

4.0) CONCLUSÕES

O presente trabalho apresentou um processo estruturado de tratamento e análise de dados, no qual foi possível identificar problemas na base em análise e posteriormente tratá-los, conseguindo assim uma visão geral deste conjunto de dados.

Como sugestão de trabalhos futuros, as bases de dados do projeto OSM da RMSP poderiam ser relacionadas com a da Pesquisa de Origem e Destino Pesquisa OD coordenada pelo Metrô de SP em colaboração com várias outras instituições interessadas.
Com isso, seria possível aplicar o modelo de previsão de demanda de passageiros de metrô utilizando Redes Neurais Artificiais (RNA), proposto por VASCONCELOS, possibilitando a previsão de passageiros, de forma dinâmica, de uma nova estação de metrô/trem inserida na RMSP. O modelo em questão utiliza 4 variáveis de entrada para a predição da demanda de passageiros, conforme figura abaixo, que por sua vez servem como base para estudos econômico-financeiros destes projetos. Na Pesquisa OD, a RMSP é subdividida em 460 zonas de tráfego, assim, para cada uma destas zonas seriam incluídos os valores das 4 entradas da RNA.

RNA

Um dos problemas nesta implementação seria quando uma estação ficasse entre duas ou mais zonas de tráfego, nestas condições quais dados seriam utilizados na entrada da RNA?
Este é um exemplo de definições de deveriam ser tratadas.