Seguro que en más de una ocasión os habéis encontrado en la situación en la que queréis realizar un análisis de datos que se encuentran en formatos difíciles de analizar, pues ninguna herramienta en el mercado es capaz de cargarlos inicialmente. Estamos hablando de PDFs, Excels sin formato y un largo etcétera con el que tenemos que lidiar.

Nuestra necesidad surgió cuando nos dispusimos a realizar un análisis de los datos del tema más candente del momento; el coronavirus (Covid-19). Buscamos en las diversas webs oficiales del Gobierno y, los datos que más se ajustaban a nuestras necesidades de análisis, estaban en formato PDF y además distribuidos en documentos con formatos diferentes.

En esta entrada del blog vamos a enseñaros con qué herramientas podemos automatizar la extracción de datos desde estos orígenes que carecen de formato estructurado.

Empezando a buscar los datos

En la web del instituto de salud Carlos III perteneciente al ministerio de Sanidad, se puede tener acceso a un análisis bastante simple de la evolución del virus, así como también del número de casos confirmados, hospitalizados, fallecidos, etc. Tras la tabla del número de casos por comunidad autónoma, existe un enlace donde nos podemos descargar los datos en formato csv (Comma Separated Value), formato más que conocido entre los profesionales de nuestro sector:

Para nuestra sorpresa, podemos encontrar un gran número de valores nulos, así como también varios disclaimers indicando que el objetivo de esos datos es saber el valor acumulado en la última fecha conocida y que, por lo tanto, no se puede deducir que el incremento de nuevos casos es la diferencia entre los últimos dos días, pues no coincidirá en la mayoría de los casos:

Por otro lado, este origen de datos carecía de históricos que nos pudieran permitir el análisis histórico para, entre otros, poder visualizar tendencias. Esto unido a la falta de fiabilidad/consolidación de los datos nos hizo decantarnos por una segunda fuente de datos oficial del gobierno. En esta web, se actualiza la información del Covid-19 diariamente. El problema principal es que los datos están en tablas incrustadas en archivos en formato pdf.

Es tan importante tener a nuestra disposición unos datos de calidad como ser capaces de capturarlos y explotarlos de manera correcta. Es por ello por lo que vamos a tratar de conseguir estos datos en un formato estándar que luego podamos usar para realizar un análisis. Asimismo, es crucial que planteemos esta carga de datos de manera que sea automatizable.

Problema y solución

Problema Estrategia para solucionarlo
Los PDFs se encuentran en diferentes URLs: https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov-China/documentos/Actualizacion_46_COVID-19.pdf => 16/03/2020

Cambiando el número 46 al 47 podemos capturar los datos del día 17/03/2020

Usar una librería para descargar todos los PDFs hasta el día actual desde la URL
La información se encuentra en tablas en formato PDF Conseguir una librería que sea capaz de leer de PDFs
Las tablas se encuentran en páginas diferentes. Por ejemplo, la tabla de datos del día 16/03/2020 se encuentra en la primera página, mientras que en la del 17/03/2020 se encuentra en la segunda Buscar si esa tabla se encuentra en la primera o en la segunda página con la librería que lee los PDFs
El formato de las tablas cambia. A medida que avanzan los días se van añadiendo colunnas. Por ejemplo:

Tabla día 17/03/2020:

Tabla día 25/03/2020:

Identificar todos los formatos y tratar de estandarizarlo de manera que se nos quede un conjunto de datos unificado. De esta manera este script de carga sólo tendrá un mínimo mantenimiento para añadir aquellos formatos nuevos
Existen filas que totalizan:

Eliminar estas filas totalizadoras

Una vez tenemos claros todos los problemas con los que vamos a tener que lidiar, ya podemos ponernos manos a la obra para llevar a cabo la carga de datos. En nuestro caso, vamos a realizar un script en el lenguaje de programación Python.

En primer lugar, necesitaremos las siguientes librerías:

import pandas as pd # básico para tratar con conjuntos de datos
import requests # realizar peticiones a las URLs
import tabula # trabajar los PDFS
import datetime # fechas
from datetime import timedelta

Establecemos un lugar donde almacenaremos todos los PDFs de forma local:

path = r"C:\Users\pcorral\Blog_Covid19\"

Para que se pueda automatizar la carga de datos, debemos establecer rangos dependientes de la fecha actual. Es por ello por lo que vamos a generar el rango de forma dinámica:

date_today = datetime.datetime.now()
var_date = datetime.datetime(2020, 3, 8) # fecha inicial donde empiezan los PDFs
days_to_loop = (date_today - var_date).days
start_loop = 39 # PDF inicial donde empezó a contabilizarse en el origen. Esto no cambiará
end_loop = start_loop + days_to_loop

rango_a_iterar = range(start_loop, end_loop)

Ahora necesitamos descargarnos todos estos PDFs a la carpeta local:

# Bucle para guardar todos los PDFs en local
for w in rango_a_iterar: # iteramos sobre todos los PDFs
    name = f"covid_{str(w)}.pdf"
    # realizamos la petición a la URL para poder guardar el PDF
    doc = requests.get(f"https://www.mscbs.gob.es/profesionales/saludPublica/ccayes/alertasActual/nCov-China/documentos/Actualizacion_{str(w)}_COVID-19.pdf", verify=False)
    with open(path + name, 'wb') as f: 
        f.write(doc.content) # una vez leído el PDF, lo almacenamos en local

Como hemos comentado anteriormente, necesitamos dar de alta los diferentes formatos para que podamos unificar finalmente el conjunto de datos. Los formatos identificados son los siguientes:

format_1 = ['CCAA', 'Total casos', 'IA (casos/100.000 habitantes)', 'Ingreso en UCI', 'Fallecidos']
format_1_reformat = ['CCAA', 'TOTAL conf.', 'IA (14 d.)', 'UCI','Fallecidos']

format_2 = ['CCAA', 'Total casos', 'IA Total*', 'Ingreso en UCI', 'Fallecidos']

format_3 = ['CCAA', 'Total casos', 'IA últimos 14 días', 'Ingreso en UCI','Fallecidos']

format_4 = ['Unnamed: 0', 'Unnamed: 1', 'Unnamed: 2', 'Unnamed: 3', 'Unnamed: 4', 'Unnamed: 5', 'Nuevos']
format_4_reformat = ['CCAA', 'TOTAL conf.', 'IA (14 d.)', 'Hospitalizados', 'UCI', 'Fallecidos', 'Nuevos']

format_5 = ['Unnamed: 0', 'Unnamed: 1', 'Unnamed: 2', 'Casos que han',
       'Casos que han.1', 'Unnamed: 3', 'Unnamed: 4', 'Unnamed: 5']
format_5_reformat = ['CCAA', 'TOTAL conf.', 'IA (14 d.)', 'Hospitalizados', 'UCI', 'Fallecidos', 'Curados', 'Nuevos']

format_6 = ['CCAA', 'TOTAL conf.', 'IA (14 d.)', 'Hospitalizados', 'UCI', 'Fallecidos', 'Curados', 'Nuevos']

ideal_format = format_6 # el formato ideal será el último (el más actual)

Ahora ya estamos preparados para iterar sobre los PDFs y recuperar los datos de las tablas:

df = pd.DataFrame()

for i in rango_a_iterar: # iteramos sobre todos los PDFs
    df_aux = None
    var_date = var_date + timedelta(days=1)
    
    # añadimos un try-catch por si hubiera algun día intermedio donde no se han subido datos, que continúe con los siguientes
    # días. De hecho, existen dos días que, por alguna razón, no se han subido PDFs 
    try:
        # identificamos que la página donde se encuentra la tabla con los datos cambia a la primera 
        # cuando el nº del pdf es menor al 47
        if i < 47:
            df_aux = tabula.read_pdf(path + f"covid_{str(i)}.pdf", pages = "2", multiple_tables = True)
        else:
            df_aux = tabula.read_pdf(path + f"covid_{str(i)}.pdf", pages = "1", multiple_tables = True)
    except:
        continue
    
    # La tabla se puede encontrar o en la posición 0 o en la 1, dependiendo de como se haya construido la
    # tabla incial en el PDF
    if len(df_aux) == 1:
        df_aux = df_aux[0]
    else:
        df_aux = df_aux[1]

    cols_to_add = []

    # Reformateamos la tabla, añadiendo alguna columna si fuera necesario y cambiamos el nombre de las que si están
    # ya presentes pero se llaman diferente.
    if list(df_aux.columns) == format_1 or list(df_aux.columns) == format_2 or list(df_aux.columns) == format_3:
        df_aux.columns = format_1_reformat
        cols_to_add = set(ideal_format) - set(format_1_reformat)
        print(cols_to_add)
    elif list(df_aux.columns) == format_4:
        df_aux = df_aux[2:]
        df_aux.columns = format_4_reformat
        cols_to_add = set(ideal_format) - set(format_1_reformat)
    elif list(df_aux.columns) == format_3:
        df_aux = df_aux[2:]
        cols_to_add = set(ideal_format) - set(format_1_reformat)
    elif list(df_aux.columns) == format_5:
        df_aux = df_aux[5:]
        df_aux.columns = format_5_reformat
        cols_to_add = set(ideal_format) - set(format_5_reformat)

    # Ponemos a nulo aquellas columnas que no se encontraban originalmente en la tabla, pues no sabemos su valor
    for c in cols_to_add:
        df_aux[c] = None

    df_aux['Fecha'] = var_date # añadimos la columna fecha
    
    df = pd.concat([df, df_aux.reset_index(drop=True)], axis=0)

Eliminamos los totales para limpiar el dataset:

df = df[df['CCAA'] != 'ESPAÑA']
df = df[df['CCAA'] != 'Total']

Por último, lo exportamos a csv:

df.to_csv('data.csv', sep='\t', index=False, encoding="latin1")

Este script se debería lanzar diariamente, pues sabemos que los datos se consolidan cada día alrededor de las 21 de la noche. Esta información y protocolo para la carga de datos se ha realizado sobre los datos del Covid-19. Sin embargo, es una tarea común a todos los proyectos de análisis de datos, pudiéndose extrapolar la mayoría del proceso.

Resultado

Podemos obtener un resultado uniforme y listo para trabajar con la visualización con herramientas como Power BI:

Conclusión

En este blog hemos querido enseñaros alguna de las técnicas que empleamos para realizar la extracción de datos de orígenes poco frecuentes. ¿Os imagináis el trabajo que supondría tener que extraer y consolidar manualmente estos datos desde miles de documentos? Y además añadiendo el incremento de probabilidad de error si nos dedicamos a copiar esos datos manualmente.

En SolidQ contamos con infinidad de recursos para que, independientemente del origen de datos, la extracción y consolidación, se realice de forma eficiente y efectiva.

Recursos

Este post tendrá el código estático. No obstante, podéis tener acceso al código actualizado diariamente accediendo a mi GitHub.

Si quieres aprender cómo utilizar herramientas como Power BI y modelar y visualizar tus datos, consulta todos nuestros cursos de 0 a experto con Power BI, desde nuestro curso de Power BI para usuarios de negocio hasta formación más avanzada como DAX, Data Governance o Power Query.

Pablo Corral