lunes, 11 de noviembre de 2019

Investigación de librerías de geolocalización

INVESTIGACIÓN DE LIBRERÍAS DE GEOLOCALIZACIÓN

Uno de los problemas más comunes a la hora de trabajar con grandes volúmenes de datos es saber interpretarlos correctamente y darles un tratamiento adecuado. Por ello, los ingenieros requieren del uso de gráficos y estadísticos que les faciliten la tarea de análisis. A día de hoy, existen multitud de librerías creadas para el procesamiento y visualización de datos y en este post vamos a investigar algunas de ellas.

En nuestro proyecto estudiamos subsistemas de movilidad, en concreto, la movilidad urbana con bicis. De este sistema, tenemos información de la posición que ocupa cada estación en el mapa geográfico. Así que, nos será de mucha ayuda mostrar cada estación en un mapa de calles y sobre él, empezar a visualizar más datos. Por ejemplo: área de influencia de una estación, movimientos de bicis, zonas con mayor concentración de bicis...



BASEMAP

Basemap no es exactamente una librería sino que se trata de una extensión de Matplotlib. Facilita el trabajo a la hora de mostrar información geográfica, hacer transformaciones entre diferentes tipos de proyecciones geográficas y trabajar con gráficos vectoriales. Sin embargo, se trata de una extensión que va a ser declarada obsoleta para dejar paso a un nuevo proyecto llamado Cartopy. 

Si queréis investigar cómo funciona Basemap, os muestro a continuación un ejemplo de gráfico que se puede generar con Basemap y un enlace a un tutorial (click aquí).



CARTOPY

Cartopy es una librería que entra para sustituir a Basemap. Ofrece las funcionalidades que ya ofrecía su antecesor y añade mejoras en el procesamiento de información vectorial gracias a la integración de Shapely, una librería diseñada para el análisis y manipulación de objetos geométricos planos. Cartopy se encuentra todavía en desarrollo pero ya se pueden generar imágenes como la que se muestra a continuación.

Esta imagen ha sido generada como ejemplo de introducción a Cartopy. Podéis visitar el tutorial que desarrolla este gráfico en el siguiente enlace (click aquí).


OPEN STREET MAP (OSM)

Hasta ahora, los dos ejemplos que hemos visto trabajan con mapas geográficos. Sin embargo, ninguno de ellos se centra en ciudades y calles concretas sino que se dedican a un estudio más global. Para realizar una investigación exhaustiva de las calles de una ciudad, existe una librería llamada Open Street Map.

Open Street Map es un proyecto de software libre a escala global que pretende mapear todos los aspectos importantes de las ciudades: calles, carreteras, carriles bici, edificios, puntos de interés, colegios, hospitales... Cuenta con miles de personas involucradas que se dedican a mantener información actualizada y a facilitar su uso en aplicaciones y proyectos de investigación.



OPEN STREET MAP NX (OSMnx)

Open Street Map NX se trata de una librería auxiliar que nos va a facilitar el trabajo con Open Street Map. Permite obtener datos de las ciudades desde los repositorios oficiales de OSM de forma muy sencilla con unas pocas líneas de código. 

Después de investigar varias alternativas, vemos que ésta es la que mejor se adapta a nuestras necesidades, así que, será la que utilicemos a lo largo del proyecto. Como ejemplo, os muestro un mapa de calles extraído desde OSM usando OSMnx. Os dejo también el tutorial que se ha seguido para generar esta imagen (click aquí) y una página web donde se explican más ejemplos (click aquí).



domingo, 3 de noviembre de 2019

Comenzando en el análisis y extracción de datos. Diagrama de Voronoi

¿QUÉ DATOS TENGO A MI DISPOSICIÓN?

Cuando empezamos un proyecto de análisis de datos debemos estructurar la información disponible y fijar los aspectos que queremos observar. En esta ocasión, estudiaremos las estaciones de bici de diferentes ciudades del mundo. La información de cada estación es la siguiente:
  • Ciudad a la que pertenece.
  • Identificador único para cada estación dentro de una ciudad.
  • Dirección de la calle en la que se encuentra.
  • Valores de longitud y latitud para situarla geográficamente.
  • Capacidad máxima de bicis que puede albergar.
  • Bicis disponibles.
De todos los atributos que hemos mencionado, lo único que varía a lo largo del tiempo son las bicis disponibles. Cada cinco minutos, las estaciones envían a una base de datos cuántas bicis tienen en ese momento. De esta forma, podemos saber cómo evoluciona el número de bicis a lo largo de un día, una semana, un mes, un año...




¿QUÉ PRETENDEMOS OBSERVAR?

Según lo que queramos observar, tendremos que procesar los datos de una forma u otra. Vamos a empezar con una pequeña aproximación a nuestro sistema de bicis. En primer lugar, podría ser de interés saber qué zonas de la ciudad tienen más estaciones de bicis. Es de esperar que en las zonas céntricas halla más estaciones, pero, ¿serán suficientes? ¿Habrá zonas sin estaciones cerca? Vamos a comprobarlo.

Empezaremos segmentando el mapa de la ciudad y para ello, necesitaremos implementar algunas herramientas informáticas que nos faciliten la tarea. Vamos a utilizar un diagrama de Voronoi.

DIAGRAMA DE VORONOI

Un diagrama de Voronoi es una malla como la que se muestra a continuación:


Los puntos azules son los puntos de Voronoi y se utilizan para crear las diferentes regiones de la malla. El proceso que se sigue para crear la malla es el siguiente:
  1. Se genera una nube de puntos. 
  2. Se trazan las mediatrices entre los puntos de la nube. 
  3. Los vértices de las regiones de la malla son los puntos donde corten las mediatrices.
Nota: Dados dos puntos A y B, la mediatriz es la recta que divide por la mitad el segmento que une  A y B. Se cumple que los puntos de la mediatriz equidistan de A y B.

La nube de puntos constituye el conjunto de puntos de Voronoi. Entre cada par de puntos (P1, P2) existe una frontera que equidista de (P1, P2). De esta manera, si estudiamos una muestra dentro de una región de Voronoi, sabemos que el punto de Voronoi más cercano es aquel que se encuentra también dentro de esa región. Veamoslo gráficamente:



P1 es el punto de Voronoi más cercano a cualquier punto de la región coloreada. Si utilizamos los barrios de una ciudad como puntos de Voronoi, podemos averiguar cuántas estaciones de bici hay por cada barrio y así conocer cuáles son las zonas con más y menos densidad de bicis. Del mismo modo, si consideramos las estaciones de bici como puntos de Voronoi, podemos ver el área de influencia de cada estación. 

HERRAMIENTAS INFORMÁTICAS 

Para construir el diagrama de Voronoi vamos a usar:
  • Python como lenguaje de programación. 
  • Jupyter como entorno de trabajo.
  • Anaconda como gestor de paquetes.
  • scipy.spatial.Voronoi como librería.
No vamos a implementar el diagrama desde cero, vamos a apoyarnos en una librería que ya lo genera y vamos a utilizarlo para nuestra aplicación. Tendremos que añadir algunas líneas de código para el análisis que queremos llevar a cabo, pero partimos de una base.

Nota: si no estás familiarizado con Python, Jupyter o Anaconda, tengo un post dedicado para hacer la configuración del entorno de desarrollo que vamos a usar a lo largo del proyecto. Link: click aquí.

Nota (II): para el código que se va a mostrar, se recomienda estar familiarizado con matplotlib (tutorial: click aquí) y con numpy (tutorial: click aquí).


IMPLEMENTAR DIAGRAMA VORONOI

Vamos a crear un entorno virtual en Anaconda para trabajar con la librería scipy. Para ello:
  1. Abrimos una terminal de Anaconda. Podemos hacerlo escribiendo en la barra de búsqueda Anaconda prompt.
  2. Creamos un entorno virtual: conda create -n voronoienv python=3.7
  3. Activamos el entorno virtual: conda activate voronoienv
  4. Instalamos los paquetes que vamos a usar: conda install scipy
  5. Instalamos la librería que enlaza Anaconda con Jupyter: conda install nb_conda
  6. Abrimos Jupyter: jupyter notebook
  7. Creamos un nuevo proyecto dentro del entorno virtual voronoienv.
CÓDIGO FUENTE: Todo el código que se va a mostrar a continuación está subido en mi cuenta de GitHub: click aquí.

PASO 1. Importación de librerías

Cuando hayamos creado el proyecto en Jupyter, vamos a importar las siguientes librerías:

Si nos da algún error porque no encuentre alguna de las librerías que estamos importando, la forma de solucionarlo es instalar la librería que falte usando Anaconda y el comando conda install nombreLibreria.

PASO 2. Graficar diagrama de Voronoi sencillo

Vamos a crear un diagrama de Voronoi aleatorio para probar que la librería funciona como esperamos.


Los puntos azules son los puntos de Voronoi y los puntos naranjas son los vérticos de las regiones de Voronoi. Podemos mostrar este mismo diagrama sin que se vean los puntos azules y naranjas añadiendo parámetros a la función voronoi_plot_2d.


Para conocer todos los parámetros que se pueden usar para configurar el diagrama, os recomiendo visitar la documentación oficial: click aquí.


PASO 3. Pintar puntos encima de un diagrama de Voronoi

Vamos a generar una nube de puntos aleatorio y la vamos a pintar encima de un diagrama de Voronoi usando la librería matplotlib. De ahora en adelante, vamos a referirnos a los puntos que pintamos sobre el diagrama de Voronoi como puntos de interés.


PASO 4. Densidad de puntos en una región

Ahora que somos capaces de mostrar los puntos de interés y el diagrama de Voronoi, sería interesante conocer la densidad de puntos de interés dentro de cada región de Voronoi. Para ello, tenemos que hacer lo siguiente:
  1. Extraer las regiones de Voronoi.
  2. Crear un objeto Path por cada región que no sea infinita. Un objeto Path define un polígono irregular que es capaz de determinar si contiene o no un punto del espacio. Si vemos el diagrama, hay regiones cuyas fronteras son líneas discontinuas. Esto quiere decir que la región es infinita porque no se corta con ninguna otra recta. Los vértices de este tipo de regiones almacenan el valor -1 para indicar que es una región infinita.
  3. Estudiar cuántos puntos de interés caen dentro de cada Path.
Nota: el código Python que se va a mostrar a continuación es avanzado. Si no estás familiarizado con el lenguaje, no te centres en qué hace cada sentencia y trata de quedarte con la idea general.

Vamos a mostrar el código y luego lo explicamos:


Explicación del código:
  • Líneas 1-5: creamos el diagrama de Voronoi y los puntos de interés.
  • Línea 8: extraemos las regiones del diagrama de Voronoi.
  • Línea 9: inicializamos a vacío el array que usaremos para almacenar los objetos Path.
  • Líneas 10-14: por cada región, si no tiene un vértice con -1 (es decir, si no es una región infinita) y no es una región sin vértices, se utilizan los vértices para crear un polígono. Con el polígono, creamos un objeto Path y, finalmente, almacenamos el Path en el array.
  • Línea 17: creamos un array con tantas posiciones como regiones de Voronoi e inicializamos todas sus posiciones a cero. Este array contendrá las densidades de puntos de interés de cada región.
  • Líneas 17-25: por cada punto de interés, buscamos el Path que lo contiene y sumamos 1 a la posición que le corresponda en el array de densidades.

PASO 5. Colorear cada región en función de la densidad de puntos

Vamos a colorear con tonos más oscuros las regiones que tengan mayor concentración de puntos de interés. Para ello, primero debemos graficar el diagrama de Voronoi y los puntos y después colorear las regiones. Es importante hacerlo en este orden porque el método que vamos a usar para colorear las regiones se apoya en que ya haya un diagrama pintado. 

Para el código, vamos a suponer que ya tenemos el cálculo de las densidades hecho. Al igual que en el paso anterior, mostramos el código y después lo explicamos:



Explicación del código:
  • Líneas 2-3: graficamos el diagrama de Voronoi y los puntos.
  • Línea 5: definimos un valor alpha. Este valor va a ser el que indique la intensidad del color. Debe tener un valor comprendido entre 0 y 1, así que, lo vamos a normalizar atendiendo al valor máximo de densidad para evitar que supere el valor 1 y vamos a añadir un porcentaje al cociente para evitar que la región con mayor densidad tenga un color completamente negro.
  • Líneas 7-11: por cada región, si no tiene un vértice con -1 (es decir, si no es una región infinita) y no es una región sin vértices, se utilizan los vértices para crear un polígono. Basándonos en el polígono y la densidad correspondiente a esa región, coloreamos el diagrama.
Podemos observar que hay una región muy oscura que se corresponde con la mayor concentración de puntos de interés. Además, cabe destacar que los puntos situados en regiones infinitas no han sido contabilizados


PASO 6. Añadir peso a los puntos de interés

Cada punto que pintamos sobre el diagrama de Voronoi lo contabilizamos como +1. Sin embargo, podemos modificar nuestro código para que se tenga en cuenta que hay puntos con más valor que otros. Para no alejarnos mucho de lo que queremos conseguir, darle peso a los puntos lo podemos asociar con el tamaño de nuestras estaciones de bici. Cuanto mayor sea la estación y más bicis pueda tener, mayor influencia ejerce.

Para implementar esto, vamos a considerar que los puntos de interés vienen dados con tres valores: (coordenada x, coordenada y, peso). Lo único que tenemos que hacer es añadir este factor al cálculo de densidades. Solo se modifican dos líneas:



Líneas modificadas:
  • Línea 1: a la hora de crear los points_of_interest debemos generar tres valores en lugar de 2.
  • Línea 9: la densidad se actualiza sumando el valor del peso asociado al punto de interés.
Si queremos que nuestro código admita entradas con peso y sin peso, podemos añadir un fragmento de código que, en caso de que los puntos de interés no tengan peso, se les asocie un peso de 1.



PASO 7. Adaptar la nube de puntos a coordenadas no normalizadas y añadir bordes

Cuando generamos la nube de puntos usando la función random de la librería Numpy, los valores que se obtienen están en coordenadas normalizadas entre 0 y 1. Sin embargo, en la realidad, las coordenadas de los puntos de interés no tienen por qué estar entre 0 y 1.

Además, vimos en el paso 5 que había puntos que se situaban en regiones de Voronoi infinitas y que estas regiones se sitúan en los bordes del diagrama de Voronoi. Podemos conseguir que todos los puntos de interés queden en regiones finitas si generamos los puntos de Voronoi más dispersos, ya que, así, los puntos de interés se alejarán de los bordes y caerán en regiones finitas.

El grado de dispersión de los puntos de Voronoi debe ser relativo al tamaño de la nube de puntos de interés. Para expresar el grado de dispersión vamos a utilizar un porcentaje de borde que actuará de la siguiente forma: si las coordenadas de los puntos de interés están entre 0 y 100 y usamos un porcentaje de borde del 20%, los puntos de Voronoi se generarán entre -20 y 120.

En definitiva, tenemos que desplazar y escalar los puntos de Voronoi para que se adapten a la nube de puntos de interés. El código es el siguiente:


Paso 8. Crear función

Finalmente, después de probar todos los bloques de código, podemos unirlos en una función. El resultado que se obtiene es:



Como vemos, las regiones se colorean, los puntos de interés tienen pesos asociados, todos caen sobre regiones finitas del diagrama y, además, están en coordenadas no normalizadas. Esta función es la primera herramienta informática que hemos creado y que nos va a permitir el análisis de datos posterior.

CÓDIGO FUENTE: Todo el código que se va a mostrar a continuación está subido en mi cuenta de GitHub: click aquí.


Configurando el entorno de trabajo


Para nuestro proyecto vamos a usar:
  • Python como lenguaje de programación. 
  • Jupyter como entorno de trabajo.
  • Anaconda como gestor de paquetes.
A continuación, vamos a describir qué es cada elemento que hemos mencionado y cómo los instalamos para poder llevar a cabo el proyecto.

¿QUÉ ES PYTHON?

Python es un lenguaje de programación interpretado, imperativo y orientado a objetos. Tiene una extensa variedad de librerías que facilitan enormemente el trabajo del programador y, por ello, se ha convertido en uno de los lenguajes más populares del momento.

Para aquellos que quieran iniciarse en la programación en Python, yo recomiendo este tutorial. Es un tutorial en inglés de unas seis horas que te introduce los fundamentos básicos del lenguaje. Si estás interesado en profundizar aún más, os recomiendo investigar las siguientes librerías:
  • Matplotlib: es una librería para dibujar gráficos en 2D. Tutorial: click aquí.
  • Numpy: es una librería que facilita el trabajo con vectores y matrices. Tutorial: click aquí.
  • Pandas: es una extensión de Numpy para trabajar con tablas de datos. Tutorial: click aquí.
Link para descargar Python: click aquí.



¿QUÉ ES JUPYTER?

Jupyter es un entorno de trabajo interactivo que permite escribir bloques de código y visualizar gráficos de forma muy dinámica. Podemos ejecutar bloques de código de forma aislada y comprobar lo que estamos haciendo.

Link para descargar Jupyter: click aquí.



¿QUÉ ES ANACONDA?

Anaconda es un gestor de paquetes que nos va a facilitar el uso de librerías y de entornos virtuales. Un entorno virtual es un espacio aislado donde podemos instalar los paquetes que necesitemos para nuestros proyectos. Estos paquetes estarán instalados únicamente dentro del entorno virtual y no interferirán de ninguna forma con los paquetes de otro entorno. De esta forma, podemos probar librerías sin el riesgo de provocar efectos colaterales con algún paquete que ya estemos usando.

Link para descargar Anaconda: click aquí.




PREPARANDO EL ENTORNO DE TRABAJO

Lo primero que tenemos que hacer para empezar a trabajar es instalar Python, Jupyter y Anaconda. Una vez hayamos hecho esto, lo que haremos a continuación será:

  1. Crear un entorno virtual.
  2. Instalar los paquetes que necesitemos en ese entorno virtual.
  3. Vincular Jupyter con Anaconda.
  4. Crear un proyecto en Jupyter dentro de un entorno virtual de Anaconda.
  5. Ejecutar código en Python dentro del proyecto de Jupyter.

PASO 1. Crear entorno virtual.

Debemos abrir un terminal de Anaconda. Podemos escribir en la barra de búsqueda Anaconda Prompt y, si hemos realizado correctamente la instalación de Anaconda, nos aparecerá una terminal como esta:



Si vemos que pone (base) delante del prompt, quiere decir que estamos en el entorno virtual raiz (root). El entorno base es un entorno que se crea por defecto y que está activo si no hay otro en uso. Para crear un entorno virtual, la sintaxis del comando es:

conda create -n nombreEntorno python=x.x


El nombre del entorno lo decidimos nosotros y x.x es la versión de Python que tenemos instalada. Si no recordamos nuestra versión de Python, podemos abrir una terminal de Windows y escribir python.



Vamos a probar creando un entorno virtual con el siguiente comando:

conda create -n mientorno python=3.7


Cuando lo ejecutemos, nos aparecerá la lista de paquetes que se va a instalar y nos preguntan si queremos aceptar la instalación [y/n]. Para ver los entornos virtuales que tenemos creados, podemos usar el comando:

conda env list

La salida que se obtiene es de la siguiente forma:



En mi caso, tengo varios entornos creados y entre ellos se encuentra el que acabamos de crear (el * indica el entorno virtual activo). Para poder usar el entorno que hemos creado, debemos ejecutar el comando:

conda activate mientorno

Después de esto, veremos que el prompt ha cambiado.


Para salir del entorno y volver al entorno base, debemos ejecutar:

conda deactivate

PASO 2. Instalando paquetes

Vamos a probar instalando una librería en este entorno. La sintaxis del comando es:

conda install nombreLibreria

Por ejemplo, vamos a instalar la librería matplotlib para poder hacer gráficos en 2D.

conda install matplotlib

Al igual que ocurrió antes, nos mostrará una lista con los paquetes que se van a instalar. La ventaja de usar Anaconda es que si matplotlib tiene dependencias con otra librería, Anaconda las busca y las instala automáticamente. 

PASO 3. Vincular Jupyter con Anaconda

Para poder usar Jupyter con Anaconda, debemos instalar un paquete llamado nb_conda dentro del entorno virtual de trabajo, en nuestro caso, dentro de mientorno.

conda install nb_conda

Una vez lo hayamos instalado, podemos abrir Jupyter desde Anaconda usando el comando:

jupyter notebook

Importante: si vamos a trabajar en un entorno virtual, debemos asegurarnos de que tenemos ese entorno activado con conda activate nombreEntorno.

PASO 4. Crear un proyecto en Jupyter

Cuando tengamos abierto Jupyter, veremos que se ha abierto en el navegador y que nos muestra las carpetas que tenemos en nuestro equipo. Seleccionamos la carpeta en la que queremos crear nuestro proyecto y abrimos la pestaña new que se encuentra a la derecha de la pantalla. 

Veremos que se despliega una lista que muestra los entornos virtuales que tenemos creados. Seleccionamos el entorno que nos interese y se creará el proyecto. Debemos asegurarnos de que el entorno que seleccionemos esté acompañado de un * para asegurarnos de que estamos creando el proyecto en el entorno virtual activo.



PASO 5. Ejecutar código en Python

Para ejecutar el código, solo tenemos que escribirlo dentro del cuadro y dar al botón Run. Por ejemplo, vamos a visualizar un gráfica sencilla usando la librería que hemos instalado:



Llegados a este punto, ya podemos empezar a ejecutar todo el código que veamos a lo largo del proyecto. Os animo a experimentar con Jupyter y con las librerías que os he mencionado al principio del post. 

Un saludo!


Teoría de Redes Complejas (Parte 1). Conceptos básicos

Para continuar con nuestro proyecto vamos a hacer un estudio aplicando teoría de grafos y de redes complejas con el objetivo de demostrar...