GeoDataScience com Python: Analisando Acidentes de Carros nos EUA

Gustavo Bruschi
PowerOfData
Published in
9 min readAug 31, 2020
Imagem de Satélite dos Países Baixos. Fonte: https://www.fugro.com/pt-br/sobre-a-fugro/locations/europa/países-baixos

Este artigo pretende mostrar como realizar análises geoespaciais com a linguagem de programação Python. Vamos introduzir o leitor com os dados do US Accidents.

Sobre o US Accidents:

Este dataset documenta acidentes de carro em 49 estados dos EUA (não contém Alaska e Hawaii). Os dados de acidentes são coletados de fevereiro de 2016 a junho de 2020, usando duas APIs que fornecem streaming de dados de incidentes (ou eventos) de tráfego.

Essas APIs transmitem dados de tráfego capturados por uma variedade de entidades, como os Estados Unidos e departamentos estaduais de transporte, agências de aplicação da lei, câmeras de tráfego e sensores de tráfego nas redes rodoviárias. Atualmente, existem cerca de 3,5 milhões de registros de acidentes neste conjunto de dados.

Análise Exploratória de Dados e Limpeza de dados

A análise exploratória de dados consiste em analisar as características principais de um conjunto de dados geralmente por meio de métodos de visualização e estatísticas resumidas. O objetivo é entender os dados, descobrir padrões e anomalias e verificar a suposição antes de realizarmos avaliações adicionais.

Depois de baixar o arquivo .csv do Kaggle, podemos carregá-lo em um dataframe do Pandas usando a função pandas.read_csv(). E com o método pandas.DataFrame.shapepara ver a quantidade de linhas e colunas.

US_accidents = pd.read_csv(“US_Accidents_June20.csv”)
US_accidents.shape

Agora, visualizamos as primeiras 5 linhas usando o método pandas.DataFrame.head()

US_accidents.head()

Como podemos observar, o dataframe contém 49 colunas, e podemos listá-las com a função `pandas.DataFrame.columns.to_list()`.

US_accidents.columns.tolist()

Dessa forma, vemos as seguintes colunas:

#[‘ID’, ‘Source’, ‘TMC’, ‘Severity’, ‘Start_Time’, ‘End_Time’, ‘Start_Lat’, ‘Start_Lng’, ‘End_Lat’, ‘End_Lng’, ‘Distance(mi)’, ‘Description’, ‘Number’, ‘Street’, ‘Side’, ‘City’, ‘County’, ‘State’, ‘Zipcode’, ‘Country’, ‘Timezone’, ‘Airport_Code’, ‘Weather_Timestamp’, ‘Temperature(F)’, ‘Wind_Chill(F)’, ‘Humidity(%)’, ‘Pressure(in)’, ‘Visibility(mi)’, ‘Wind_Direction’, ‘Wind_Speed(mph)’, ‘Precipitation(in)’, ‘Weather_Condition’, ‘Amenity’, ‘Bump’, ‘Crossing’, ‘Give_Way’, ‘Junction’, ‘No_Exit’, ‘Railway’, ‘Roundabout’, ‘Station’, ‘Stop’, ‘Traffic_Calming’, ‘Traffic_Signal’, ‘Turning_Loop’, ‘Sunrise_Sunset’, ‘Civil_Twilight’, ‘Nautical_Twilight’, ‘Astronomical_Twilight’]

Uma maneira de começar as análises geoespaciais é perceber se o dataset possuí informações georreferenciadas como bairros, cidades, estados, e demais regiões geográficas. Além disso, informações como cep (em inglês zip) e coordenadas geográficas como latitude e longitude também são extremamente importantes.

Aqui, temos um metadados sobre cada coluna. Depois consultem a documentação no site do Kaggle.

Após ler os metadados, notamos que há informações geográficas nas colunas:City , County , State , Zipcode , Start_Lat e Start_Lng.

US_accidents[['ID','City','County','State','Zipcode','Start_Lat','Start_Lng']].head()

Utilizando o GeoPandas para análise geoespacial

GeoPandas é um projeto de código aberto para facilitar o trabalho com dados geoespaciais no Python. O GeoPandas estende os tipos de dados usados pelos Pandas para permitir operações espaciais em tipos geométricos. As operações geométricas são realizadas em cima da biblioteca shapely. E utiliza o fiona para acesso ao arquivo e descartes e matplotlib para plotagem.

Primeiro, vamos importar a biblioteca, e já adiantamos que é melhor instalar o pacote em seu Anaconda Prompt com o seguinte comando conda install --chanel conda-forge geopandas , pois ele garante a instalação e atualização das dependências necessárias.

Após instalar e importar a biblioteca, podemos importar um arquivo shapefile (.shp), que é uma extensão comum em problemas de análise geoespacial, uma vez que consegue armazenar os dados como formas geométricas. Baixamos um arquivo contendo todos os 51 estados dos EUA neste site.

Utilizamos o método geopandas.read_file() para ler o .shp e geopandas.GeoDataFrame.shape para ver a quantidade de linhas e colunas. Bem similar ao Pandas.

import geopandas as gpdUS_states = gpd.read_file('USA_States.shp')
US_states.shape

E na mesma linha do Pandas, podemos inspecionar as primeiras linhas do arquivo com o método geopandas.GeoDataFrame.head() .

US_states.head()

Na coluna geometry, há a geometria relacionada ao respectivo estado. Que são formas utilizadas para representar algum componente espacial de características geográficas com limites discretos de municípios e estados, bem como poços, rios, estados, ruas, quadras, etc.

As geometrias podem ser pontos, linhas e polígonos. No caso do nosso GeoDataFrame , cada ponto que forma o polígono de cada estado é representado por um par de coordenadas (x, y) que são latitudes e longitudes. Abaixo, mostro um exemplo prático do estado de Montana:

US_states['geometry'][1]

Outro ponto importante ao analisar dados geoespaciais é se atentar ao Sistema de Referência de Coordenadas (SRC), ou Coordinate Reference System (CRS), que é a maneira como os dados são representados.

Todas as representações da Terra, seja em sua totalidade ou parcialmente, ocorrem em uma superfície plana. O que acaba por gerar distorções, pois nosso planeta não é plano (como algumas pessoas acham) e sim um geoide, que após uma série de transformações matemáticas, é convertido no formato de elipsoide que aprendemos na escola.

Fonte: Geoide, o formato original da Terra. Em: Instituto Brasileiro de Estatística e Geografia (IBGE). Introdução à Cartografia.

Um exemplo clássico é o das projeções de Peters (A) e Mercator (B), que como podemos notar, distorcem consideravelmente as áreas dos países.

Podemos atribuir um CRS com o método geopandas.GeoDataFrame.crs e depois inspecioná-lo. Vamos utilizar o CRS WGS 84, que possuí o código EPSG 4326. Podemos procurar os padrões disponíveis nos sites abaixo:

US_states.crs = ('epsg:4326')
US_states.crs

Agora, vamos fazer nosso primeiro mapa com o método geopandas.GeoDataFrame.plot() . E de cara, vamos definir seus tamanhos com o comando figsize

US_states.plot(figsize=(10,10))

Olha que massa, conseguimos plotar (ainda que simplérrimo) nosso primeiro mapa.

Mas e a análise dos acidentes ?

Sim pessoal, não esqueci daquele dataset com os 3,5 milhões de acidentes.

Lembrem-se que aquele arquivo foi importado como um DataFrame , agora nós podemos convertê-lo em um GeoDataFrame através de seu par de coordenadas com geopandas.GeoDataFrame() .

Utilizamos geopandas.points_from_xy()para transformar Longitude e Latitude em uma lista de objetos shapely . E assim configurá-la como uma geometria ao criar o GeoDataFrame .

import geopandas as gpdgeo_US_accidents = gpd.GeoDataFrame(US_accidents, geometry=gpd.points_from_xy(US_accidents.Start_Lng, US_accidents.Start_Lat))geo_US_accidents.crs = (‘epsg:4326’)
geo_US_accidents.head()

Note que ao final do dataset, há uma coluna contendo a geometria de cada acidente, definida por pares de coordenadas (x, y) que formam pontos.

E o CRS que definimos para cada acidente, é similar ao do shapefile dos estados que importamos

geo_US_accidents.crs

Agora, vamos realizar nossa primeira plotagem. Vamos plotar uma amostra de 50.000 acidentes do dataset já 3,5MM pode demorar muito.

geo_US_accidents.sample(50000).plot(figsize=(12,12))

Que bacana, conseguimos ver com um plot de 50.000 pontos de acidentes no território dos EUA praticamente formam o contorno das fronteiras do país.

Agora, vamos plotar sobrepor as camadas do mapa com os limites dos estados do objeto US_statescom o mapa de acidentes em geo_US_accidentes. Lembrem-se que não há acidentes no Alaska e no Hawaii, então bora removê-los.

### Remover Alaska e Hawaii ###US_states_new = US_states[US_states[‘STATE_NAME’]!=’Alaska’]
US_states_new = US_states_new[US_states_new[‘STATE_NAME’]!=’Hawaii’]
US_states_new.shape

Pronto, agora há 49 estados em nosso mapa de fronteiras. E vamos criar um novo objeto com uma amostra de 50.000 acidentes.

geo_US_accidents_sample = geo_US_accidents.sample(50000)

Agora, vamos plotar os acidentes de acordo com seu nível de severidade, que está na coluna Severity .

Para este mapa, utilizamos métodos do matplotlib como o subplots para combinar as duas camadas: limites territoriais e acidentes.

### Plotar Mapa Básico - Visualizar "Severity" ###fig, ax = plt.subplots(1, 1, figsize=(12,12))US_states_new.plot(ax= ax)geo_US_accidents_sample.plot(ax= ax,
figsize=(12,12),
column='Severity')

Uau, que visualização legal né ? Mas eaí, você consegue interpretar os pontos nesse mapa ? Você saberia informar quais são os acidentes mais e menos severos no mapa ? Acho que não né ?

No próximo mapa, vamos alterar algumas coisas, como o a cor da fronteira entre os estados, através do edgecolor e vamos adicionar uma legenda para facilitar a vida do leitor através do legend=True e vamos utilizar o legend_kwds para editar os textos.

### Plotar Mapa Básico - Visualizar "Severity" ###fig, ax = plt.subplots(1, 1, figsize=(12,12))US_states_new.plot(ax= ax,
edgecolor='white')
geo_US_accidents_sample.plot(ax= ax,
figsize=(12,12),
column='Severity',
legend=True,
legend_kwds={'label': "Severidade dos Acidentes",
'orientation': "horizontal"})

Com apenas poucos argumentos adicionais, pudemos melhorar incrivelmente nosso mapa. Mas isso não para por aí. Vamos editar aspectos estéticos da visualização com todo o arsenal que o matplotlib nos oferece.

Agora, vamos mudar as cores dos elementos do mapa com o padrão color-hex

### Plotar Mapa Básico - Visualizar "Severity" ###fig, ax = plt.subplots(1, 1, figsize=(12,12))US_states_new.plot(ax=ax,
color='#e1faf4',
alpha=0.6,
edgecolor="#99cfc2",
linewidth=0.6)
geo_US_accidents_sample.plot(ax= ax,
figsize=(12,12),
column='Severity',
legend=True,
legend_kwds={'label': "Severidade dos Acidentes",
'orientation': "horizontal"})

A mudança em cima da camada dos Estados foi muito boa, porém ficou dificil visualizar a severidade dos acidentes. Vamos mudar isso definindo uma nova escala de cor com o argumento cmap .

### Plotar Mapa Básico - Visualizar "Severity" ###fig, ax = plt.subplots(1, 1, figsize=(12,12))US_states_new.plot(ax=ax,
color='#e1faf4',
alpha=0.6,
edgecolor="#99cfc2",
linewidth=0.6)
geo_US_accidents_sample.plot(ax= ax,
figsize=(12,12),
column='Severity',
cmap='cool',
legend=True,
legend_kwds={'label': "Severidade dos Acidentes",
'orientation': "horizontal"})

BLÉ ! Péssima escolha de paleta de cores, agora entramos numa iteração um pouco artesanal e lenta, mas que pode gerar resultados incríveis.

### Plotar Mapa Básico - Visualizar "Severity" ###fig, ax = plt.subplots(1, 1, figsize=(12,12))US_states_new.plot(ax=ax,
color='#e1faf4',
alpha=0.6,
edgecolor="#99cfc2",
linewidth=0.6)
geo_US_accidents_sample.plot(ax= ax,
figsize=(12,12),
column='Severity',
cmap='Reds',
legend=True,
legend_kwds={'label': "Severidade dos Acidentes",
'orientation': "horizontal"})
ax.axis("off")
plt.show()

Agora sim ! A paleta Reds se destaca muito bem e transmite muito melhor a severidade de um acidente de automóvel com a transição dos tons de vermelho. E com ax.axis('off') nós removemos os marcações dos eixos x e y para deixar a visualização mais limpa.

Considerações Finais

Presumo que se o leitor chegou até aqui, imagino que esteja interessado em conhecer mais sobre GeoDataScience. Este foi um simples exemplo de como utilizar poucas bibliotecas pode gerar um ótimo resultado. E ainda assim, alerto ao leitor que podemos fazer muito mais.

Há mais bibliotecas para manipulação de dados, visualização (estática como a que mostrei hoje e interativas com dashboards), e até modelagem estatística e com machine learning.

Futuramente, escreverei mais sobre esse tipo de aplicação tecnológica fascinante, que gera valor em diferentes áreas de negócios.

Esse é um dos primeiros artigos do time PowerOfData. Convido você a acompanhar os demais posts do time aqui no Medium, em nosso LinkedIn, e se quiser entrar em contato com nossa equipe.

Notas:

  • Sobre o dataset utilizado, ver estes links:

Moosavi, Sobhan, Mohammad Hossein Samavatian, Srinivasan Parthasarathy, and Rajiv Ramnath. “A Countrywide Traffic Accident Dataset.”, 2019.

Moosavi, Sobhan, Mohammad Hossein Samavatian, Srinivasan Parthasarathy, Radu Teodorescu, and Rajiv Ramnath. “Accident Risk Prediction based on Heterogeneous Sparse Data: New Dataset and Insights.” In proceedings of the 27th ACM SIGSPATIAL International Conference on Advances in Geographic Information Systems, ACM, 2019.

--

--