Creación y uso de UDFs en PySpark para manejo de datos faltantes

Clase 21 de 25Curso de Fundamentos de Spark para Big Data

Contenido del curso

Data Frames y SQL

Resumen

Cuando trabajas con archivos CSV que contienen valores ausentes, las transformaciones estándar de Spark pueden no ser suficientes. Aquí es donde las UDF (User Defined Functions) se convierten en una herramienta esencial, ya que permiten registrar funciones personalizadas que operan de forma nativa dentro de tus DataFrames y consultas SQL. A continuación se explica paso a paso cómo cargar un archivo problemático, construir un esquema y aplicar una UDF para convertir valores vacíos en null.

¿Por qué un archivo con valores ausentes requiere una UDF?

El archivo deportista_error.csv presenta celdas vacías en columnas que deberían contener datos numéricos como altura o peso [01:02]. Cuando se carga directamente, esos campos quedan como cadenas vacías en lugar de reflejar la ausencia de valor. Para que Spark los interprete correctamente como null, necesitamos una función que evalúe cada celda y decida si puede convertirse a entero o debe devolver None.

¿Cómo se carga el RDD y se elimina el encabezado?

  • Se crea el RDD con spark.textFile apuntando a la ruta del archivo deportista_error.csv [01:22].
  • Se aplica un .map con una lambda que separa cada línea por coma: lambda l: l.split(',') [01:38].
  • Se reutiliza la función elimina_encabezado, que recibe un índice y un iterador, y devuelve todos los elementos menos el primero [01:52].
  • Se invoca con mapPartitionsWithIndex para retirar la fila de encabezado respetando la inmutabilidad del RDD, es decir, reasignando el resultado a una nueva variable [02:15].

¿Cómo se construye el esquema del DataFrame?

Se define un StructType con varios StructField, todos inicialmente de tipo StringType [03:10]. Los campos son: deportista_id, nombre, género, altura, peso y equipo_id. Mantenerlos como cadena en esta etapa es intencional, porque la conversión a entero se delegará a la UDF.

El DataFrame se genera con sqlContext.createDataFrame, pasando el RDD remapeado y el esquema como parámetros [03:55]. Al visualizarlo, las celdas vacías aparecen como cadenas sin contenido, no como null.

¿Cómo se crea y registra una UDF en PySpark?

Primero se importa udf desde pyspark.sql.functions [04:30]. Luego se define la función Python que contendrá la lógica:

python def conversion_enteros(valor): if len(valor) > 0: return int(valor) else: return None

Esta función verifica si la longitud del valor es mayor a cero. Si lo es, lo convierte a entero; de lo contrario, retorna None, que Spark interpreta como null en el DataFrame [04:42].

El siguiente paso es envolver la función en un objeto UDF:

python conversion_enteros_udf = udf(lambda z: conversion_enteros(z), IntegerType())

La convención de usar z como variable en la lambda es habitual al registrar UDFs [05:15]. El segundo argumento, IntegerType(), indica el tipo de dato que devolverá la función.

Finalmente se registra en el contexto SQL para que Spark la reconozca de forma nativa:

python sqlContext.udf.register("conversion_enteros_udf", conversion_enteros)

Con register se asigna un nombre con el cual Spark podrá invocarla incluso desde consultas SQL puras [05:35].

¿Cómo se aplica la UDF al DataFrame?

Se utiliza select sobre el DataFrame y se pasa la columna altura a través de la UDF [05:55]:

python deportista_error_df.select( "altura", conversion_enteros_udf("altura").alias("altura_udf") ).show()

  • La columna original altura muestra las cadenas vacías.
  • La nueva columna altura_udf ya refleja valores null donde no existía dato y enteros donde sí.
  • El método .alias() permite renombrar la columna resultante para mayor claridad [06:15].

¿Por qué las UDF son tan relevantes en la industria?

En entornos productivos es común contar con librerías completas de UDFs que encapsulan operaciones repetitivas de limpieza y transformación [06:50]. Algunas UDFs incluso registran múltiples RDDs y ejecutan toda la curaduría de datos en un solo paso. Al registrarlas, quedan disponibles tanto para la API de DataFrames como para consultas SQL, lo que las hace extremadamente versátiles.

Si ya lograste cargar y limpiar tu primer archivo con una UDF, comparte en los comentarios qué otras transformaciones te gustaría automatizar con funciones personalizadas.