{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Procesamiento del Lenguaje Natural con Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Esta notebook fue creada originalmente como un blog post por [Raúl E. López Briega](https://relopezbriega.com.ar/) en [Matemáticas, Analisis de datos y Python](https://relopezbriega.github.io). El contenido esta bajo la licencia BSD.*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\"NLP\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> \"El lenguaje sirve no sólo para expresar el pensamiento, sino para hacer posibles pensamientos que no podrían existir sin él.\"\n", "\n", "**[Bertrand Russell](https://es.wikipedia.org/wiki/Bertrand_Russell)**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introducción\n", "\n", "El [lenguaje](https://es.wikipedia.org/wiki/Lenguaje) es una de las herramientas centrales en nuestra vida social y profesional. Entre otras cosas, actúa como un medio para transmitir ideas, información, opiniones y sentimientos; así como para persuadir, pedir información, o dar ordenes. Asimismo, el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje) humano es algo que esta en constante cambio y evolución; y que puede llegar a ser muy *ambiguo* y *variable*. Tomemos por ejemplo la frase *\"comí una pizza con amigos\"* comparada con *\"comí una pizza con aceitunas\"*; su estructura es la misma, pero su significado es totalmente distinto. De la misma manera, un mismo mensaje puede ser expresado de formas diferentes; *\"comí una pizza con amigos\"* puede también ser expresado como *\"compartí una pizza con amigos\"*. \n", "\n", "Los seres humanos somos muy buenos a la hora de producir e interpretar el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje), podemos expresar, percibir e interpretar significados muy elaborados en fracción de segundos casi sin dificultades; pero al mismo tiempo, somos también muy malos a la hora de entender y describir formalmente las reglas que lo gobiernan. Por este motivo, entender y producir el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje) por medio de una *computadora* es un problema muy difícil de resolver. Éste problema, es el campo de estudio de lo que en [inteligencia artificial](https://iaarbook.github.io/inteligencia-artificial/) se conoce como [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) o [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) por sus siglas en inglés. \n", "\n", "## ¿Qué es el Procesamiento del Lenguaje Natural?\n", "\n", "El [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) o [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) es una disciplina que se encuentra en la intersección de varias ciencias, tales como las [Ciencias de la Computación](https://es.wikipedia.org/wiki/Ciencias_de_la_computaci%C3%B3n), la [Inteligencia Artificial](https://iaarbook.github.io/) y [Psicología Cognitiva](https://es.wikipedia.org/wiki/Psicolog%C3%ADa_cognitiva). Su idea central es la de darle a las máquinas la capacidad de leer y comprender los idiomas que hablamos los humanos. La investigación del [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) tiene como objetivo responder a la pregunta de cómo las personas son capaces de comprender el significado de una oración oral / escrita y cómo las personas entienden lo que sucedió, cuándo y dónde sucedió; y las diferencias entre una suposición, una creencia o un hecho.\n", "\n", "En general, en [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) se utilizan seis niveles de comprensión con el objetivo de descubrir el significado del discurso. Estos niveles son:\n", "\n", "* **Nivel fonético:** Aquí se presta atención a la [fonética](https://es.wikipedia.org/wiki/Fon%C3%A9tica), la forma en que las palabras son pronunciadas. Este nivel es importante cuando procesamos la palabra hablada, no así cuando trabajamos con texto escrito. \n", "\n", "* **Nivel morfológico:** Aquí nos interesa realizar un análisis [morfológico](https://es.wikipedia.org/wiki/Morfolog%C3%ADa_ling%C3%BC%C3%ADstica) del discurso; estudiar la estructura de las palabras para delimitarlas y clasificarlas.\n", "\n", "* **Nivel sintáctico:** Aquí se realiza un análisis de [sintaxis](https://es.wikipedia.org/wiki/Sintaxis), el cual incluye la acción de dividir una oración en cada uno de sus componentes. \n", "\n", "* **Nivel semántico:** Este nivel es un complemente del anterior, en el análisis [semántico](https://es.wikipedia.org/wiki/Sem%C3%A1ntica) se busca entender el significado de la oración. Las palabras pueden tener múltiples significados, la idea es identificar el significado apropiado por medio del contexto de la oración.\n", "\n", "* **Nivel discursivo:** El nivel discursivo examina el significado de la oración en relación a otra oración en el texto o párrafo del mismo documento.\n", "\n", "* **Nivel pragmático:** Este nivel se ocupa del análisis de oraciones y cómo se usan en diferentes situaciones. Además, también cómo su significado cambia dependiendo de la situación.\n", "\n", "Todos los niveles descritos aquí son inseparables y se complementan entre sí. El objetivo de los sistemas de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) es incluir estas definiciones en una *computadora* y luego usarlas para crear una oración estructurada y sin ambigüedades con un significado bien definido.\n", "\n", "## Aplicaciones del Procesamiento del Lenguaje Natural\n", "\n", "Los algoritmos de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) suelen basarse en algoritmos de [aprendizaje automático](https://iaarbook.github.io/machine-learning/). En lugar de codificar manualmente grandes conjuntos de reglas, el [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) puede confiar en el [aprendizaje automático](https://iaarbook.github.io/machine-learning/) para aprender estas reglas automáticamente analizando un conjunto de ejemplos y haciendo una [inferencia estadística](https://es.wikipedia.org/wiki/Estad%C3%ADstica_inferencial). En general, cuanto más datos analizados, más preciso será el modelo. Estos algoritmos pueden ser utilizados en algunas de las siguientes aplicaciones:\n", "\n", "* **Resumir texto:** Podemos utilizar los modelos de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) para extraer las ideas más importantes y centrales mientras ignoramos la información irrelevante.\n", "\n", "* **Crear chatbots:** Podemos utilizar las técnicas de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) para crear [chatbots](https://iaarhub.github.io/capacitacion/2017/04/09/31-herramientas-que-te-ayudaran-a-crear-tu-propio-chatbot/) que puedan interactuar con las personas.\n", "\n", "* **Generar automáticamente [etiquetas de palabras clave](https://es.wikipedia.org/wiki/Etiquetado_gramatical)**: Con [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) también podemos realizar un análisis de contenido aprovechando el algoritmo de [LDA](https://es.wikipedia.org/wiki/Latent_Dirichlet_Allocation) para asignar palabras claves a párrafos del texto.\n", "\n", "* **[Reconocer entidades](https://es.wikipedia.org/wiki/Reconocimiento_de_entidades_nombradas)**: Con [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) podemos identificar a las distintas entidades del texto como ser una persona, lugar u organización.\n", "\n", "* **[Análisis de sentimiento](https://es.wikipedia.org/wiki/An%C3%A1lisis_de_sentimiento)**: También podemos utilizar [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) para identificar el *sentimiento* de una cadena de texto, desde muy negativo a neutral y a muy positivo.\n", "\n", "\n", "## Librerías de Python para Procesamiento del Lenguaje Natural\n", "\n", "Actualmente, [Python](https://www.python.org/) es uno de los lenguajes más populares para trabajar en el campo la [Inteligencia Artificial](https://iaarbook.github.io/). Para abordar los problemas relacionados con el [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) [Python](https://www.python.org/) nos proporciona las siguientes librerías:\n", "\n", "* **[NLTK](https://www.nltk.org/):** Es la librería líder para el [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). Proporciona interfaces fáciles de usar a más de *[50 corpus](https://nltk.org/nltk_data/)* y recursos léxicos, junto con un conjunto de bibliotecas de procesamiento de texto para la clasificación, tokenización, el etiquetado, el análisis y el razonamiento semántico.\n", "\n", "\n", "* **[TextBlob](http://textblob.readthedocs.io/en/dev/):** [TextBlob](http://textblob.readthedocs.io/en/dev/) simplifica el procesamiento de texto proporcionando una interfaz intuitiva a [NLTK](https://www.nltk.org/). Posee una suave curva de aprendizaje al mismo tiempo que cuenta con una sorprendente cantidad de funcionalidades.\n", "\n", "\n", "* **[Stanford CoreNLP](https://stanfordnlp.github.io/CoreNLP/):** Paquete desarrollado por la universidad de [Stanford](http://web.stanford.edu/class/cs224n/), para muchos constituye el estado del arte sobre las técnicas tradicionales de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). Si bien esta escrita en Java, posee una [interface con Python](https://github.com/stanfordnlp/python-stanford-corenlp).\n", "\n", "\n", "* **[Spacy](https://spacy.io/):** Es una librería relativamente nueva que sobresale por su facilidad de uso y su velocidad a la hora de realizar el procesamiento de texto.\n", "\n", "\n", "* **[Textacy](http://textacy.readthedocs.io/en/latest/):** Esta es una librería de alto nivel diseñada sobre [Spacy](https://spacy.io/) con la idea de facilitar aun más las tareas relacionadas con el [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales).\n", "\n", "\n", "* **[Gensim](https://radimrehurek.com/gensim/intro.html):** Es una librería diseñada para extraer automáticamente los temas semánticos de los documentos de la forma más eficiente y con menos complicaciones posible.\n", "\n", "\n", "* **[pyLDAvis](https://pyldavis.readthedocs.io/en/latest/readme.html):** Esta librería está diseñado para ayudar a los usuarios a interpretar los temas que surgen de un análisis de tópicos. Nos permite visualizar en forma muy sencilla cada uno de los temas incluidos en el texto.\n", "\n", "\n", "Como veremos, el [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) también se está sumando a la popularidad del [Deep Learning](https://relopezbriega.github.io/blog/2017/06/13/introduccion-al-deep-learning/), por lo que muchos de los [frameworks](https://iaarbook.github.io/python/#frameworks-para-deep-learning) que se utilizan en [Deep Learning](https://relopezbriega.github.io/blog/2017/06/13/introduccion-al-deep-learning/) pueden ser aplicados para realizar modelos de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales).\n", "\n", "## Corpus lingüístico \n", "\n", "Hoy en día, es indispensable el uso de buenos recursos lingüísticos para el desarrollo de los sistemas de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). Estos recursos son esenciales para la creación de gramáticas, en el marco de aproximaciones simbólicas; o para llevar a cabo la formación de módulos basados en el [aprendizaje automático](https://iaarbook.github.io/machine-learning/).\n", "\n", "Un [corpus lingüístico](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) es un conjunto amplio y estructurado de ejemplos reales de uso de la lengua. Estos ejemplos pueden ser textos (los más comunes), o muestras orales (generalmente transcritas). Un [corpus lingüístico](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) es un conjunto de textos relativamente grande, creado independientemente de sus posibles formas o usos. Es decir, en cuanto a su estructura, variedad y complejidad, un [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) debe reflejar una lengua, o su modalidad, de la forma más exacta posible; en cuanto a su uso, preocuparse de que su representación sea real. La idea es que representen al lenguaje de la mejor forma posible para que los modelos de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) puedan aprender los patrones necesarios para entender el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje). Encontrar un buen [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) sobre el cual trabajar no suele ser una tarea sencilla; un [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) que se suele utilizar para entrenar modelos es el que incluye la información extraída de [wikipedia](https://dumps.wikimedia.org/eswiki/latest/).\n", "\n", "## Procesamiento del Lenguaje Natural con Python\n", "\n", "Hasta aquí la introducción, ahora llegó el momento de ensuciarse un poco las manos y comenzar a explorar algunos ejemplos de las herramientas que nos ofrece [Python](https://www.python.org/) para trabajar con problemas de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). Comencemos por ejemplo, descargando el [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) de [wikipedia](https://dumps.wikimedia.org/eswiki/latest/) en español. Esto lo podemos hacer fácilmente utilizando [Textacy](http://textacy.readthedocs.io/en/latest/)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# \n", "# Importando las librerías que vamos a utilizar\n", "import pandas as pd\n", "import numpy as np \n", "import matplotlib.pyplot as plt \n", "import textacy\n", "from textacy.datasets import Wikipedia\n", "from collections import Counter, defaultdict\n", "import warnings; warnings.simplefilter('ignore')\n", "\n", "# graficos incrustados\n", "%matplotlib inline\n", "\n", "# función auxiliar\n", "def leer_texto(texto):\n", " \"\"\"Funcion auxiliar para leer un archivo de texto\"\"\"\n", " with open(texto, 'r') as text:\n", " return text.read()" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Descargando copus de wikipedia\n", "wp = Wikipedia(data_dir='/home/raul/Documents/data', lang='es', version='latest')\n", "wp.download()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'data_dir': '/home/raul/Documents/data',\n", " 'description': 'All articles for a given language- and version-specific Wikipedia site snapshot.',\n", " 'name': 'wikipedia',\n", " 'site_url': 'https://meta.wikimedia.org/wiki/Data_dumps'}" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Chequeando la información descargada\n", "wp.info" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Andorra\n", "\n", "Andorra, oficialmente Principado de Andorra , es un pequeño país soberano del suroeste de Europa. Constituido en Estado independiente, de derecho, democrático y social, cuya forma de gobierno es el coprincipado parlamentario. Su territorio está organizado en siete parroquias, con una población total en 2016 de 78.264 habitantes. Su capital es Andorra la Vieja.\n", "\n", "Ti \n", "\n", "Argentina\n", "\n", "La República Argentina, conocida simplemente como Argentina, es un país soberano de América del Sur, ubicado en el extremo sur y sudeste de dicho subcontinente. Adopta la forma de gobierno republicana, representativa y federal.\n", "\n", "El Estado argentino es un Estado federal descentralizado, integrado por un Estado nacional y veintitrés estados provinciales autónomos \n", "\n" ] } ], "source": [ "for text in wp.texts(min_len=1000, limit=2):\n", " print(text[:375], \"\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como podemos ver, con la ayuda de [Textacy](http://textacy.readthedocs.io/en/latest/) es muy fácil descargar la información de [wikipedia](https://dumps.wikimedia.org/eswiki/latest/) para luego poder utilizarla de base para nuestro [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico). Veamos otros problemas que también podemos resolver con la ayuda de [Textacy](http://textacy.readthedocs.io/en/latest/); como ser los casos de detectar el idioma o de procesar todo un texto y analizarlo." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "es\n", "en\n", "fr\n", "de\n", "it\n", "pt\n" ] } ], "source": [ "# Detectando el idioma con taxtacy\n", "saludos = [\"Hola\", \"Hello\", \"Bonjour\", \"Guten Tag\", \"Buon giorno\", \"Bom dia\"]\n", "for saludo in saludos:\n", " print(textacy.text_utils.detect_language(saludo))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Cargando el modelo en español de spacy\n", "nlp = textacy.data.spacy.load('es_core_web_md')" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['cuanto',\n", " 'sera',\n", " 'trabajo',\n", " 'tan',\n", " 'ya',\n", " 'claro',\n", " 'encuentra',\n", " 'arriba',\n", " 'despacio',\n", " 'primero',\n", " 'pocas',\n", " 'tiempo',\n", " 'aquéllos',\n", " 'días',\n", " 'enseguida']" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# detalle de stop words\n", "# las stop words son las palabras más comunes de un corpus que en general\n", "# queremos eliminar porque no son significativas para un análisis.\n", "# Ocurren muchas veces, pero aportan muy poca información\n", "stop = list(textacy.data.spacy.es.STOP_WORDS)\n", "stop[:15]" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Procesando un texto \n", "# Procesando 1984 de George Orwell - mi novela favorita\n", "texto = leer_texto('ORWELL_1984.txt')\n", "texto_procesado = nlp(texto)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "8114\n" ] } ], "source": [ "# Cuántas sentencias hay en el texto?\n", "sentencias = [s for s in texto_procesado.sents]\n", "print(len(sentencias))" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[Winston Smith, con la barbilla clavada en el pecho en su esfuerzo por burlar el molestísimo viento, se deslizó rápidamente por entre las puertas de cristal de las Casas de la Victoria, aunque, no con la suficiente rapidez para evitar que una ráfaga polvorienta se colara con él.\n", ", El vestíbulo olía a legumbres cocidas y a esteras viejas., Al fondo, un cartel de colores, demasiado grande para hallarse en un interior, estaba pegado a la pared., Representaba sólo un enorme rostro de más de un metro de anchura: la cara de un hombre de unos cuarenta y cinco años con un gran bigote negro y facciones hermosas y endurecidas., Winston se dirigió hacia las escaleras., Era inútil intentar subir en el ascensor., No funcionaba con frecuencia y en esta época la corriente se cortaba durante las horas de día., Esto era parte de las restricciones con que se preparaba la Semana del Odio., Winston tenía que subir a un séptimo piso.]\n" ] } ], "source": [ "# imprimir las primeras 10 sentencias para verificar el texto\n", "print(sentencias[1:11])" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[—¿Morirá el Gran Hermano?,\n", " ael Gran Hermano.,\n", " Pensó en el Gran Hermano.,\n", " ¿Cuáles eran sus verdaderos sentimientos hacia el Gran Hermano?,\n", " Dime: ¿cuáles son los verdaderos sentimientos que te inspira el Gran Hermano?,\n", " Tienes que amar ael Gran Hermano.,\n", " l Gran Hermano.,\n", " l Gran Hermano.,\n", " Amaba ael Gran Hermano.\n", " \n", " \n", " \n", " \n", " ,\n", " Hubiera sido posible, por ejemplo, decir el «Gran Hermano inbueno».]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# sentencias con las que aparece el Gran Hermano\n", "[sent for sent in texto_procesado.sents if 'Gran Hermano' in sent.string][-10:]" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# \n", "def encontrar_personajes(doc):\n", " \"\"\"\n", " Devuelve una lista de los personajes de un `doc` con su cantidad de\n", " ocurrencias\n", " \n", " :param doc: NLP documento parseado por Spacy\n", " :return: Lista de Tuplas con la forma\n", " [('winston', 686), (\"o'brien\", 135), ('julia', 85),]\n", " \"\"\"\n", " \n", " personajes = Counter()\n", " for ent in doc.ents:\n", " if ent.label_ == 'PERSON':\n", " personajes[ent.lemma_] += 1\n", " \n", " return personajes.most_common()" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[('winston', 686), (\"o'brien\", 135), ('julia', 85), ('partido', 85), ('parsons', 36), ('syme', 29), ('goldstein', 29), ('pensamiento', 22), ('odio', 13), ('ministerio', 13), ('', 11), ('katharine', 11), ('winston ?', 10), ('rutherford', 9), ('ogilvy', 8), ('aaronson', 8), ('charrington', 8), ('—la', 7), ('withers', 6), ('ingsoc', 6)]\n" ] } ], "source": [ "# Extrayendo los personajes principales del texto y contando cuantas veces\n", "# son nombrados.\n", "print(encontrar_personajes(texto_procesado)[:20])" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# \n", "def obtener_adj_pers(doc, personaje):\n", " \"\"\"\n", " Encontrar todos los adjetivos relacionados a un personaje en un `doc`\n", " \n", " :param doc: NLP documento parseado por Spacy\n", " :param personaje: un objeto String \n", " :return: lista de adjetivos relacionados a un `personaje`\n", " \"\"\"\n", " \n", " adjetivos = []\n", " for ent in doc.ents:\n", " if ent.lemma_ == personaje:\n", " for token in ent.subtree:\n", " if token.pos_ == 'ADJ':\n", " adjetivos.append(token.lemma_)\n", " \n", " for ent in doc.ents:\n", " if ent.lemma_ == personaje:\n", " if ent.root.dep_ == 'nsubj':\n", " for child in ent.root.head.children:\n", " if child.dep_ == 'acomp':\n", " adjetivos.append(child.lemma_)\n", " \n", " return adjetivos\n" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['superior', 'separado', 'fuertes', 'abierto', 'oscura', 'dirigiéndose', 'negro', 'saltones', 'tristes', 'burlones', 'solo', 'humano', 'sostenemos', 'dubitativo', 'completa', 'definitiva', 'sobresaltado', 'fascinado', 'extraña', 'sobrado', 'propio', 'solos', 'joven', 'sorprendido', 'sorprendido', 'hermosa', 'breve', 'cortante', 'primera', 'junto', 'obediente', 'metafísico', 'blanca', 'sonriente', 'sentado', 'irresoluto', 'sumergido', 'feliz']\n" ] } ], "source": [ "# Encontrar adjetivos que describen a algún personaje.\n", "print(obtener_adj_pers(texto_procesado, \"winston\"))" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# \n", "def personaje_verbo(doc, verbo):\n", " \"\"\"\n", " Encontrar los personajes que utilizan determinado `verbo` en `doc`\n", " \n", " :param doc: NLP documento parseado por Spacy\n", " :param verbo: un objeto String \n", " :return: lista de personajes que utilizan `verbo`\n", " \"\"\"\n", " contar_verbo = Counter()\n", " for ent in doc.ents:\n", " if ent.label_ == 'PERSON' and ent.root.head.lemma_ == verbo:\n", " contar_verbo[ent.text] += 1\n", " \n", " return contar_verbo.most_common(10)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('Winston', 7),\n", " ('Julia', 4),\n", " ('Syme', 2),\n", " ('Julia—. Espera', 1),\n", " ('Parsons', 1),\n", " ('—le', 1)]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Encontrar personajes que utilizan determinado verbo\n", "personaje_verbo(texto_procesado, \"dijo\")" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'CARDINAL', 'LOC', 'MISC', 'ORDINAL', 'ORG', 'PERSON'}" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Trabajando con las entidades del texto\n", "# Una entidad nombrada es cualquier objeto del mundo real como una persona,\n", "# ubicación, organización o producto con un nombre propio.\n", "\n", "# tipos de entidades del texto\n", "set(ent.label_ for ent in texto_procesado.ents)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[INGSOC,\n", " Partido:,\n", " ES LA,\n", " Departamento de Registro,\n", " Dos Minutos de Odio,\n", " Departamento de Novela,\n", " Partido Interior,\n", " Partido Interior,\n", " Departamento de Registro,\n", " Dos Minutos]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Entidades nombradas de tipo ORG\n", "[ent for ent in texto_procesado.ents if ent.label_ == 'ORG'][:10]" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'ADJ',\n", " 'ADP',\n", " 'ADV',\n", " 'AUX',\n", " 'CONJ',\n", " 'DET',\n", " 'INTJ',\n", " 'NOUN',\n", " 'NUM',\n", " 'PART',\n", " 'PRON',\n", " 'PROPN',\n", " 'PUNCT',\n", " 'SCONJ',\n", " 'SPACE',\n", " 'SYM',\n", " 'VERB'}" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Partes de la oración (POS)\n", "# En las partes de la oración se etiquetan las palabras de acuerdo a lo \n", "# que significan segun su contexto. Algunas de estas etiquetas pueden\n", "# ser: Adjetivos, verbos, adverbios, conjunciones, pronombres, sustantivos.\n", "\n", "# Etiquetas del texto\n", "set(token.pos_ for token in texto_procesado)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['frío',\n", " 'clavada',\n", " 'molestísimo',\n", " 'suficiente',\n", " 'polvorienta',\n", " 'cocidas',\n", " 'viejas',\n", " 'grande',\n", " 'pegado',\n", " 'enorme']" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Etiquetas de tipo ADJ\n", "[token.orth_ for token in texto_procesado if token.pos_ == 'ADJ'][1:11]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['GEORGE',\n", " 'ORWELL',\n", " 'PARTE',\n", " 'CAPITULO',\n", " 'Winston',\n", " 'Smith',\n", " 'Casas',\n", " 'Victoria',\n", " 'Winston',\n", " 'Semana']" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Etiquetas de tipo PROPN\n", "[token.orth_ for token in texto_procesado if token.pos_ == 'PROPN'][1:11]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como demuestran estos ejemplos [Textacy](http://textacy.readthedocs.io/en/latest/) / [Spacy](https://spacy.io/) son herramientas muy poderosas que nos pueden ayudar a analizar y obtener información valiosa de un texto en forma rápida y sencilla. \n", "\n", "\n", "## Deep Learning y Procesamiento del Lenguaje Natural\n", "\n", "Durante mucho tiempo, las técnicas principales de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) fueron dominadas por métodos de [aprendizaje automático](https://iaarbook.github.io/machine-learning/) que utilizaron [modelos lineales](https://es.wikipedia.org/wiki/Modelo_lineal) como las [máquinas de vectores de soporte](https://es.wikipedia.org/wiki/M%C3%A1quinas_de_vectores_de_soporte) o la [regresión logística](https://es.wikipedia.org/wiki/Regresi%C3%B3n_log%C3%ADstica), entrenados sobre [vectores](https://es.wikipedia.org/wiki/Vector) de características de muy alta dimensional pero muy escasos.\n", "Recientemente, el campo ha tenido cierto éxito en el cambio hacia modelos de [deep learning](https://iaarbook.github.io/deeplearning/) sobre entradas más densas.\n", "\n", "Las [redes neuronales](https://relopezbriega.github.io/category/redes-neuronales.html) proporcionan una poderosa maquina de aprendizaje que es muy atractiva para su uso en problemas de *lenguaje natural*. Un componente importante en las [redes neuronales](https://relopezbriega.github.io/category/redes-neuronales.html) para el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje) es el uso de una capa de [word embedding](https://en.wikipedia.org/wiki/Word_embedding), una asignación de símbolos discretos a vectores continuos en un espacio dimensional relativamente bajo. Cuando se utiliza [word embedding](https://en.wikipedia.org/wiki/Word_embedding), se transforman los distintos símbolos en objetos matemáticos sobre los que se pueden realizar operaciones. En particular, la distancia entre vectores puede equipararse a la distancia entre palabras, facilitando la generalización del comportamiento de una palabra sobre otra.\n", "Esta representación de palabras como vectores es aprendida por la red como parte del proceso de entrenamiento.\n", "Subiendo en la jerarquía, la red también aprende a combinar los vectores de palabras de una manera que es útil para la predicción. Esta capacidad alivia en cierta medida los problemas de dispersión de los datos.\n", "Hay dos tipos principales de arquitecturas de [redes neuronales](https://relopezbriega.github.io/category/redes-neuronales.html) que resultan muy útiles en los problemas de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales): las [Redes neuronales prealimentadas](https://es.wikipedia.org/wiki/Red_neuronal_prealimentada) y las [Redes neuronales recurrentes](https://en.wikipedia.org/wiki/Recurrent_neural_network).\n", "\n", "Para ejemplificar, veamos como podemos utilizar [word embedding](https://en.wikipedia.org/wiki/Word_embedding) utilizando el modelo `word2vec` de [Gensim](https://radimrehurek.com/gensim/intro.html)." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Using TensorFlow backend.\n" ] } ], "source": [ "# \n", "# importando gensim y TSNE para graficar\n", "import gensim\n", "from sklearn.manifold import TSNE" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "# transformando el texto para pasarlo al modelo de gensim\n", "texto = [[str(palabra).lower() for palabra in sent if str(palabra) not in stop ]\n", " for sent in sentencias]\n", "# generando el diccionario\n", "diccionario = gensim.corpora.Dictionary(texto)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "# creando el modelo\n", "modelo = gensim.models.Word2Vec(texto, workers=4, size=100, \n", " min_count=5, window=10, sample=1e-3)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([-0.22692642, -0.08890257, -0.12868501, -0.17392403, 0.22627664,\n", " 0.10127033, -0.09027202, 0.10692301, -0.30289358, 0.06429829,\n", " 0.17862263, 0.20448232, -0.54694331, -0.40681064, 0.61438572,\n", " 0.0217872 , 0.080202 , 0.46306548, 0.09076022, -0.02869571,\n", " -0.46194851, 0.28670114, 0.38570273, 0.32555154, 0.13098474,\n", " -0.03134775, -0.09577781, 0.06859019, -0.15935177, 0.61558241,\n", " 0.07509102, -0.24245416, -0.44668666, -0.77279037, 0.84581488,\n", " -0.54047441, -0.18756895, -0.12506978, -0.52870399, 0.1898849 ,\n", " -0.00930689, 0.36932173, 0.22370262, -0.67407966, -0.45509291,\n", " -0.00848365, 0.62967575, 0.16172817, 0.09978516, 0.15064637,\n", " -0.34957823, 0.20686783, 0.1038606 , -0.09155462, 0.08276461,\n", " 0.31154567, -0.3129864 , -0.45181432, -0.12060832, 0.30541465,\n", " -0.37994722, 0.13566031, 0.16380484, 0.32732216, 0.15746659,\n", " 0.69340295, -0.25527388, 0.37333885, 0.23317885, -0.4710786 ,\n", " -0.22506852, 0.14103019, -0.30253953, 0.00573605, -0.14745024,\n", " -0.50815731, -0.37789851, -0.3400358 , 0.62753612, 0.04747195,\n", " -0.07443633, 0.4276363 , -0.28931141, 0.29784235, -0.07251735,\n", " -0.07709371, -0.1003265 , -0.29098341, 0.47159177, 0.41372281,\n", " -0.10831725, -0.04670507, 0.07489309, 0.00146162, -0.02867368,\n", " -0.2771121 , 0.37281424, -0.53325164, 0.19094327, 0.51455575], dtype=float32)" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# representación de la palabra hermano como vector.\n", "modelo['hermano']" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('mano', 0.9999627470970154),\n", " ('se', 0.9999592304229736),\n", " ('mesa', 0.9999556541442871),\n", " ('lo', 0.999954879283905),\n", " ('podía', 0.9999546408653259),\n", " ('personas', 0.9999545812606812),\n", " ('habría', 0.9999533891677856),\n", " ('sitio', 0.9999532103538513),\n", " ('no', 0.999953031539917),\n", " ('pero', 0.999951958656311)]" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# como convertimos las palabras en vectores y los vectores capturan muchas \n", "# regularidades lingüísticas, podemos aplicar operaciones vectoriales para\n", "# extraer muchas propiedades interesantes. \n", "\n", "# palabras similares a persona\n", "modelo.most_similar_cosmul('persona')" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "# por último podemos graficar el modelo\n", "vocab = list(modelo.wv.vocab)\n", "X = modelo[vocab]\n", "\n", "# aplicamos TSNE\n", "tsne = TSNE(n_components=2)\n", "X_tsne = tsne.fit_transform(X)\n", "\n", "# transformamos en DataFrame\n", "df = pd.concat([pd.DataFrame(X_tsne),\n", " pd.Series(vocab)],\n", " axis=1)\n", "\n", "df.columns = ['x', 'y', 'palabra']" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "image/png": "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\nelQffvih6tWrpx07dmjo0KE6Mdq7ceNGbdu2TS1atFCfPn20ZMkSDRo0yHs+n332mVJSUrRu3Tr5\n+flp7Nixmj9/vh544IHqvKwAUGPiw4MkyXs3YrMAfyXHBXv31xTCFq54J98GXFxarrScAgVIio6O\n1o033ihJCgsLk8fj0W233aZ69erpN7/5jfr27etdJ7VhwwYtWbJEkjR8+HA9/vjj3vrvu+8++fj4\nqE2bNmrdurW2b9+ujIwMjR8/XtLxJxi3aNHCG7Z69eqlRo0anbavpaWlGjdunHJzc+Xr6+s9Rj/1\nt3Xr1pKkoUOHKiMjo1LY+uijj5Sdna2oqChJUnFxsa6//vqquIQAcNmIDw+q8XD1c4QtXNF+fhuw\nMdKkJZs1rPkR1a1b11vO19dXZWVlqlOnjjZu3KiPPvpIqamp+q//+i+tWrXqjG04jnPG7Z+75ppr\nfvG76dOnq0mTJsrLy1NFRYXq1at3zu0YYzRixAj98Y9/PGP7AIDqxZotXNFOdxtwcWm5FmV+c9ry\nRUVFOnz4sO666y5Nnz5deXl5ko7fxXjiIXfz589Xly5dvMf885//VEVFhXbu3Kkvv/xSwcHB6tKl\ni+bPny9J+vzzz7Vr1y4FB5+6ZuDaa6/VkSNHvNuHDx9W06ZN5ePjo3nz5lVac7Vx40Z99dVXqqio\nUEpKijp37lyprp49eyo1NVX79u2TJB08eFBff/31OV8rAIAdjGzhivZLtwEfKPpRLU+z/8iRI+rf\nv79KSkpkjNGf//xnSdKMGTM0cuRITZ06VYGBgZo9e7b3mObNmys6Olrff/+9XnvtNdWrV09jx47V\nmDFj5HK5VKdOHc2ZM6fSSNoJbrdbvr6+Cg0NVUJCgsaOHat7771Xc+fOVZ8+fSqNgkVFRWncuHH6\n4osv1KNHDw0YMKBSXe3bt9fzzz+v3r17q6KiQn5+fpo5c6ZatGhx/hcOAFBlePQDrmi2bwNOSEhQ\n3759K62dAgDUDjz6AdClexswAKD2YBoRVzTbtwHPmTOnSuoBAPyywsJCLViwQGPHjq3prlwQphEB\nAMAlzePxqG/fvtqyZUtNd6USphEBAEC18Xg83jdytGvXToMGDdLRo0crvb81KytL3bt3lyT98MMP\nSkxMVHR0tMLDw7Vs2TJJ0tatW71vwnC73dqxY4cmTpyonTt3KiwsTMnJyTLGKDk5WSEhIXK5XEpJ\nSamp0z4nTCMCAIAqkZ+frzfeeEMxMTFKTEzUq6+++otlX3jhBcXGxurNN99UYWGhoqOjdccdd+i1\n117Tb39YZKhGAAAgAElEQVT7Ww0bNkzHjh1TeXm5pkyZoi1btig3N1eStHjxYuXm5iovL08HDhxQ\nVFSUunbtqqZNm1bXqZ4XwhYAALggJ7+ho5E5rMY3HH/9mSTdf//9euWVV37x2A8++EBvv/22pk2b\nJun4O2V37dql22+/XS+88IK+/fZbDRw4UG3atDnl2IyMDA0dOlS+vr5q0qSJunXrpszMTN1zzz12\nTvQiEbYAAMB5+/kbOvZ+X6LCo2VKyynw3oTkOI7q1KmjiooKSccD1QnGGC1evPiUBz63a9dOt956\nq959913ddddd+utf/+p9VdnlijVbAADgvJ3uDR1l3+/TU7OOv0d2wYIF6ty5s1q2bKns7GxJx6f/\nToiLi9OMGTN04ka9nJwcSdKXX36p1q1bKykpSf3799emTZtOedtGly5dlJKSovLycu3fv1/p6emK\njo62er4Xg7AFAADO2+ne0FGn0Y36Mn2J2rVrp0OHDmnMmDF6+umn9dvf/laRkZHy9f2/5x4++eST\nKi0tldvtVocOHfTkk09Kkv7xj38oJCREYWFh2rJlix544AH927/9m2JiYhQSEqLk5GQNGDBAbrdb\noaGhio2N1UsvvaQbbrih2s79fPHoBwAAcN5+/oaOssN7tS/1GUX9bnaVvKHjcsCjHwAAgDWne0OH\n4zi8oeM0WCAPAADO28/f0NGiRUv914p1VfaGjisJYQsAAFyQ+PAgwtU5YBoRAADAIsIWAACARYQt\nAAAAiwhbAAAAFhG2AAAALCJs1ZDdu3dr0KBBF12Px+NRSEhIFfQIAADYQNiqIc2aNVNqampNdwMA\nAFhWa8PW3Llzve9VGj58uDwej2JjY+V2u9WzZ0/t2rVLkpSQkKAxY8botttuU+vWrbVmzRolJiaq\nXbt2SkhI8NZXv3597+fU1FTvdwkJCUpKSlKnTp3UunVrb8A6eUSqvLxcjz32mEJCQuR2uzVjxgxJ\n0rPPPquoqCiFhIRo9OjR3pd1ZmdnKzQ0VKGhoZo5c6a33ZKSEo0cOVIul0vh4eFavXq1tesHAADO\nTa0MW1u3btXzzz+vVatWKS8vTy+//LLGjx+vESNGaNOmTRo2bJiSkpK85Q8dOqQNGzZo+vTpuuee\ne/Qf//Ef2rp1qzZv3qzc3Nyztrdnzx5lZGRo+fLlmjhx4infz5o1Sx6PR7m5ud72JWncuHHKzMzU\nli1bVFxcrOXLl0uSRo4cqRkzZigvL69SPTNnzpTjONq8ebMWLlyoESNGqKSk5GIuFQAAuEi1Kmyl\n5RQoZsoqdX10ho4GRSnjmx8lSY0aNdKGDRv061//WpI0fPhwZWRkeI/r16+fHMeRy+VSkyZN5HK5\n5OPjow4dOsjj8Zy13fj4ePn4+Kh9+/bau3fvKd+vXLlSDz30kOrUqePtjyStXr1at956q1wul1at\nWqWtW7eqsLBQhYWF6tq1q7evJ2RkZOj++++XJLVt21YtWrTQ559/fgFXCgAAVJVa87qetJwCTVqy\nWcWl5TKSjvxYpklLNkvSWV81ULduXUmSj4+P9/OJ7bKyMknHX755ws9Hk04+5sRU4NmUlJRo7Nix\nysrK0k033aTJkyczSgUAwGXI2siW4ziTHccpcBwn96efu2y1dS6mrshXcWm5JKlec7eObs9Q0feH\nNHVFvg4ePKhOnTpp0aJFkqT58+erS5cu51V/kyZN9Nlnn6miokJLly49r2N79eqlv/71r97gdvDg\nQW+waty4sYqKirxrvQICAhQQEOAdeZs/f763ni5duni3P//8c+3atUvBwbx9HQCAmmR7ZGu6MWaa\n5TbOye7CYu/nqwJbqMHtQ7R3wUTtdXz0/7Z304wZMzRy5EhNnTpVgYGBmj179nnVP2XKFPXt21eB\ngYGKjIxUUVHROR/74IMP6vPPP5fb7Zafn59GjRqlcePGadSoUQoJCdENN9ygqKgob/nZs2crMTFR\njuOod+/e3v1jx47VmDFj5HK5VKdOHc2ZM6fSqBoAAKh+zrlOa513xY4zWVLR+YStyMhIk5WVZaU/\nMVNWqeCkwHVCUIC/1k2MtdImAAC4cjmOk22MiTxbOdsL5Mc5jrPJcZw3HcdpeLoCjuOMdhwny3Gc\nrP3791vrSHJcsPz9fCvt8/fzVXIc02wAAMCeixrZchxnpaQbTvPVE5I+lnRAkpH0nKSmxpjEM9Vn\nc2RLOr5IfuqKfO0uLFazAH8lxwWfdXE8AADA6ZzryNZFrdkyxtxxjp15XdLyi2mrKsSHBxGuAABA\ntbJ5N2LTkzYHSNpiqy0AAIBLlc27EV9yHCdMx6cRPZIestgWAADAJcla2DLGDD97KQAAgCtbrXpd\nDwAAQHUjbAEAAFhE2AIAALCIsAUAAGARYQsAAMAiwhYAAIBFhC0AAACLCFsAAAAWEbYAAAAsImwB\nAABYRNgCAACwiLAFAABgEWELAADAIsIWAACARYQtAAAAiwhbAAAAFhG2AAAALCJsAQAAWETYAnDe\nPB6PQkJCarobAHBZIGwBAABYRNgCcEHKyso0bNgwtWvXToMGDdLRo0clSS1bttSBAwckSVlZWere\nvbsk6YcfflBiYqKio6MVHh6uZcuWSZLmzJmjgQMHqk+fPmrTpo0ef/xxbxsLFy6Uy+VSSEiIJkyY\nUL0nCABVhLAF4ILk5+dr7Nix+uyzz3Tdddfp1VdfPWP5F154QbGxsdq4caNWr16t5ORk/fDDD5Kk\n3NxcpaSkaPPmzUpJSdE333yj3bt3a8KECVq1apVyc3OVmZmptLS06jg1AKhShC0AkqS0nALFTFml\nVhPfVcyUVUrLKThj+ZtuukkxMTGSpPvvv18ZGRlnLP/BBx9oypQpCgsLU/fu3VVSUqJdu3ZJknr2\n7KkGDRqoXr16at++vb7++mtlZmaqe/fuCgwMVJ06dTRs2DClp6dXzckCQDWqU9MdAFDz0nIKNGnJ\nZhWXluvIp8tVkLdCv/qLo6YN6qmBv98p5UeNGiXHcSrtO7Fdp04dVVRUSJJKSkq83xtjtHjxYgUH\nB1c67pNPPlHdunW9276+viorK6uycwOAmsbIFgBNXZGv4tJySdK1EX3VbOQM3ZDwipqNnKHc3NxT\nfu6++27t2rVLGzZskCQtWLBAnTt3lnR8zVZ2drYkafHixd424uLiNGPGDBljJEk5OTln7FN0dLT+\n9a9/6cCBAyovL9fChQvVrVu3Kj93ALCNsAVAuwuLz2u/JAUHB2vmzJlq166dDh06pDFjxkiSnn76\naf32t79VZGSkfH19veWffPJJlZaWyu12q0OHDnryySfP2KemTZtqypQp6tGjh0JDQ9WxY0f179//\nAs4OAGqWc+K3zEtBZGSkycrKquluALVOzJRVKjhNsAoK8Ne6ibE10CMAuPQ5jpNtjIk8WzlGtgAo\nOS5Y/n6+lfb5+/kqOS74F44AAJwrFsgDUHx4kKTja7d2FxarWYC/kuOCvfsBABeOsAVA0vHARbgC\ngKrHNCIAAIBFhC0AAACLCFsAAAAWEbYAAAAsImwBAABYRNgCAACwiLAFAABgEWELAGrYU089pZUr\nV56yf82aNerbt28N9AhAVeKhpgBQw5599tma7gIAixjZAoCfmTt3rtxut0JDQzV8+HB5PB7FxsbK\n7XarZ8+e2rVrlyQpISFBSUlJ6tSpk1q3bq3U1FRJkjFGycnJCgkJkcvlUkpKirfuP/3pT3K5XAoN\nDdXEiRO99Zw49v3331fbtm0VERGhJUuWeI87ePCg4uPj5Xa7ddttt2nTpk3VdTkAXCRGtgDgJFu3\nbtXzzz+v9evXq3Hjxjp48KBGjBjh/XnzzTeVlJSktLQ0SdKePXuUkZGh7du365577tGgQYO0ZMkS\n5ebmKi8vTwcOHFBUVJS6du2q3NxcLVu2TJ988omuvvpqHTx4sFLbJSUlGjVqlFatWqV///d/15Ah\nQ7zfPf300woPD1daWppWrVqlBx54QLm5udV6bQBcGEa2ANR6aTkFipmySq0mvqt+k16Tu0sfNW7c\nWJLUqFEjbdiwQb/+9a8lScOHD1dGRob32Pj4ePn4+Kh9+/bau3evJCkjI0NDhw6Vr6+vmjRpom7d\nuikzM1MrV67UyJEjdfXVV3vrPtn27dvVqlUrtWnTRo7j6P777/d+l5GRoeHDh0uSYmNj9d133+n7\n77+3d1EAVBnCFoBaLS2nQJOWbFZBYbGMpMLiUq3J36e0nIJzOr5u3brez8YYS70EcDkjbAGo1aau\nyFdxabl3u15ztw5vW6sXl2yUdHytVKdOnbRo0SJJ0vz589WlS5cz1tmlSxelpKSovLxc+/fvV3p6\nuqKjo9WrVy/Nnj1bR48e9dZ9srZt28rj8Wjnzp2SpIULF1aqc/78+ZKO36XYuHFjXXfddRd59gCq\nA2u2ANRquwuLK21fFdhCDW4fotzXHlXo25MVHh6uGTNmaOTIkZo6daoCAwM1e/bsM9Y5YMAAbdiw\nQaGhoXIcRy+99JJuuOEG9enTR7m5uYqMjNRVV12lu+66Sy+++KL3uHr16mnWrFm6++67dfXVV6tL\nly46cuSIJGny5MlKTEyU2+3W1VdfrbfeeqvqLwYAK5xLadg7MjLSZGVl1XQ3ANQiMVNWqeBngUuS\nggL8tW5ibA30CMDlwnGcbGNM5NnKMY0IoFZLjguWv59vpX3+fr5KjguuoR4BuNIwjQigVosPD5J0\nfO3W7sJiNQvwV3JcsHc/AFwswhaAWi8+PIhwBcAaphEBAAAsImwBAABYRNgCAACwiLAFAABgEWEL\n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\nYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# creamos el gráfico\n", "fig = plt.figure(figsize=(10, 8))\n", "ax = fig.add_subplot(1, 1, 1)\n", "\n", "# reducimos el dataset a 15 palabras para el ejemplo\n", "df_e = df.head(15)\n", "\n", "ax.scatter(df_e['x'], df_e['y'])\n", "\n", "for i, txt in enumerate(df_e['palabra']):\n", " ax.annotate(txt, (df_e['x'].iloc[i], df_e['y'].iloc[i]))\n", " \n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Aquí termina este artículo, obviamente el [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) es un campo muy amplio y quedaron muchas cosas sin desarrollar. Espero que esta introducción les haya sido de utilidad.\n", "\n", "Saludos!\n", "\n", "*Este post fue escrito por [Raúl e. López Briega](https://relopezbriega.github.io/) utilizando [Jupyter notebook](https://jupyter.org/). Pueden descargar este [notebook](https://github.com/relopezbriega/relopezbriega.github.io/blob/master/downloads/NLP.ipynb) o ver su version estática en [nbviewer](https://nbviewer.ipython.org/github/relopezbriega/relopezbriega.github.io/blob/master/downloads/NLP.ipynb).*" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.3" } }, "nbformat": 4, "nbformat_minor": 2 }