La visualización de datos espaciales es un procedimiento básico utilizado a diario por profesionales del Análisis de Datos y la Ciencia de Datos. Como es bien sabido, existen decenas (probablemente cientos) de librerías y plataformas orientadas a la visualización de datos.
Para el caso de los datos espaciales o geográficos, cada vez de mayor relevancia en el contexto de la transformación digital de muchas empresas y su Inteligencia de Negocio, también tienen soluciones específicas de visualización en ciertas librerías.
Plotly es una de esas librerías y además lo hace bastante bien, a pesar de ciertos aspectos propios que puedan incomodar a algunos usuarios provenientes del mundo GIS.
Este artículo pretende ofrecer una pequeña introducción a Plotly: una de las principales y más populares librerías de visualización de datos que también permite crear mapas sencillos e interactivos con ella.
¿Qué es Plotly y por qué utilizarla con datos espaciales?
Plotly es una librería Open Source para generar visualizaciones de datos en forma de gráficos y mapas interactivos. Se trata de una gran solución para múltiples proyectos debido a su facilidad de uso y sobretodo de integración.
Se encuentra disponible para trabajar con su conjunto de herramientas en los lenguajes de programación Python, R y JavaScript. Por supuesto, cada librería con sus peculiaridades dada la adaptación a la gramática y las características de cada uno de los lenguajes.
Sobre las librerías de Plotly se construye Dash, una plataforma Saas para empresas orientada al análisis y la visualización integral de datos.
¿Qué hace interesante a Plotly para visualizar datos espaciales?
Existen múltiples maneras de evaluar la calidad de una librería. Después de probar Plotly y conociendo su enorme popularidad (recordamos que el plugin Data Plotly para QGIS integra esta librería para visualizar datos dentro de su interfaz) podrían destacar algunos aspectos como:
- Sencillez. Los gráficos no pretenden ser espectaculares, más bien son sencillos a pesar de las múltiples posibilidades de personalización. En su diseño no existen elementos gráficos pesados, que llamen la atención. Se trata de visualizaciones modernas, muy en la línea de ggplot2 para R, por ejemplo, o Seaborn para Python.
- Integración. La ventaja de utilizar Plotly es su integración en múltiples lenguajes y plataformas, así como la facilidad que ofrece para compartir los gráficos, mapas y visualizaciones de datos creados con esta librería.
- Variedad. El abanico de posibilidades de visualización y personalización de los gráficos y los mapas es abrumador. Existen cientos de gráficos distintos que aportan una gran flexibilidad a la hora de realizar visualizaciones de datos.
- Interactividad. Este es quizás el punto más interesantes de todos. Los gráficos y mapas son interactivos. Podemos jugar con el hover y el zoom, la selección múltiple y única de elementos, entre otros… Básicamente Plotly genera figuras habilitadas para interaccionar con los datos plasmados en el gráfico o mapa, desplazarse por ellos e ir al detalle. Con ello, se ofrece la posibilidad de conocer en mayor profundidad los datos, destacar algunos de ellos, modificar sus visualizaciones, etcétera, en comparación con un gráfico estático.
Mapas con Plotly para visualizar datos espaciales
Este artículo pretende mostrar algunas ideas sencillas acerca de cómo empezar a generar mapas con Plotly para Python sobre Jupyter Notebook.
Vamos a tratar de mostrar algunas visualizaciones de datos espaciales distintas en base a tres conjuntos de datos distintos, que integren recursos y parámetros distintos.
Cada uno de los conjuntos de datos responde a una geometría distinta (puntos, polígonos y líneas), con el objetivo de introducir cierta variedad y exprimir las posibilidades que ofrece Plotly.
Generar un mapa de puntos con Plotly
Empecemos por crear un mapa en base a un archivo que representa geometrías de puntos. Trataremos de visualizar las principales ciudades del mundo con un estilo de marcador y mapa base personalizados y una proyección adecuada.
Desde Jupyter Notebook, primero importaremos las librerías y paquetes necesarios. En este caso sólo serán necesarios dos:
import pandas as pd
import plotly.graph_objects as go
A continuación deberemos acceder al archivo que contiene los datos y mostraremos las cabeceras y primeros registros:
ciudades = 'ciudades/pob_urbana.csv'
datos = pd.read_csv(ciudades)
datos.head()
Utilizaremos la población proyectada para el año 2025 (campo «pop2025») en las principales ciudades del mundo.
Antes de crear el mapa, deberemos crear un nuevo campo (etiqueta) que contenga la etiqueta que deseamos mostrar. Para mostrar la población de la etiqueta en millones de habitantes como unidad, deberemos reconvertir el campo pop2025:
datos['poblacion'] = (datos['pop2025']/1000).astype(float)
datos['etiqueta'] = datos['City'].astype(str) + '<br>' + datos['poblacion'].astype(str) + ' millones de habitantes'
datos.head()
A continuación generaremos la figura, daremos estilo a los puntos a mostrar y al mapa base:
# Generamos la figura del mapa
fig = go.Figure()
# Recorremos los datos del archivo csv y extraemos sus coordenadas,
# etiquetas a mostrar y especificamos características del marcador
for i in datos:
fig.add_trace(go.Scattergeo(
lon = datos['Longitude'],
lat = datos['Latitude'],
text = datos['etiqueta'],
marker = dict(
size = datos['poblacion']*10,
color = 'lightseagreen',
line_color='black',
line_width=0.5,
sizemode = 'area'
)))
# Establecemos las características del mapa base, el título y opciones
# de leyenda, así como la propia proyección del mapa.
fig.update_layout(
title_text = 'Población en las ciudades del mundo, año 2025',
showlegend = False,
geo = dict(
scope='world',
resolution=110,
projection_type='mollweide',
showcoastlines=True,
showocean=True,
oceancolor='#132630',
lakecolor='#132630',
coastlinecolor='#224354',
landcolor='#224354',
)
)
# Mostramos la figura creada con las características dadas
fig.show()
El resultado obtenido se vería del siguiente modo:
Al navegar por la figura creada, podemos ver que se muestra la etiqueta en hover, al pasar el ratón por encima de cualquier punto, hacer zoom y seleccionar sólo algunos elementos, otorgando cierta interacción:
Mapa de coropletas usando Plotly
A continuación, vamos a intentar mostrar en un mapa de coropletas, basado en geometrías poligonales, la información relativa al porcentaje de población de carácter urbano en los países europeos.
Cargamos la información y mostramos la cabecera para inspeccionar el conjunto de datos:
datos_pob = pd.read_csv('pob_urbana/poblacion_urbana_europa.csv')
datos_pob.head()
A continuación deberemos crear la figura en base a los datos cargados para que muestren los valores del campo «Urban_pop_2015» en base a una paleta de colores.
También deberemos crear un mapa base de referencia e indicar el encuadre sobre Europa:
# Generamos la figura y establecemos los valores para configurar
# el encuadre, la paleta de colores, la capa de datos, la etiqueta
fig = go.Figure(go.Choropleth(
locations = datos_pob['Country'],
locationmode = "country names",
z = datos_pob['Urban_pop_2015'],
text = datos_pob['Country'],
colorscale = 'viridis',
autocolorscale=False,
reversescale=True,
marker_line_color='#efefef',
marker_line_width=0.5,
colorbar_ticksuffix = '%',
colorbar_title = 'Población urbana',
)
)
# Establecemos las características del título y la apariencia del mapa base
fig.update_layout(
title_text = 'Porcentaje de población urbana en Europa por país, año 2015',
showlegend = False,
geo = dict(
scope='europe',
resolution=50,
projection_type='miller',
showcoastlines=True,
showocean=True,
showcountries=True,
oceancolor='#eaeaea',
lakecolor='#eaeaea',
coastlinecolor='#dadada'
)
)
# Mostramos la figura generada
fig.show()
El resultado obtenido es el siguiente:
Cabe destacar que la lectura de datos se hace desde un archivo de texto y no desde una capa GIS habitual que contenga las geometrías.
De hecho, Plotly funciona ligeramente distinto a otras librerías que pueden leer archivos geométricos. En cierto modo, recrea las geometrías extrayéndolas de atributos que no siempre representan coordenadas.
Por ello, deberemos especificar manualmente y según el tipo de geometría la forma en cómo leer y asignar las geometrías sobre el gráfico.
En el caso de capas de puntos, como hemos visto, se realiza obteniendo las coordenadas de latitud y longitud contenidas en campos.
Para mapas de polígonos, como el visto de coropletas, esto es radicalmente distinto. Por la imposibilidad de expresar geometrías poligonales en un csv, Plotly necesita que exista un campo con un topónimo o código para poder «emparejar» dicho campo con las opciones disponibles en el mapa base.
En otras palabras, Plotly asigna el valor del campo indicado (en nuestro caso «Urban_pop_2015») a una región del mapa predefinida, mediante la asignación al parámetro «locationmode». Puedes encontrar más información en este enlace.
Así pues, con Plotly no vamos a poder importar cualquier tipo de geometría poligonal, sino que debe responder y poder ser emparejado con un código o topónimo concreto disponible y habilitado por Plotly.
Visualizar una capa de líneas en Plotly
Para el caso de capas con geometrías de tipo líneas, el funcionamiento también es distinto. En este caso, lógicamente, no es necesario emparejar las líneas con ningún código: funciona muy similar al caso de los puntos aunque debe proporcionarse mayor información.
Cuando trabajamos con capas de líneas, lo que deberemos indicar y extraer del csv son las coordenadas origen y final de cada elemento lineal.
Como ejemplo, vamos a tratar de mostrar el recorrido por etapas del Tren Transiberiano.
Cargaremos una capa de puntos y una capa de líneas, inspeccionando sus cabeceras para comprender y diferenciar la manera de cargar ambas capas:
# Capa de puntos correspondiente a las principales ciudades con parada
ciudades = pd.read_csv('transiberiano/ciudades.csv')
ciudades.head()
# Capa de líneas correspondiente a cada etapa
recorrido = pd.read_csv('transiberiano/etapas.csv')
recorrido.head()
Como vemos, en este caso se requiere disponer del csv con las coordenadas de inicio de la línea (latitud y longitud iniciales) y coordenadas de final de la línea (latitud y longitud final). En base a dichas coordenadas se podrán construir las lineas a mostrar en el mapa y darles el aspecto deseado.
Así pues, vamos a tratar de cargar ambas capas y dotarlas de cierto aspecto para diferenciar la forma de se tratadas:
# Creamos la figura
fig = go.Figure()
# Carga de la capa de líneas (etapas)
for i in range(len(recorrido)): # Debemos recorrer cada registro
fig.add_trace( # Añadir el trazo a la figura
go.Scattergeo(
# Especificamos la longitud y la latitud en dos variables,
# extrayendo los valores de los campos del csv del recorrido
lon = [recorrido['lon_ini'][i], recorrido['lon_fin'][i]],
lat = [recorrido['lat_ini'][i], recorrido['lat_fin'][i]],
mode = 'lines',
line = dict(width = 1,color = 'red')
)
)
# Carga de la capa de puntos (ciudades)
fig.add_trace(go.Scattergeo(
lon = ciudades['lon'],
lat = ciudades['lat'],
text = ciudades['Nombre'],
marker = dict(
size = 5,
color = 'red',
line = dict(
width = 0.5,
color = 'white'
)
)))
# Especificamos características del mapa base y la proyección
fig.update_layout(
title_text = 'Ruta del Tren Transiberiano',
showlegend = False,
geo = dict(
scope='world',
resolution=110,
projection = dict(
type = 'orthographic',
rotation = dict(
lon = 100,
lat = 60,
roll = 0
)
),
# Añadimos ejes de meridianos
lonaxis = dict(
showgrid = True,
gridcolor = 'rgb(102, 102, 102)',
gridwidth = 0.5
),
# Añadimos ejes de paralelos
lataxis = dict(
showgrid = True,
gridcolor = 'rgb(102, 102, 102)',
gridwidth = 0.5
),
showcoastlines=True,
showocean=True,
showcountries=True,
countrycolor='#829199',
oceancolor='#132630',
lakecolor='#132630',
coastlinecolor='#829199',
landcolor='#224354',
)
)
# Mostramos el mapa creado
fig.show()
El resultado obtenido es el siguiente:
Puedes descargar los datos utilizados en los distintos ejemplos a continuación (múltiples fuentes):
Animamos a probar a realizar gráficos y mapas con Plotly, tanto con Python como para R y JavaScript para obtener visualizaciones dinámicas, interactivas e interesantes, no excesivamente difíciles de realizar.
Para tratar de realizar visualizaciones de datos más sencillas y estáticas se puede utilizar Matplotlib junto a Geopandas o Basemap como librerías de Python de base.
Puedes encontrar ejemplos en nuestro artículo sobre Geopandas y Matplotlib para generar mapas estáticos en Python, un breve tutorial que realizamos para mostrar brevemente cómo automatizar salidas de mapas y realizar visualizaciones sencillas de datos espaciales.