Expresiones regulares en R para revisar columnas de archivos de texto
"Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems" (Tomado del libro "Regular Expressions Cookbook")
En relación al artículo anterior una de las razones por las cuales es necesario hacer manipulación de las consultas devueltas por Apache Spark es sin duda la presentación de resultados.
En este artículo vamos a presentar una forma de obtener y analizar las columnas de un archivo delimitado por algún carácter (en sus inicios la definición era Comma Separated Values -CSV- sin embargo debido a que la coma pudiera ser parte del contenido de una columna pues ahora se separan por cualquier otro caracter) y un acercamiento a sus tipos de datos.
Cómo sería muy azaroso el revisar millones de registros con las herramientas estándar del lenguaje y citando nuevamente a la que pareciera es la biblia de los usuarios de R que utilizan Spark (Mastering Spark with R) en la sección 8.2.2 nos muestra un código para realizar esto, así como el siguiente comentario:
"Cuando leemos datos, Spark es capaz de determinar el nombre de las columnas de una fuente de datos y sus tipos, a este se le conoce como el esquema. Aunque, el obtener el esquema viene con un costo; Spark necesita hacer un pase inicial a los datos para determinar que contienen. Para un conjunto de datos grande, esto puede llevar una cantidad significativa de tiempo en el proceso de ingesta de datos, lo cual puede volverse 'costoso' incluso para conjuntos de datos (datasets) de tamaño mediano. Para archivos que se leen una y otra vez, el tiempo adicional de lectura se acumula.
Para evitar esto, Spark te permite proporcionar una definición de columna proporcionando un argumento de columnas para describir tu conjunto de datos (dataset). Puedes crear este esquema por muestreo de una pequeña porción de tu archivo." [1]
En el mismo texto viene la instrucción y para ejemplificar aquí vamos a descargar los datos abiertos de la página del IMSS para ver las columnas y sus tipos tomando una muestra de 10 registros.
spec_with_r <- sapply(read.csv("ruta\\asg-2022-06-30.csv", sep = '|', nrows = 10), class)
Cada estructura de datos y más formalmente los manejadores de bases de datos definen tipos distintos de acuerdo al software que manejan, lo recomendable sería lograr un consenso y definir un estándar para la definición de dichos tipos que entiendan tanto las personas que manejan los datos de forma conceptual así como los que la implementan en una plataforma de software.
Una buena referencia sería el esfuerzo que hace el Banco Mundial y adoptar ese sistema para definir los tipos de los metadatos. Esta iniciativa es el estándar DDI (Data Documentation Initiative por sus siglas en inglés) y en este estándar se manejan cuatro tipos de datos (que en nuestro caso serían solo 3).
- Numérica
- Alfanumérica fija.
- Alfanumérica variable.
- Fecha
Entonces vamos a pasar un tema que es casi obligado en todos los lenguajes de programación como lo son las expresiones regulares.
En este ejercicio hubo mucho aprendizaje y ahonde más a profundidad en el tema logrando investigar, adecuar, probar y en el caso de los números desarrollar las expresiones regulares siguientes:
Detectar números:
expreg_num <- '^([0-9])*(\\.){0,1}([0-9])*$'
Explicación:
- Los caracteres ^ y $ significan el inicio y final de la cadena.
- La expresión ([0-9])* significa que tenemos sucesión de dígitos del 0 al 9, 0 o n veces (*).
- La expresión (\\.){0,1} significa que puede ir un punto o ninguno (en R hay que poner doble diagonal inversa ya que esta expresión la van a encontrar en otros lenguajes simplemente con una diagonal inversa)
- La expresión ([0-9])* misma explicación
Aquí dejamos un pequeño script con un vector que es analizado por la expresión regular a través de la función grepl.
Detectar fechas:
Aquí fue el gran obstáculo y aunque hubo que investigar algo más a profundidad se pudieron implementar dos expresiones regulares que se encuentra quizás en el foro donde más respuestas se encuentran (stackoverflow) [2], sin embargo al querer verificar el contenido de una columna usando Spark, la expresión regular no pudo detectar dichas fechas.
La primera expresión regular es muy sencilla son fechas del tipo día-mes-año.
expreg_fec_dma <- '^([0-2][0-9]|3[0-1])(-|\\/)(0[1-9]|1[0-2])\\2(\\d{4})$'
Explicación:
- Los caracteres ^ y $ significan el inicio y final de la cadena.
- ([0-2][0-9]|3[0-1]) aquí algo nuevo e interesante que inclusive tiene ya algo de validación el primer digito esta entre 0 y 2 acompañado del segundo dígito que puede ir de 0 a 9, con esta primera parte comprende desde el día 00 (el cual sería incorrecto) hasta el día 29 "o" - que se representa con el pipeline (|) - el dígito 3 acompañado del dígito 0 o 1.
- (-|\\/) representa el separador entre los días, meses y años el cual puede ser el guion de en medio (-) "o" (|) la diagonal (/).
- (0[1-9]|1[0-2]) la validación del mes que es el 0 acompañado del 1 al 9 para formar la serie 01 al 09, o (|) el dígito 1 acompañado del dígito 1 o 2.
- \\2 otra cosa que aprendí, al anotar esta expresión significa que el caracter que sigue tiene que ser igual este caso a la segunda expresión que sería el separador (-|\\/).
- (\\d{4}) para la última expresión es el año que nos dice que tiene que ser un dígito \\d de cuatro posiciones {4}, lo da la posibilidad de la serie 0000 a 9999.
- La explicación es la misma que la anterior solo que agrego la nueva expresión para evitar el 00 y el orden como vienen estas fechas que es año, mes y día.
- \\s es un espacio
- ([0-1][0-9]|[2][0-3]) esta es la hora que iría de las 12am (00 horas) hasta las 7pm (19 horas) o (|) bien de las 8pm (20 horas) a las 11pm (23 horas)
- (:) el caracter que divide a las horas minutos y segundos
- ([0-5][0-9]) los minutos y sus segundos que van desde 00 a 59.
Otra cosa a notar es que el uso de sdf_nrow es de Spark por lo que para usar dlpyr usamos tally() cuando leemos el archivo como CSV.
Comentarios
Publicar un comentario