Cuerpo documental
El Procesamiento de Lenguaje Natural (Natural Language Processing – NLP) es el conjunto de técnicas (herramientas, métodos, algoritmos, …) que nos sirven para que las máquinas comprendan el lenguaje que utilizamos los humanos. En este aspecto nos podríamos meter en discusiones filosóficas para concretar lo que significa «entender», pero no es el objetivo de este post.
Cuando se trata de procesar lenguaje natural en forma de texto, el conjunto de documentos que queremos procesar se denomina habitualmente corpus (palabra del Latín que significa literalmente cuerpo). Es decir, corpus es el cuerpo documental que queremos procesar y que queremos analizar para sacar conclusiones o aprender algo. Hay autores que utilizan el plural de corpus, corpora, para destacar que el cuerpo documental es amplio o está formado por colecciones de cuerpos documentales, aunque esta distinción no es realmente muy importante.
Lo verdaderamente importante es el estado en el que se encuentra nuestro corpus: ¿está limpio y libre de impurezas o por el contrario está «sucio» y tiene rugosidades? De otra forma, ¿hay caracteres no estándar, saltos de línea innecesarios, símbolos, bullet points, entrecomillados raros, números de página, etc? Dependiendo de la fuente de la que saquemos los textos, nos los podemos encontrar más o menos «limpios». Por ejemplo, si convertimos un fichero pdf en un fichero de texto, lo normal es encontrarnos con caracteres no estándar que tenemos que limpiar para facilitar el procesamiento. Pero si tenemos suerte, nos encontraremos con un cuerpo documental impoluto, listo para ser usado.
Vamos a ver un ejemplo de cada tipo, junto con el código Python que nos ayudaría a procesarlo.
Corpus limpio
Comencemos con un ejemplo fácil, de un corpus limpito, que pertenece a una escena de Cadena Perpetua donde se escucha la voz en off de Ellis Boyd ‘Red’ Redding (Morgan Freeman):
Y así fue como en el penúltimo día de trabajo el pelotón de convictos que había alquitranado el tejado del taller la primavera de 1949 terminó sentado a las diez de la mañana, bebiéndose una cerveza pilsener helada por cortesía del mayor cabronazo que jamás haya estado al cuidado de una prisión estatal. Ese maldito capullo incluso logró parecer benévolo. Nos sentamos a beber con el sol a la espalda y nos sentimos como hombres libres. Diablos, fue como si estuviéramos alquitranando el tejado de una de nuestras propias casas. Era como si fuéramos los señores de la creación. En cuanto a Andy, se pasó todo el descanso sentado en la sombra con una extraña sonrisa en su rostro, mirando cómo nos bebíamos nuestra cerveza. Algunos dirán que lo hizo para ganarse el favor de los celadores, o para ganarse amigos entre los presos. Pero yo creo que lo hizo para sentirse normal, aunque fuese sólo por un momento.
Como podemos observar, se trata de un texto donde no hay ningún carácter extraño.
A continuación podemos ver cómo procesar el corpus en Python. Para simplificar el código, vamos a considerar que cada frase es un documento y por tanto el corpus es la lista de frases de la escena anterior:
1 2 3 4 5 6 7 8 9 |
import pandas as pd raw_text = "Y así fue como en el penúltimo día de trabajo el pelotón de convictos que había alquitranado el tejado del taller la primavera de 1949 terminó sentado a las diez de la mañana, bebiéndose una cerveza pilsener helada por cortesía del mayor cabronazo que jamás haya estado al cuidado de una prisión estatal. Ese maldito capullo incluso logró parecer benévolo. Nos sentamos a beber con el sol a la espalda y nos sentimos como hombres libres. Diablos, fue como si estuviéramos alquitranando el tejado de una de nuestras propias casas. Era como si fuéramos los señores de la creación. En cuanto a Andy, se pasó todo el descanso sentado en la sombra con una extraña sonrisa en su rostro, mirando cómo nos bebíamos nuestra cerveza. Algunos dirán que lo hizo para ganarse el favor de los celadores, o para ganarse amigos entre los presos. Pero yo creo que lo hizo para sentirse normal, aunque fuese sólo por un momento" corpus = raw_text.split('.') trimmed_corpus = [sentence.strip() for sentence in corpus] print("Frases en el corpus = {}\n".format(len(trimmed_corpus))) for index, sentence in enumerate(trimmed_corpus): print("Frase {} = {}\n".format(index + 1, sentence)) |
Frase 1 = Y así fue como en el penúltimo día de trabajo el pelotón de convictos que había alquitranado el tejado del taller la primavera de 1949 terminó sentado a las diez de la mañana, bebiéndose una cerveza pilsener helada por cortesía del mayor cabronazo que jamás haya estado al cuidado de una prisión estatal
Frase 2 = Ese maldito capullo incluso logró parecer benévolo
Frase 3 = Nos sentamos a beber con el sol a la espalda y nos sentimos como hombres libres
Frase 4 = Diablos, fue como si estuviéramos alquitranando el tejado de una de nuestras propias casas
Frase 5 = Era como si fuéramos los señores de la creación
Frase 6 = En cuanto a Andy, se pasó todo el descanso sentado en la sombra con una extraña sonrisa en su rostro, mirando cómo nos bebíamos nuestra cerveza
Frase 7 = Algunos dirán que lo hizo para ganarse el favor de los celadores, o para ganarse amigos entre los presos
Frase 8 = Pero yo creo que lo hizo para sentirse normal, aunque fuese sólo por un momento
Corpus sucio
Aunque podamos encontrar corpus limpios, lo más común es que tengamos que hacer algún trabajo de limpieza sobre él antes de que sea útil para las tareas que vienen después. Por ejemplo, debajo vemos un texto extraído directamente de la página del BOE (aquí) relativo a la convocatoria de elecciones generales para el 10-N:
Real Decreto 551/2019, de 24 de septiembre, de disolución del Congreso de los Diputados y del Senado y de convocatoria de elecciones.
Publicado en:
«BOE» núm. 230, de 24 de septiembre de 2019, páginas 105300 a 105302 (3 págs.)
Sección:
I. Disposiciones generales
Departamento:
Jefatura del Estado
Referencia:
BOE-A-2019-13558
Permalink ELI:
https://www.boe.es/eli/es/rd/2019/09/24/551
Otros formatos:
PDF EPUB XML
Texto
TEXTO ORIGINAL
De acuerdo con lo dispuesto en el artículo 99.5 de la Constitución, y de conformidad con lo previsto en el artículo 167.4 y en la disposición adicional séptima de la Ley Orgánica 5/1985, de 19 de junio, del Régimen Electoral General,
DISPONGO:
Artículo 1. Disolución del Congreso de los Diputados y del Senado.
Quedan disueltos el Congreso de los Diputados y el Senado elegidos el día 28 de abril de 2019.
Artículo 2. Convocatoria de elecciones.
Se convocan elecciones a ambas Cámaras, que se celebrarán el domingo 10 de noviembre de 2019.
Artículo 3. Diputados y Senadores que corresponden a cada circunscripción.
1. En aplicación del artículo 162 de la Ley Orgánica 5/1985, de 19 de junio, del Régimen Electoral General, el número de Diputados correspondiente a cada circunscripción es el que se acompaña en el anexo.
2. En aplicación de los artículos 69 de la Constitución y 165 de la Ley Orgánica 5/1985, de 19 de junio, del Régimen Electoral General, en cada circunscripción provincial se eligen cuatro Senadores; en las circunscripciones insulares se eligen tres en Gran Canaria, Mallorca y Tenerife, y uno en Ibiza-Formentera, Menorca, Fuerteventura, Gomera, Hierro, Lanzarote y La Palma.
Las poblaciones de Ceuta y Melilla eligen cada una de ellas dos Senadores.
Artículo 4. Campaña electoral.
La campaña electoral durará ocho días, comenzando a las cero horas del viernes 1 de noviembre y finalizando a las veinticuatro horas del viernes 8 de noviembre.
Artículo 5. Reunión constitutiva de las Cámaras.
Celebradas las elecciones convocadas por este real decreto, las Cámaras resultantes se reunirán, en sesiones constitutivas, el día 3 de diciembre de 2019, a las diez horas.
Artículo 6. Normas por las que se rigen estas elecciones.
Las elecciones convocadas por el presente real decreto se regirán por la Ley Orgánica 5/1985, de 19 de junio, del Régimen Electoral General, y su normativa de desarrollo.
Disposición final única. Entrada en vigor.
El presente real decreto entrará en vigor el mismo día de su publicación en el «Boletín Oficial del Estado».
Dado en Madrid, el 24 de septiembre de 2019.
En esta ocasión hay un poco más de trabajo para dejar el corpus limpio. Leeremos el fichero que contiene el texto del BOE y esta vez consideraremos que cada línea del fichero es un documento del corpus. Sobre cada documento (línea) vamos a hacer el siguiente proceso de limpieza:
- Pasar todo a minúsculas
- Eliminar URL (direcciones web)
- Eliminar números
- Eliminar caracteres no estándard, como €º”—“«»>•‘’!»»#$%&'()*+,-./:;?@[\]^_`{|}~
- Eliminar palabras con una sóla letra (o letras que hayan quedado aisladas después de aplicar los pasos anteriores)
- Eliminar múltiples espacios en blanco consecutivos
- Eliminar espacios extra al principio y al final de cada línea (documento del corpus)
- Eliminar líneas vacías
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import re boe_file_location = '../files/BOE.txt' with open(boe_file_location, 'r', encoding="utf8") as boe_file: boe_data = boe_file.read().split('\n') # Clean up sections boe_data = [line.lower() for line in boe_data] # to lowercase boe_data = [re.sub(r'^https?:\/\/.*[\r\n]*', '', line, flags=re.MULTILINE) for line in boe_data] # no URLs boe_data = [re.sub("(\d)+", '', line) for line in boe_data] # no numbers boe_data = [re.sub("[€º”—“«»>•‘’!""#$%&'()*+,-./:;?@[\]^_`{|}~]+", ' ', line) for line in boe_data] # no strange characters boe_data = [re.sub(r'\b\w{1}\b', '', line) for line in boe_data] # no single-character words boe_data = [re.sub('\s\s+', ' ', line) for line in boe_data] # no multiple spaces in the middle boe_data = [line.strip() for line in boe_data] # no extra spaces at the beginning/end boe_data = [line for line in boe_data if len(line) > 0] for index, line in enumerate(boe_data): print("Frase {} = {}\n".format(index + 1, line)) |
Frase 1 = real decreto de de septiembre de disolución del congreso de los diputados del senado de convocatoria de elecciones
Frase 2 = publicado en
Frase 3 = boe núm de de septiembre de páginas págs
Frase 4 = sección
Frase 5 = disposiciones generales
Frase 6 = departamento
Frase 7 = jefatura del estado
Frase 8 = referencia
Frase 9 = boe
Frase 10 = permalink eli
Frase 11 = otros formatos
Frase 12 = pdf epub xml
Frase 13 = texto
Frase 14 = texto original
Frase 15 = de acuerdo con lo dispuesto en el artículo de la constitución de conformidad con lo previsto en el artículo en la disposición adicional séptima de la ley orgánica de de junio del régimen electoral general
Frase 16 = dispongo
Frase 17 = artículo disolución del congreso de los diputados del senado
Frase 18 = quedan disueltos el congreso de los diputados el senado elegidos el día de abril de
Frase 19 = artículo convocatoria de elecciones
Frase 20 = se convocan elecciones ambas cámaras que se celebrarán el domingo de noviembre de
Frase 21 = artículo diputados senadores que corresponden cada circunscripción
Frase 22 = en aplicación del artículo de la ley orgánica de de junio del régimen electoral general el número de diputados correspondiente cada circunscripción es el que se acompaña en el anexo
Frase 23 = en aplicación de los artículos de la constitución de la ley orgánica de de junio del régimen electoral general en cada circunscripción provincial se eligen cuatro senadores en las circunscripciones insulares se eligen tres en gran canaria mallorca tenerife uno en ibiza formentera menorca fuerteventura gomera hierro lanzarote la palma
Frase 24 = las poblaciones de ceuta melilla eligen cada una de ellas dos senadores
Frase 25 = artículo campaña electoral
Frase 26 = la campaña electoral durará ocho días comenzando las cero horas del viernes de noviembre finalizando las veinticuatro horas del viernes de noviembre
Frase 27 = artículo reunión constitutiva de las cámaras
Frase 28 = celebradas las elecciones convocadas por este real decreto las cámaras resultantes se reunirán en sesiones constitutivas el día de diciembre de las diez horas
Frase 29 = artículo normas por las que se rigen estas elecciones
Frase 30 = las elecciones convocadas por el presente real decreto se regirán por la ley orgánica de de junio del régimen electoral general su normativa de desarrollo
Frase 31 = disposición final única entrada en vigor
Frase 32 = el presente real decreto entrará en vigor el mismo día de su publicación en el boletín oficial del estado
Frase 33 = dado en madrid el de septiembre de
Takeaway
Cualquiera que sea la longitud de nuestro corpus y con independencia del tipo de entidad que contenga (líneas, párrafos, documentos, etc), uno de los primeros pasos es limpiar su contenido y así dejarlo listo para las siguientes tareas.
Trackbacks/Pingbacks