4 bibliotecas para Mapas no Python: Altair, Bokeh,Folium e Plotly

4 bibliotecas para Mapas no Python: Altair, Bokeh,Folium e Plotly

Em 25 de Fevereiro de 2020 foi registrado o primeiro caso de Covid-19 no Brasil, no Estado de São Paulo. Em 05 de Março deste mesmo ano foi confirmado um caso no Rio de Janeiro, e no dia seguinte – 06 de março – foi confirmados casos na Bahia e Espirito Santo. Em 11 de Março de 2020 a Organização Mundial de Saúde decretou Pandemia de Covid-19 e em 17 de março foi registrado o primeiro óbito no Brasil no estado de São Paulo. Somente quase 1 ano depois do primeiro caso registrado foi que iniciou a vacinação no Brasil.

Na data da escrita deste post, o Brasil já havia vivido 4 ondas de Covid-19, sendo registrados mais de 34 milhões de casos e quase 700 mil óbitos devido a complicações do Covid-19, de acordo com o site do ministério da saúde (https://covid.saude.gov.br/).

Neste post, vamos descrever passo a passo para criar mapa com a taxa de mortalidade devido à complicação por Covid-19 no Brasil, utilizando 4 diferentes bibliotecas do Python: Altair, Bokeh, Folium e Plotly.


O conjunto de dados, oriundo dos registros de casos e mortes por Covid19, divulgados pelas Secretarias Estaduais de Saúde e do Ministério da Saúde do Brasil, os quais foram sistematizados pelo pesquisador Wesley Cota e disponibilizado, com atualização diária, em sua página no Github (https://github.com/wcota/covid19br/).

Fiz a extração dos dados no dia 11/08/2022 e disponibilizei o conjunto de dados em um arquivo no formato *.csv , separado por ‘,’ , em um repositório de dados abertos que mantenho em meu GitHub – jonates/opendata.

Nesses conjuntos de dados, cada instância é um registro de uma taxa de desocupação em determinada unidade da federação do Brasil em determinado trimestre de um dado ano, e possui 3 atributos:

  • country – País;
  • state – Unidade da Federação do Brasil;
  • city – Nome do Município.
  • ibgeID – Código IBGE do Município.
  • deaths – Total de mortes até a última apuração.
  • totalCases – Total de casos até a última apuração.
  • deaths_per_100k_inhabitants – Mortes por 100 mil habitantes até a última apuração.
  • totalCases_per_100k_inhabitants – Casos por 100 mil habitantes até a última apuração.
  • _source – Fonte dos dados.
  • date – Data da divulgação.
  • newCases – Casos novos no dia da apuração.
  • newDeaths -Óbitos novas no dia da apuração.
  • last_info_date– dia da apuração.

Neste post vamos utilizar o atributo total de mortes por 100 mil habitantes até a data da apuração, e o código IBGE do município.

Para realizar o Cartograma, precisamos do arquivo georreferenciado que delimita os municípios do Brasil. Este arquivo em formato *.json também se encontra disponível no repositório opendata do meu GitHub.

Então! Animado para meter as mãos no código e construir o mapa? Vamos lá!


Primeiro, acesse o Google Colab e crie um novo notebook.

Para carregar e fazer transformações no conjunto de dados, vamos utilizar a biblioteca Pandas. Portanto, precisamos importá-la:

# Importando bibliotecas necessárias
import pandas as pd

Em seguida, é preciso Carregar o conjunto de dados no Python. Para tanto utilizamos o read_csv do pandas:

# Importando o conjunto de dados
covid = pd.read_csv(
    filepath_or_buffer = 'https://raw.githubusercontent.com/jonates/opendata/master/covid19_brasil/cases-brazil-cities-20220811.csv', 
    sep=',',
    decimal='.'
)

Pronto! Vamos dar uma espiadinha no conjunto de dados bem como em sua estrutura:

# Espiando o conjunto de dados
covid.tail()

Agora, precisamos fazer o download do arquivo geoespacial para dentro do Google Colab:

# Fazendo download do conjunto de dados
!wget --verbose --show-progress --no-check-certificate https://raw.githubusercontent.com/jonates/opendata/master/arquivos_geoespaciais/geojs-100-mun.json

Finalizado o download, carregamos o arquivo no formato *.json (é precisar importar a biblioteca json):

# Importando biblioteca necessária
import json

# Abrindo o JSON e guardando em um objeto Python
objeto_geo_cidades = open('/content/geojs-100-mun.json',)

# Lendo o arquivo georreferenciado no formato JSON
geo_uf = json.load(objeto_geo_cidades) 

Para entender a estrutura deste arquivo *.json com informações geoespaciais dos municípios do Brasil, espiamos a instância relativa ao primeiro município do json:

# Espiando a estrutura do json
geo_cidades['features'][1]

Veja que a estrutura *.json relativa ao DF contém as coordenadas com as latitudes e longitudes que definem o seu polígono; e outras propriedades como Código IBGE do município (id), Nome do município (name). Atente-se ao fato de que a properties.id que é o código IBGE do município será de suma importância pois será através dela que vamos ligar o objeto de informações geoespaciais com o dataframe que contém os dados de taxa de mortalidade.

Antes de fazer o mapa, é preciso fazer duas transformações no dataframe covid:

  1. renomear o atributo ibgeID para id para ficar igual ao objeto com informações geoespaciais.
  2. transformar o atributo id para o tipo object;
# Renomeando o ibgeID para id igual ao do arquivo geoespacial
covid = covid.rename(columns={'ibgeID':'id'})

# transformando o 'id' para tipo object, o mesmo do geoespacial
covid['id'] = covid['id'].astype('str')

# Espiando resultado da estrutura
covid.info()

Construindo o mapa da taxa de mortalidade com Plotly

Para construir o mapa, utilizamos a biblioteca Plotly que precisa ser importada:

# Importando biblioteca necessaria
import plotly.express as px

Com o dataframe e o objeto geoespacial totalmentes compatíveis, já é possível fazer o mapa utilizando o método choropleth_mapbox() da biblioteca plotly express:

# Criando o mapa
mapa_mortalidade_covid = px.choropleth_mapbox(
    data_frame = covid, 
    geojson = geo_cidades, 
    locations='id', 
    featureidkey='properties.id',
    color='deaths_per_100k_inhabitants',
    color_continuous_scale= 'reds',
    range_color=(150, 450),
    mapbox_style='open-street-map',
    zoom=2.5, 
    center = {"lat": -17.14, "lon": -57.33},
    opacity=1,
    labels={'deaths_per_100k_inhabitants':'Tx. Mortalidade (/ 100 mil hab.)', 
            'id' : 'Código do município'
    },
    width = 1000,
    height = 800,
    title = 'Taxa de Mortalidade por Covid-19, por municípios, 11/08/2022'
)

# Ajustando as margens
mapa_mortalidade_covid.update_layout(margin={'r':0,'t':0,'l':0, 'b':0})

# Reduzindo a largura das bordas dos municípios
mapa_mortalidade_covid.update_traces(marker_line_width=0.01)

# Exibindo o mapa
mapa_mortalidade_covid.show()

Pronto! Mapa gerado com sucesso, com o popup funcionando, de modo que ao passar o mouse por cima do mapa, o código do município e a taxa de mortalidade aparecem em uma janela popup.


Uso do GeoPandas para construir mapas com outras bibliotecas

Além do Plotly, outras bibliotecas para elaboração de visualização de dados também podem ser utilizadas para criação de mapas cloropléticos: Folium, Bokeh e Altair. Porém, diferente da plotly, essas 3 necessitam que o arquivo geoespacial contenham também os dados, portanto, precisamos utilizar o geopandas para carregar o arquivo geoespacial e juntar com o arquivo de dados.

# Instalando o Geopandas
!pip install geopandas

Uns 10 segundinhos a biblioteca já vai estar instalada. Importe-a:

# Importando biblioteca necessaria
import geopandas as gpd
import json
# Pegando o geojson
url_geojson = '/content/geojs-100-mun.json'
geo_cidades = gpd.read_file(url_geojson)
# Verificando o tipo de objeto gerado
print( type(geo_cidades) )
# Espiando o objeto
geo_cidades.head()
# Espiando a estrutura do objeto geoespacial
geo_cidades.info()

Para que o dataframe contendo os dados de covid-19 consiga “se comunicar” com o arquivo geoespacial, é preciso que haja nos dois arquivos um atributo similar que servirá como chave primária. Portanto, vamos dar uma espiada novamente na estrutura do conjunto de dados de covid-19.

# Espiando a estrutura do objeto
covid.info()

Perceba que o atributo “id” que será a chave primaria, é do tipo object tanto no dataframe com os dados de covid, quanto no Geodataframe com informações geoespaciais dos municípios.

Já seria possível o mapa com a biblioteca Folium, porém, as bibliotecas Altair e Bokeh utilizam somente o Geodataframe, portanto, é preciso levar a variável que contém os dados estatísticos de interesse – ‘deaths_per_100k_inhabitants’ – para dentro do arquivo geoespacial.

# Levando os dados da taxa de mortalidade para dentro do objeto geoespacial
geo_cidades = pd.merge(
    left = geo_cidades,
    right = covid.filter(items=['id','deaths_per_100k_inhabitants']), 
    on='id'
)
# Verificando a estrutuea do geopandasdataframe resultado da junção
geo_cidades.info()

Veja que além das informações geoespaciais, o arquivo GeoDataFrame contém também a taxa de mortalidade que usaremos para “pintar” o mapa cloroplético.

Com o GeoDataFrame todo arrumadinho, contendo os polígonos bem como os dados estatísticos de interesse, vamos elaborar os mapas clorolléticos utilizando as bibliotecas Folium, Altair e Bokeh respectivamente.


Construindo o mapa da taxa de mortalidade com Folium

# Importando as bibliotecas necessarias
import folium
# Criando o mapa base
mapa_folium = folium.Map(
    width = 800, 
    height = 600, 
    location = [-12.97, -38.51], 
    zoom_start = 4, 
    tiles = "cartodbpositron"
)

# Criando a camada Choroplet
mapa = folium.Choropleth(
    geo_data = geo_cidades,
    name ='choropleth',
    data = covid,
    columns = ['id','deaths_per_100k_inhabitants'],
    key_on = 'feature.properties.id',
    fill_color = 'Reds',
    fill_opacity = 1.0,
    line_opacity = 0.9,
    line_color = 'grey', 
    line_weight = 0.25,
    legend_name = 'Taxa de Mortalidade por Covid-19, por municípios, 11/08/2022',
    highlight = True,
    reset = True 

).add_to(mapa_folium)

# Inserindo os rotulos ao passar o mouse
mapa.geojson.add_child(
  folium.features.GeoJsonTooltip(
      fields = ['name','id','deaths_per_100k_inhabitants'],
      aliases = ['Município:','id:', 'Taxa de Mortalidade:'],
      labels = True,
      localize = True,
      sticky = False,
  )
)

# Renderizando o mapa
mapa_folium

Construindo o mapa da taxa de mortalidade com Altair

# Importando as bibliotecas necessarias
import altair as alt

Como o Altair envia o conjunto de dados inteiro para o navegador e o processa em frond-end, ele não performa bem para conjunto de dados grandes. Assim, por default, eles limitam o conjunto de dados em no máximo 5.000 linhas. Como no Brasil temos 5.570 municípios e desses 5.564 apresentaram mortes por Covid-19, o conjunto de dados ultrapassa o limite que o Altair impôs. Portanto, devemos desabilitar esse critério do máximo de linhas.

# Desabilitanto o limite de 5.000 linhas de df do Altair
alt.data_transformers.enable('default', max_rows=None)
# criando o mapa
mapa_altair = alt\
  .Chart(
    data = geo_cidades, 
    title = 'Taxa de Mortalidade por Covid-19, por municípios, 11/08/2022'
  )\
  .mark_geoshape(
    stroke='grey',
    strokeWidth=0.1
  )\
  .encode( 
      alt.Color(shorthand = 'deaths_per_100k_inhabitants', 
                  type='quantitative', 
                  scale=alt.Scale(scheme='reds'),
                  title = "deaths_per_100k_inhabitants"),
    tooltip=['name','id','deaths_per_100k_inhabitants']
  )\
  .properties( 
    width=600,
    height=400

)

# Exibindo o mapa
(mapa_altair).configure_view(strokeWidth=0)

Construindo o mapa da taxa de mortalidade com Bokeh

# Importando de forma explícitas as bibliotecas
import bokeh.io
import bokeh.plotting

# Habilitando a visualização de gráficos gerados pelo Bokeh em Notebooks
bokeh.io.output_notebook()

# Importando bibliotecas necessárias
from bokeh.models import GeoJSONDataSource
from bokeh.palettes import YlGnBu
from bokeh.models import HoverTool
from bokeh.models import LinearColorMapper, LogColorMapper, ColorBar
# Lendo json e fazendo transformações necessárias para GEOJSON
merged_json = json.loads(geo_cidades.to_json())
json_data = json.dumps(merged_json)
geojson_cidades = GeoJSONDataSource(geojson = json_data)


# Definindo paleta de cores
palette = bokeh.palettes.Reds[6]
palette = palette[::-1]

# Fazendo cortes lineares na escala para para aplicar paleta
# Para cortes logaritmos utilize LogColorMapper
color_mapper = LinearColorMapper(
    palette = palette, 
    low = geo_cidades['deaths_per_100k_inhabitants'].min(), 
    high = geo_cidades['deaths_per_100k_inhabitants'].max(), 
    nan_color = '#d9d9d9')

# Ajustando ferramenta para popup com mouse
hover = HoverTool(
    tooltips = [ ('Município','@name'),
                ('Taxa de Mortalidade','@deaths_per_100k_inhabitants')
    ])

# Criando barras de cores 
color_bar = ColorBar(
    color_mapper=color_mapper, 
    label_standoff=6,
    width = 500, 
    height = 20,
    border_line_color=None,
    location = (0,0), 
    orientation = 'horizontal', 
)

# Definindo propriedades do mapa
mapa_bokeh = bokeh.plotting.figure(
    title = 'Taxa de Mortalidade por Covid-19, por municípios, 11/08/2022.', 
    plot_height = 600 , 
    plot_width = 600, 
    toolbar_location = None, 
    tools = [hover]
)

# Ajustando linhas de grades e visibilidade dos eixos
mapa_bokeh.xaxis.visible = False

mapa_bokeh.yaxis.visible = False

mapa_bokeh.xgrid.grid_line_color = None

mapa_bokeh.ygrid.grid_line_color = None


# Ajustes para renderização. 
mapa_bokeh.patches(
  'xs','ys', source = geojson_cidades,
  fill_color = {'field' :'deaths_per_100k_inhabitants', 'transform':color_mapper},
  line_color = 'grey', line_width = 0.25, fill_alpha = 1
)

# Adicionando barra de cores
mapa_bokeh.add_layout(color_bar, 'below')


# Exibindo o mapa
bokeh.io.show(mapa_bokeh)

Bom, vamos encerrando o post, espero que tenha gostado!

Recomendo que você dê uma lida na documentação e em alguns materiais complementares listados abaixo para obter mais informações e detalhes dos métodos utilizados neste post:

Por fim, você pode baixar o notebook com todas essas informações diretamente do meu GitHub.

Grande Abraço!

Jonatas Silva do Espirito Santo – https://www.linkedin.com/in/jonatasses/

Tags: | | | | | | | | | | | |

Sobre o Autor

Jonatas Silva Espirito Santo
Jonatas Silva Espirito Santo

Estatístico com ampla experiência em projetos de dados. Líder de projetos de pesquisas no Governo da Bahia. Desenvolvedor na linguagem R e Python. Tem conhecimentos SQL e Spark. Busca continuamente conhecimento na área de modelagem de dados e processamento de big data. Doutorando em Computação. Mestre e Bacharel em Estatística. Proativo, Flexível, Criativo e Inovador, de Boa comunicação, tem capacidade de adaptação e Liderança.

0 Comentários

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

plugins premium WordPress