Serie Data Science: Extraer y Transformar los datos (2 de 3)

Hoy en día nos encontramos cada vez más conectados a internet, significando esto que consumimos más información y por consecuencia la generamos en igual o mayor proporción. Todo este mar de datos es una ventaja competitiva para las organizaciones que son capaces de capturarlos y obtener un verdadero valor de ellos. Esto lo pudimos ver en detalle en el artículo anterior - "Serie Data Science: Los datos y roles (1 de 3)", en el cual se explica el valor de los datos, los distintos tipos que podemos encontrar, las alternativas para su almacenamiento, además de definir los roles y procesos que entran en juego al momento de contratar este tipo de servicios.

Sin embargo en este artículo nos centraremos en aspectos más técnicos, como son: La extracción y transformación de datos. Para entender cómo esto puede permitirnos accionar la data y comenzar a generar nuevo valor.

Sobre este artículo

Este es el segundo artículo de la serie sobre Data Science. Cada artículo se puede leer independiente del orden ya que el contenido está separado por distintas etapas que, a pesar de tener una fuerte conexión se pueden entender individualmente. Cada publicación busca dar luz sobre los procesos que se llevan a cabo en la industria y que podrían ayudarte a decidir si tu organización puede contratar un servicio para migrar tus datos a la nube o aprender acerca de cómo funciona el desarrollo de este tipo de proyectos en caso de que seas estudiante. Hay algunos conceptos que se dan por sabidos, los cuales son explicados en la serie por lo que se recomienda dar una lectura completa en caso de que algunas palabras claves suenen desconocidas.

Existen distintos profesionales involucrados en el desarrollo de proyectos de Science, sin embargo en este caso nos centraremos en el rol que desempeña el profesional de Data Engineer.

¿Qué rol desempeña un Data Engineer?

El Data Engineer construye flujos ETL para que los datos puedan ser consultados fácilmente y con el formato apropiado desde distintas plataformas. Dichos flujos contienen una serie de transformaciones que no son más que scripts que deben ser programados para modificar y enriquecer la data. Es de esperar que el Data Engineer cree automatizaciones para ejecutar periódicamente el flujo y los procesos ETL. En esencia el Data Engineer se encarga de que el resto de roles tengan datos para analizar. A continuación se explica en detalle en qué consiste el flujo ETL.

Enriquecer la data: Es cualquier proceso desde el cual un conjunto de datos pasan por una transformación y resultan en la creación de nuevas columnas o nuevos datos a la misma. También puede resultar en estandarizar y limpiar la data tras pasar por la transformación.

Flujo ETL

Para entender desde donde extraer nuestros datos y por qué debemos transformarlos, debemos primero entender qué es el flujo ETL.

ETL son las siglas de Extract, Transform y Load. ETL es un proceso que extrae información de distintas fuentes de datos hacia un Data Lake, transforma los datos en una ruta o zona y finalmente los carga en un Data Warehouse para que sean consumidos.

Extracción

En la extracción se recogen datos de varias fuentes, aquellos datos seleccionados son almacenados en una staging area. Suele ser bastante común que se tomen como fuentes de datos archivos extraídos desde SAP u otras plataformas similares que gestionen la data empresarial.

Staging area: Es un área destinada enteramente para el procesamiento de datos, es desde donde se toman los datos para realizar las transformaciones y al mismo tiempo es donde son almacenados los archivos de procesamiento los cuales consisten en archivos que son generados y que serán utilizados por otros pasos de transformación del proceso ETL.

Durante el proceso de extracción los datos seleccionados pueden poseer varios formatos, entre ellos podemos encontrar:

  • Tablas
  • Bases de datos SQL
  • Bases de datos NoSQL
  • Data no estructurada
  • Archivos de texto

Es importante dejar los datos de extracción en un staging area debido a que es muy común que los datos para extracción contengan data poco consistente, sucia y con una variedad de formatos impredecible. De esta manera al ubicar los datos en un espacio concreto pueden ser leídos por el proceso y corregir todos sus errores mediante transformaciones, de este modo podemos generar nueva data, con el formato correcto y lista para ser analizada.

Se considera data poco consistente a aquella información que de una misma columna o documento posee valores con formatos distintos. Por ejemplo si tenemos una columna con valores de fecha y recibimos algunos valores con formato 2021/11/09 y otros con formatos 2022-03-10 14:30, etc.

Transformación

En el proceso de transformación se toman los datos que se cargaron durante la extracción y son convertidos a un formato estándar con el fin de poder cargarlos a otra zona del Data Lake o a un Data Ware House y de esta manera obtener resultados que analizar o presentar.

Las transformaciones son realizadas múltiples veces con distintos scripts durante un mismo flujo o pipeline. Estas son realizadas generalmente en scripts que pueden estar construidos con distintos lenguajes y librerías. Lo más común es utilizar Python como lenguaje de programación y Pandas o PySpark como librería para trabajar con datos.

Pipeline de datos: Son una serie de procesos de datos que son ejecutados en serie en donde cada proceso posee una entrada y una salida que continúa el flujo.

En los scripts de transformación se realizan tareas secuenciales para asegurar que todos los elementos procesen determinadas tareas como pueden ser:

  • Aplicar formato: Podemos definir el tipo de dato de cada columna y realizar transformaciones según el tipo de dato asignado.
  • Aplicar filtros: Para obtener solo los valores que necesitamos.
  • Limpiar columnas: De esta manera podemos aplicar una transformación sobre los valores vacíos o nulos y al mismo tiempo unificando valores que se encuentren repetidos o registrados de forma distinta pero que apunten a lo mismo.
  • Integrar datos: Podemos obtener datos nuevos al integrar dos fuentes de datos o columnas distintas.

Carga

En este proceso se toma la data transformada y se carga en un Data Warehouse. Este es el resultado final del proceso ETL y la idea es que todo el flujo desde la extracción hasta la carga sea ejecutado en determinados periodos de tiempo en forma regular.

Una vez los datos ya se encuentran disponibles en un Data Warehouse pueden ser consultados y utilizados para visualizar los datos desde un sistema de reportes como Tableau o Google Data Studio. A continuación se explorara en profundidad el segundo proceso, es decir cómo son transformados los datos.

Accionar la data

Poder aprovechar la data disponible es esencial para los organizaciones, aún más si se quiere transitar el negocio en un proceso de transformación digital. Una de las formas más comunes de aprovechar la data es tomando dos o más datasets y obtener nuevos resultados al mezclar la data.

Podemos construir un nuevo dato basándonos en algún criterio, sí tomamos un dataset de ventas y otro de proveedores, al relacionarlos podemos obtener una nueva columna que nos indique la antigüedad del proveedor o construir un nuevo valor al unir el contenido de valores de ambos datasets.

Dataset: Los datasets son conjuntos de distintos datos que están cohesionados o son generados desde la misma fuente. Generalmente poseen un formato tabular, es decir: Poseen filas y columnas. Un ejemplo de esto serían los datos generados por un usuario al realizar una compra por internet.

Mapeo de datos

Para comenzar a sacarle provecho a la información de una organización primero es necesario tener mapeada la data. Para hacer esto se puede generar un mapa de data, el cual consiste en un diagrama donde se identifican todas las fuentes de datos generadas en los procesos de negocio.

El componente base desde el cual comenzar a identificar la data que está siendo generada son los datasets. Podemos agrupar un conjunto de datasets bajo un mismo origen para conformar un Bucket de datos. Por ejemplo podemos tener un Bucket con todos los datasets de Usuarios/Clientes, otro de data Corporativa y finalmente un Bucket de data de la Industria/Competencia.

Identificar data no mapeada

Estos tres Buckets conforman el mapa de la data. Si se agregan más datasets a los Buckets se podrán sacar conclusiones mucho más enriquecedoras con esta nueva información encontrada.

Sin embargo pueden ocurrir ocasiones en que existan datos que no estamos mapeando ni ubicando correctamente, pero que sabemos que existen. En este caso podemos comenzar desde los Buckets para determinar que dataset nos están haciendo falta. Por ejemplo: Mi organización está generando la data unas operaciones que aun no hemos capturado, entonces en el mapa de data dejamos indicado que esta pendiente obtener un dataset en el Bucket de data Corporativa.

Patrones de cambio de nube

Las migraciones a la nube permiten que los sistemas y soluciones puedan acceder a distintos beneficios como son: desarrollo de soluciones más ágiles y seguras, poder trabajar la data desde la nube ahorrando costos y generando nuevo valor. Además si la solución on premise está obsoleta o legacy se puede transformar completamente para integrar otros servicios y modernizarla.

Trasladar soluciones a la nube no es un proceso que obedezca a las mismas reglas siempre, todo depende del contexto. Existen al menos cinco patrones que se pueden adoptar cuando se quiere modernizar una solución on premise con la nube.

On-premise: Se refiere a aquellas aplicaciones o sistemas que se han desarrollado y que se ejecutan de manera local o en la infraestructura propia del negocio.

01. Mover y cambiar

Este patrón consiste en mover la aplicación a la nube, realizar modificaciones mínimas para que opere y una vez se encuentre corriendo se puede actualizar y mejorar la solución original de forma más simple y ágil.

02. Cambiar y mover

El siguiente patrón es similar al anterior solo que los pasos se invierten. Se modifica la solución on premise para prepararla a la nube y una vez lista, se realiza la migración para comenzar a ejecutar la aplicación y aplicar cualquier cambio o mejora que se necesite contando con los servicios asociados al proveedor de la nube seleccionado.

03. Mantener y extender

Consiste en construir desde cero una nueva solución desde la nube que sea una extensión o se conecte con la aplicación on premise. Un ejemplo de este tipo de patrones son la creación de APIs para conectar la aplicación local con otros servicios o aplicaciones que no se pueden integrar en la aplicación original.

04. Mantener y reemplazar

Otra alternativa muy común es mantener la aplicación on premise operando mientras se crea una nueva aplicación en el ambiente de la nube para que una vez desarrollada reemplace a la solución on premise y de esta manera comenzar a obtener mayor valor y agilidad para el negocio.

05. SOLO Mover

En algunos casos basta solo con liberar la aplicación o los datos para modernizar la capa de infraestructura. Esto se puede realizar con productos más simples o comunes. Como por ejemplo: migrar la data de la organización a la nube o modernizar la infraestructura de una aplicación creando un ambiente virtualizado para recuperación de desastres, entre muchos otros casos.

A continuación veremos un caso concreto en donde se aplican transformaciones a un caso concreto.

Transformar la data

Como se describió anteriormente transformar la información nos ayuda a sacarle el máximo provecho a los datos de los cuales disponemos, al mismo tiempo otorga un mayor control sobre los mismos para poder analizarlos.

Caso de uso

Para demostrar cómo son realizadas las transformaciones utilizaremos la construcción de un reporte a modo de ejemplo.

Este reporte on premise puede tomar una determinada cantidad de tiempo en realizarse y es construido tomando muchas fuentes de datos distintas, las cuales manualmente deben ser consultadas y configuradas para obtener las métricas deseadas. Luego se reúne toda la data en un Excel y se aplican varios cálculos. El reporte final tendría un formato similar a este:

El reporte resultante simula los datos de una empresa distribuidora de distintos productos, podemos ver que es un reporte que posee información concentrada de los resultados obtenidos con algunos productos, además del cálculo del presupuesto y Forecast para cada uno. Esta es la tabla resultado y para llegar a ella deberemos realizar transformaciones sobre distintas fuentes, específicamente una serie de datos transaccionales y maestros.

Datos transaccionales

Los datos transaccionales son aquellos que son procesados cada ciertos periodos de tiempo, los cuales cada vez que son procesados suelen traer datos nuevos, modificaciones de los datos anteriores o los mismos datos.

En nuestro ejemplo contamos con varios datos transaccionales, los cuales denominaremos como venta_ropa.csv, venta_accesorios.csv y costos_margen.csv

Lo que haremos es tomar las fuentes de ventas y agruparemos datos basándonos en tipo_producto. Al mismo tiempo necesitamos relacionar los datos de estos datos con la fuente maestra para obtener los datos descriptivos que serán usados para crear las columnas nombre y descripción.

Datos maestros

Los datos maestros o tablas maestras son aquellos datos que no suelen modificarse a lo largo del tiempo y poseen valores ya definidos. En el ejemplo se trata de tablas que poseen los códigos de cada producto junto con su descripción.

En nuestro ejemplo utilizaremos un solo dato maestro que denominaremos tipo_producto_detail.csv

Tanto los datos transaccionales como los maestros tendrían un formato similar al siguiente:

Transformaciones aplicadas al caso

En este caso las transformaciones las resolveremos utilizando PySpark, se trata de una librería para procesos de Big Data que esta pensada para trabajar sobre Datalakes y Data Warehouses. Se centra en la velocidad del procesamiento y se puede utilizar Spark en Python, Java, R y Scala. En nuestro caso utilizaremos Python como lenguaje de programación.

Al comenzar a crear los scripts es normal que primero se comience desarrollando unos scripts denominados scen, los cuales contienen toda la lógica y las pruebas para construir el script y posteriormente, una vez probado este script se convierte a la versión de desarrollo con ligeras modificaciones.

Leer datos

Para leer los datos en PySpark primero hacemos uso de read.format() para indicar el tipo de archivo que vamos a leer, posteriormente con .option() seleccionamos opciones por separado, en este caso lo utilizamos para definir el delimitador del archivo y si incluye o no headers. Finalmente con .load() indicamos la ruta donde se encuentra el archivo.

Para leer los datos en PySpark primero hacemos uso de read.format() para indicar el tipo de archivo que vamos a leer, posteriormente con .option() seleccionamos opciones por separado, en este caso lo utilizamos para definir el delimitador del archivo y si incluye o no headers. Finalmente con .load() indicamos la ruta donde se encuentra el archivo.

En el ejemplo leemos solo venta_ropa.csv, pero la lógica es la misma para el resto de fuentes.

Headers: Primera fila de la tabla que contiene los nombres de cada columna.

-- CODE language-python -- # Leemos el archivo ropa_df = spark.read.format('csv').option('delimiter', ';').option('header', False).load('/datasets/transactional/venta_ropa.csv') # Asignamos los headers ropa_df = ropa_df.toDF( 'NOMBRE', 'CODIGO_PRODUCTO', 'FECHA', 'CANTIDAD', ... )

Corregir tipos de datos

En este caso creamos la función set_correct_data_types la cual se encarga de estandarizar los datos para que asignar el tipo de dato correcto a cada columna y al mismo tiempo realizar modificaciones sobre dichos valores dependiendo del tipo de dato asignado.

En este ejemplo de código corregimos los valores con signo negativo a la izquierda, formatos erróneos en valores de texto y formatos de fecha.

-- CODE language-python -- def set_correct_data_types(df): columns_types_dict = { 'FECHA': DateType(), 'CANTIDAD': IntegerType(), 'US_VENTAS': DoubleType(), 'NOMBRE': StringType(), 'CODIGO_PRODUCTO': IntegerType(), ... } for column_name, column_type in columns_types_dict.items(): if column_type == DoubleType(): column = col(column_name) negative_col = concat(column.substr(-1,1), column.substr(lit(1), length(column)-1)) df = df.withColumn( column_name, when( column.endswith('-'), negative_col.cast(column_type) ).otherwise(column.cast(column_type)) ) elif column_type == DateType(): df = df.withColumn( column_name, to_date(col(column_name), 'yyyyMMdd') ) elif column_type == StringType(): df = df.withColumn( column_name, regexp_replace(column_name, '"', '') ) return {'add_correct_data_types': df}

Obtener relaciones maestras

En nuestro ejemplo tenemos un dato maestro denominado tipo_producto_detail.csv el cual posee los valores descriptivos utilizados para construir dos de las columnas del reporte final.

Los valores transaccionales como venta_ropa.csv tienen una columna denominada codigo_producto la cual al relacionarla con la columna codigo del archivo maestro tipo_producto_detail.csv permite obtener el nombre código de producto y la descripción del producto que renombraremos descripcion. El resultado es que conseguimos agregar dos columnas adicionales a ventas_ropa.csv al relacionarse con el archivo maestro, tal y como se muestra en la siguiente imagen:

En el código utilizamos .join para obtener las columnas que necesitamos al hacer coincidir la columna codigo_producto con codigo.

-- CODE language-python -- def add_tipo_producto_descr(df): maestro = dataframes_dict['tipo_producto_detail'].select('codigo', 'nombre') df = df.join(maestro, df.codigo_producto == maestro.codigo, 'left') \ .withColumnRenamed('nombre', 'nombre') \ .drop('codigo') return {'add_tipo_producto': df} def add_descripcion_descr(df): maestro = dataframes_dict['tipo_producto_detail'].select('codigo', 'desc') df = df.join(maestro, df.codigo_producto == maestro.codigo, 'left') \ .withColumnRenamed('desc', 'descripcion') \ .drop('codigo') return {'add_descripcion': df}

Filtrar para generar nuevos datos

Podemos utilizar los filtros típicamente para obtener solo aquella información que es relevante para nuestro objetivo. Sin embargo, como veremos en el siguiente caso, también se puede utilizar para construir nuevos datos.

Con withColumn() podemos modificar el valor de una columna o crearla si agregamos el nombre de una columna que no existe. Luego con when podemos agregar una condición y el valor que se aplicará en caso de que esta condición coincida. En el ejemplo lo utilizamos para crear la columna categoria.

-- CODE language-python -- def add_category_filter(df): df = df.withColumn( 'CATEGORIA', when( (df.tipo_producto == 'Polera') | (df.tipo_producto == 'Poleron') | (df.tipo_producto == 'Gorro') \ , lit('Ropa').cast(StringType()) \ .when( (df.tipo_producto == 'Lentes') | (df.tipo_producto == 'Billetera') \ , lit('Accesorios').cast(StringType()) \ .otherwise('') )

Entendiendo el resultado

En base a lo que vimos anteriormente podemos describir las transformaciones mediante la siguiente figura:

Lo que hicimos fue tomar dos fuentes de datos que compartían características, venta_ropa.csv y venta_accesorios.csv, realizamos procesos de estandarización (Script de corrección de datos), obtuvimos nuevas columnas al relacionar una columna del archivo original con la fuente maestra (Script de relaciones maestras) y mediante un proceso de filtrado generamos una nueva columna al hacer coincidir una condición (Script de filtrado). Adicionalmente podríamos tomar las referencias aprendidas y realizar cálculos más complejos para por ejemplo, utilizar la tabla transaccional de costos_margen.csv y de esta manera obtener los valores de Forecast.

Adicionalmente, debido a que los datos ahora se encuentran categorizados por la columna categoria podríamos obtener la sumatoria de las columnas numéricas, de esta manera extenderemos el alcance y propósito de nuestras transformaciones.

En el próximo artículo de la serie veremos en detalle el siguiente paso del flujo ETL, la carga de datos y la cuadratura de los resultados. Esperamos que el artículo haya sido de ayuda, si queda alguna duda o tu organización necesita ayuda para resolver proyectos de este tipo no dudes en contactarnos.

Carlos Brignardello

September 16, 2024