¿Cómo usar expresiones regulares para definir algoritmos de tokenización en Python?
Las expresiones regulares son una herramienta poderosa que nos permite manipular texto de manera eficiente. En particular, cuando se trata de procesar lenguaje natural, podemos utilizarlas para definir algoritmos de tokenización en Python. En esta clase, exploraremos cómo aprovechar las expresiones regulares con la librería NLTK para tokenizar texto y limpiar nuestro corpus para análisis.
¿Qué es una cadena de texto RAW y cómo se utiliza?
Para que Python interprete una cadena de texto sin reconocer caracteres especiales, debemos utilizar el prefijo r delante de la cadena. Esto indica que Python debe tratar los caracteres especiales como texto plano, también conocido como texto "RAW".
Por ejemplo:
print(r"Esta es una cadena con una nueva línea \n que será mostrada tal cual.")
El uso de la r convierte a los caracteres especiales en parte del texto y no en comandos ejecutables dentro de la cadena, lo que es esencial cuando tratamos con texto que incluye caracteres de escape.
¿Cómo tokenizar texto con espacios vacíos?
Tokenizar texto es el proceso de dividirlo en unidades más pequeñas, conocidas como "tokens". El método más básico de tokenización es mediante la separación por espacios vacíos, y esto se puede lograr fácilmente utilizando la función split de la librería re.
import re
texto ="Cuando sea el rey del mundo, (imaginaba él en su cabeza) no tendré que preocuparme por estas bobadas."tokens = re.split(r'\s+', texto)
Aquí, el uso de \s+ permite dividir el texto en base a uno o más espacios vacíos, creando tokens individuales para cada palabra.
¿Cómo mejorar la tokenización con expresiones regulares?
Para lograr un nivel de tokenización más sofisticado, podemos desarrollar expresiones regulares que filtren caracteres especiales no deseados.
Consideremos la siguiente expresión regular:
tokens = re.split(r'[ \t\n]+', texto)
Esta expresión busca espacios, tabulaciones y nuevas líneas, proporcionando una tokenización más limpia al eliminar estos caracteres del proceso.
¿Cómo crear un tokenizador sofisticado con NLTK?
A medida que los textos se vuelven más complejos, con caracteres y símbolos especiales, las expresiones regulares básicas no son suficientes. Aquí es donde NLTK ofrece una excelente solución con la función RegexpTokenizer.
Una aplicación avanzada de esta técnica se puede ver como sigue:
from nltk.tokenize import RegexpTokenizer
pattern =r'\b\w+\b'tokenizer = RegexpTokenizer(pattern)tokens = tokenizer.tokenize(texto)
El patrón \b\w+\b permite capturar palabras completas, evitando caracteres que no contribuyen al contenido semántico del texto.
¿Por qué usar Regex Token Eyes de NLTK?
Cuando enfrentamos textos altamente complejos, con abreviaciones, números decimales y otros elementos, Regex Token Eyes de NLTK se convierte en un recurso inestimable. Este tokenizador emplea expresiones regulares complejas para lograr una tokenización precisa.
from nltk.tokenize import RegexpTokenizer
pattern =r"\b\w+\b(?:[.\-]\w+)*"tokenizer = RegexpTokenizer(pattern)tokens = tokenizer.tokenize("En los EE.UU., esa postal vale quince cincuenta dólares...")
Así, podemos capturar correctamente acrónimos, precios y otros casos especiales como tokens únicos, mejorando significativamente la calidad del análisis.
Con herramientas como estas, estás en capacidad de construir un tokenizador tanto básico como avanzado para diversas aplicaciones en procesamiento de lenguaje natural. ¡Sigue explorando y sorprendiendo al mundo con tus habilidades en Python!
Les dejo el patrón visto en clase, con explicaciones en español que hice 😄
pattern =r'''(?x) # Flag para iniciar el modo verbose
(?:[A-Z]\.)+ # Hace match con abreviaciones como U.S.A.
| \w+(?:-\w+)* # Hace match con palabras que pueden tener un guión interno
| \$?\d+(?:\.\d+)?%? # Hace match con dinero o porcentajes como $15.5 o 100%
| \.\.\. # Hace match con puntos suspensivos
| [][.,;"'?():-_`] # Hace match con signos de puntuación
'''
Super genial este aporte!
Excelente aporte, me funcionó bastante bien!
texto ="""Cuando sea el rey del mundo(imaginaba él en su cabeza) no tendré que preocuparme por estas bobadas.Era solo un niño de 7 años, pero pensaba que podría ser cualquier cosa que su imaginación le permitiera
visualizar en su cabeza ..."""
gracias
En lugar de usar [\t\n]+ para definir un rango de espacios, tabs, etc. pueden usar el metacaracter \s, específicamente diseñado para reconocer diferentes clases de espacios ;)
Graciass Facundo, este truco me sirvió
print(re.split(r'[\s\t\n]+', texto))
Más o menos así me sentí en el minuto 8:20
El metacaracter \W lo que hace es hacer match con todo lo que no sea un caracter alfanumérico (como paréntesis, símbolos raros, etc.)
texto ='En los E.U. esa postal vale $15.50 ...'
Normalización de texto (como aplicación de las expresiones regulares)
# Imprimimos el texto con un salto de lineaprint('esta es \n una prueba')# Forzamos a Python a que nos imprima el texto como texto plano a pesar de los caracteres especiales como el salto de linea. print(r'esta es \n una prueba')
Tokenización: Es el proceso mediante el cual se sub-divide una cadena de texto en unidades lingüísticas mínimas (palabras)
# Ejemplo:texto =""" Cuando sea el rey del mundo(imaginaba él en su cabeza) no tendré que preocuparme por estas bobadas.Era solo un niño de 7 años, pero pensaba que podría ser cualquier cosa que su imaginación le permitiera
visualizar en su cabeza ..."""
# Caso1:Tokenizar por espacios vacíos
print(re.split(r' ',texto))# Caso2:Tokenizar usado expresiones regulares usando regex
print(re.split(r'[ \t\n]+', texto))# Caso3:Usando el parámetro Wprint(re.split(r'[ \W\t\n]+', texto))
Tokenizando con NLTK
texto ='En los E.U. esa postal vale $15.50 ...'print(re.split(r'[ \W\t\n]+', texto))pattern = r'''(?x) # Flag para iniciar el modo verbose(?:[A-Z]\.)+ # Hace match con abreviaciones como U.S.A.| \w+(?:-\w+)* # Hace match con palabras que pueden tener un guión interno
| \$?\d+(?:\.\d+)?%? # Hace match con dinero o porcentajes como $15.5 o 100%| \.\.\. # Hace match con puntos suspensivos
|[][.,;"'?():-_`] # Hace match con signos de puntuación
'''
nltk.regexp_tokenize(texto, pattern)
¿Que es el "modo verbose" ?
Parece que el modo verbose solo te deja agregar comentarios y que la expresion regular se vea mas bonita y comprensible para el desarrollador. Abajo dejo dos ejemplos
<# Sin usar modo VERBOSEregex_email = re.compile(r'^([a-z0-9_\.-]+)@([0-9a-z\.-]+)\.([a-z\.]{2, 6})$', re.IGNORECASE)# Usando modo VERBOSEregex_email = re.compile(r"""
^([a-z0-9_\.-]+) # local Part @ # single @ sign([0-9a-z\.-]+) # Domain name
\. # single Dot.([a-z]{2,6})$ # Top level Domain""",re.VERBOSE| re.IGNORECASE)>
Excelente clase!.
Creo que cabe aclarar que la primer expresión regular "[ \W\t\n]+" se enfocaba en buscar:
espacios.
espacios de TAB
finales de linea y caracteres que no fuesen palabras, números o puntos.
Y dividir en donde hiciera match.
Pero la segunda expresión regular "(?x) ..." se enfocaba hacer match con la palabras, frases y/o expresiones.
Tokenización con expresiones regulares
Python no interpreta el texto raw por defecto, si no que admite cosas espciales como tabs con \t o enters con \n. Podemos indicarle que lea solo raw:
print('Esta es \n una prueba')print(r'Esta es \n otra prueba')
Tokenización: Es el proceso mediante el cual se sub-divide una cadena de texto en unidades linguísticas minimas (palabras)
texto =""" Cuando sea el rey del mundo (imaginaba él en su cabeza) no tendré que preocuparme por estas bobadas.
Era solo un niño de 7 años, pero pensaba que podría ser cualquier cosa que su imaginación le permitiera
visualizar en su cabeza ..."""
Caso 1: Tokenizar por espacios vacíos. Aquí hay ruido, caracteres que no son parte de las palabras y queremos remover.
print(re.split(r' ', texto))
Caso 2: Tokenizador usando expresiones regulares
print(re.split(r'[ \W\t\n]+', texto))
Tokenizador de NLTK
Si usamos lo anterior, nos ignora abreviaciones, precios, como esto:
texto ='En los E.U. esa postal vale $15.50 ...'print(re.split('[ \W\t\n]+', texto))
Para poder considerar esto, debemos usar expresiones regulares mucho más complejas, como esta:
pattern =r'''(?x) # Flag para iniciar el modo verbose
(?:[A-Z]\.)+ # Hace match con abreviaciones como U.S.A.
| \w+(?:-\w+)* # Hace match con palabras que pueden tener un guión interno
| \$?\d+(?:\.\d+)?%? # Hace match con dinero o porcentajes como $15.5 o 100%
| \.\.\. # Hace match con puntos suspensivos
| [][.,;"'?():-_`] # Hace match con signos de puntuación
'''nltk.regexp_tokenize(texto, pattern)
Diablos, ahora otro curso a la lista de pendientes... Por qué elegí esta carrera? Jaja!
NLTK puede tokenizar por palabras directamente el útlimo ejemplo (el que tiene trampas para el regex).
Me parece mucho más limpio que definir esa regex que no cubre todos los casos.
(?x) – Modo verboso
Este es un modificador de la expresión regular que indica que la expresión está en modo verboso. Esto permite añadir espacios en blanco y comentarios en la expresión regular para hacerla más legible, sin afectar su funcionamiento.
En resumen, (?x) permite que la expresión esté escrita de forma más comprensible para los humanos.
(?:[A-Z]\.)+ – Abreviaturas con letras mayúsculasEjemplo de coincidencia:
[A-Z]: Captura cualquier letra mayúscula (de la A a la Z).
\.: Captura un punto literal ..
(?: ... ): Esta es una agrupación no capturadora, es decir, no guarda el contenido entre paréntesis como parte del resultado final.
+: El + indica que se pueden capturar una o más ocurrencias del patrón que se encuentra antes (es decir, más de una letra seguida de un punto).
Propósito: Este patrón se utiliza para capturar abreviaturas formadas por letras mayúsculas seguidas de puntos, como "U.S.A." o "E.U.".
<!---->
"U.S.A.", "E.U.", "M.B.A."
\w+(?:-\w+)* – Palabras con o sin guionesPropósito: Captura palabras simples o palabras compuestas con guiones.Ejemplo de coincidencia:
\w+: Captura una o más ocurrencias de caracteres de palabra, que incluyen letras, dígitos o el carácter subrayado (_). Equivale a [a-zA-Z0-9_].
(?:-\w+)*: Captura palabras compuestas con guiones. Esto permite que el patrón coincida con palabras separadas por guiones, como "auto-retrato" o "bien-estar".
-: Captura un guion literal.
\w+: Captura otra palabra después del guion.
*: Permite que este patrón ocurra cero o más veces (lo que significa que una palabra puede tener cero o más guiones).
<!---->
"palabra", "bien-estar", "auto-retrato".
\$?\d+(?:\.\d+)? – Números con o sin decimales y con o sin signo de dólarPropósito: Captura números enteros, números decimales, y cantidades monetarias precedidas por un signo de dólar.Ejemplo de coincidencia:
\$?: Captura un signo de dólar $ opcional (es decir, si está o no presente).
\d+: Captura uno o más dígitos, es decir, un número entero.
(?:\.\d+)?: Captura un punto decimal seguido de uno o más dígitos. Es opcional gracias al signo ?, lo que significa que la parte decimal puede o no estar presente.
<!---->
"42", "$19.99", "1000", "3.14".
\.\.\. – ElipsisPropósito: Captura secuencias de elipsis en el texto.Ejemplo de coincidencia:
\.: Captura un punto literal.
\.\.\.: Captura tres puntos seguidos (es decir, una elipsis).
<!---->
"..."
[][.,;"'?():-_]` – Caracteres de puntuación específicosPropósito: Captura ciertos caracteres de puntuación comunes en los textos.Ejemplo de coincidencia:
[]: Dentro de los corchetes, se especifica un conjunto de caracteres que pueden coincidir.
[.,;"'?():-_]`: Captura cualquier carácter de puntuación que esté dentro de este conjunto:
.: Punto.
,: Coma.
;: Punto y coma.
": Comillas dobles.
': Comillas simples.
?: Signo de interrogación.
(): Paréntesis izquierdo o derecho.
:: Dos puntos.
-: Guion.
_: Subrayado.
`: Acento grave.
<!---->
".", ",", ";", "(", ")", etc.
Uso del patrón ennltk.regexp_tokenize()
La función nltk.regexp_tokenize() toma un texto y lo divide en tokens según los patrones especificados en la expresión regular. A continuación te muestro cómo se utiliza el patrón:
A alguno no le dio la tokenización? A mí me sale así:
pattern = r'''(?x) # Flag para iniciar el modo verbose(?:[A-Z]\.)+ # Hace match con abreviaciones como U.S.A.| \w+(?:-\w+)* # Hace match con palabras que pueden tener un guión interno
| \$?\d+(?:\.\d+)?%? # Hace match con dinero o porcentajes como $15.5 o 100%| \.\.\. # Hace match con puntos suspensivos
|[][.,;"'?():-_`] # Hace match con signos de puntuación ],['''
nltk.regexp_tokenize(texto, pattern)
Les dejo el código que realicé , explicando lo que hace el patrón para el tokenizer
CODE:
#Utilizar comillas triples para que me permita implementar mas de un patrónpatron =r'''(?x) #Activa el modeo verbose para poder agregar espacios y dividir el regex en varias lineas
(?:[A-Z]\.)+ #?:; indica grupo no capturante es decir solamente si hay conicidencia agruparlo pero no guardarlo, [A-Z]; Toma todas las palabras mayusculas, \.; toma el punto, +;que se repita 1 o mas veces, junto; Toma 1 o mas veces las palabras de A-Z que tengan un punto
| \w+(?:-\w+)* #\w+; una o mas letras, (?:-\w+); toma palabras que contengan guiones y se repitan 1 o mas sveces, junto; toma palabras que se repitan 1 o mas veces y adicional palabras conguiones 1 o mas veces
| \$?\d+(?:\.\d+)?%? #\$?; signo dolar opcional, \d+; uno o mas digitos, (?:\.\d+)?; agregar partes decimales .98 opcional, %?; simbolo % opcional
| \.\.\. #Agregar ...
| [][.,;"'?():-_´] # signos de puntuación
'''
tokens = tokenizer.tokenize("En los EE.UU., esa postal vale quince cincuenta dólares...")
🌟 Ventajas:
Captura tokens completos y coherentes.
Evita dividir información relevante.
Mejora la calidad del análisis textual.
Que hacía el '?' al principio de una palabra??
Most modern regex flavors support a variant of the regular expression syntax called free-spacing mode. This mode allows for regular expressions that are much easier for people to read.
In free-spacing mode, whitespace between regular expression tokens is ignored. Whitespace includes spaces, tabs, and line breaks. Note that only whitespace between tokens is ignored. a b c is the same as abc in free-spacing mode. But \ d and \d are not the same
Gracias por proporcionar el pattern. Pero, hay algún repositorio de los patterns más comunes según para distintas aplicaciones?
Hola, existen muchos patrones de diseño, pero hay seis que son los principales:
Creacional
Factory method
Estructural
Adapter
Decorator
Conductual
Iterator
Observer
Strategy
Te recomiendo tambien ver los principios de diseño.
¿Existe otra forma de hacer tokenizacion? Osea que no sea usando expresiones regualres
Pues, hay paquetes con spacy o nltk que permiten hacer esto de manera más directa, y en español