#!/usr/bin/env python
# coding: utf-8

# # PLN (Procesamiento del lenguaje natural) con Python - Recetas
#   
# **Requisitos: Será necesario instalar la librería NLTK, además de descargar el corpus para las stopwords. Por defecto Conda incluye el paquete NLTK así como Google Colab.  En el caso de que no estuviera instalado NLTK, ejecutar el siguiente chunk**

# In[1]:


# Ejecutar este chunk sólo si no está instalado NLTK
# Descomentar la siguiente línea para instalar la libraría:

#!conda install nltk 


# In[1]:


import nltk


# In[2]:


nltk.download_shell() 
#d) DOwnload:
#stopwords


# ## 1.Descarga de un corpus externo
# Descargaremos el set de datos de películas Cornell CS Movie.  Incluye valoraciones positivas y negativas de diferentes películas.  Los datos pueden descargarse desde:
# http://www.cs.cornell.edu/people/pabo/movie-review-data/mix20_rand700_tokens_cleaned.zip

# In[3]:


from nltk.corpus import CategorizedPlaintextCorpusReader


# El corpus ya está categorizado por múltiples ficheros de texto con revisiones positivas y negativas), por eso usamos **CategorizedPlaintextCorpusReader** en este caso.  Más adelante trabajaremos con datos que no lo están. La clase CategorizedPlainCorpusReader, nos permite cargar los datos manteniendo la categorización.

# In[4]:


reader = CategorizedPlaintextCorpusReader(r'/home/mydoctor/Documents/03.Trabajos/01.C2B/21.Deep Learning - C2B (15h)/scripts/datos/movies/tokens', r'.*\.txt', cat_pattern=r'(\w+)/*', encoding='cp1252')
print(reader.categories())
print(reader.fileids()[1:50])


# Generamos los datos con revisión positiva y negativa

# In[6]:


posFiles = reader.fileids(categories='pos')
negFiles = reader.fileids(categories='neg')


# In[7]:


posFiles [1:10]


# Vamos a extraer de manera aleatoria los nombres de 2 ficheroas (uno con revisión negativa y otro positiva).

# In[8]:


from random import randint
fileP = posFiles[randint(0,len(posFiles)-1)]
fileN = negFiles[randint(0, len(posFiles) - 1)]
print(fileP)
print(fileN)


# Imprimimos cada fichero...

# In[9]:


for w in reader.words(fileP):
    print(w + ' ', end='')
    if (w is '.'):
        print()


# In[10]:


for w in reader.words(fileN):
    print(w + ' ', end='')
    if (w is '.'):
        print()


# ## 2.Contando todas las palabras 'wh'
# 
# Usaremos en este caso el corpus 'Brown' incluido en el paquete NLTK.  Contiene aproximadamente 500 textos categorizados en 15 diferentes géneros y categorías (noticias, humor, ...).

# In[11]:


import nltk
from nltk.corpus import brown


# Descargamos el set de datos.

# In[12]:


nltk.download('brown')


# Las categorías existentes en el set de datos son:

# In[14]:


print(brown.categories())


# Seleccionamos 3 géneros, así como las palabras que queremos contar.

# In[15]:


generos = ['fiction', 'humor', 'romance']
palabraswh = ['what', 'which', 'how', 'why', 'when', 'where', 'who']


# In[16]:


for i in range(0,len(generos)):
    genero = generos[i]
    print()
    print("Analizando '"+ genero)
    texto_generos = brown.words(categories = genero)
    print (texto_generos)


# Hemos extraído para cada género los textos en brown.  Ahora comprobaremos la distribución de frecuencias, para cada categoría seleccionada.

# In[17]:


for i in range(0,len(generos)):
    genero = generos[i]
    print()
    print("Analizando '"+ genero)
    texto_generos = brown.words(categories = genero)
    print (texto_generos)
    fdist = nltk.FreqDist(texto_generos)
    print (fdist)


# Podemos hacer lo mismo, para las palabras wh

# In[18]:


for wh in palabraswh:
    print(wh + ':', fdist[wh], end=' ')


# Estamos iterando palabraswb y obteniendo el total de ocurrencias de cada caso (what aparece 121 veces en la categoría romance, which 104, ...).  Juntando todos los pasos...

# In[19]:


print(brown.categories())
for i in range(0,len(generos)):
    genero = generos[i]
    print()
    print("Analizando '"+ genero+"'")
    texto_generos = brown.words(categories = genero)
    fdist = nltk.FreqDist(texto_generos)
    for wh in palabraswh:
        print(wh + ':', fdist[wh], end=' ')


# ## 3.Analizar la distribución de frecuencias de corpuses en la web y en ficheros de chats
# 
# Aprovecharemos el set de datos webtext de la librería NLTK

# In[20]:


import nltk
from nltk.corpus import webtext
nltk.download('webtext')


# In[21]:


print(webtext.fileids())


# Analizamos el set singles.txt, que va a ser nuestro conjunto de datos "objetivo".

# In[25]:


fileid = 'singles.txt'
wbt_words = webtext.words(fileid)
fdist = nltk.FreqDist(wbt_words)
print (fdist)


# Podemos extraer la palabra más común...

# In[26]:


print('Total de apariciones del token "',fdist.max(),'" : ', fdist[fdist.max()])


# Podemos extraer el total de tokens diferentes en nuestro corpus

# In[28]:


print('Total de tokens distintos en el corpus: ', fdist.N())


# Extraemos los 10 tokens más habituales en el corpus

# In[29]:


print('Los 10 tokens más comunes del corpus:', fdist.most_common(10))


# Pintamos una gráfica con las frecuencias acumuladas de los 20 elementos más habituales.

# In[30]:


fdist.plot(20,cumulative=True)


# Hemos visto que el token más común es ',', seguido de '.' debido a que no hemos preprocesado los datos (eliminación de stopwords, ...).

# ## 4.Extracción de textos a través de BeautifulSoup

# La librería BeautifulSoup tiene unas potentes funciones para el tratamiento de textos.  Vamos usarla para limpiar el texto descargado desde una web.

# In[31]:


import nltk
import urllib.request


# In[32]:


response = urllib.request.urlopen('http://php.net/')
html = response.read()
print (html)


# El resultado contiene muchas etiquetas HTML que deben limpiarse.  En este caso usaremos BeautifulSoup

# In[33]:


from bs4 import BeautifulSoup
import urllib.request

response = urllib.request.urlopen('http://php.net/')
html = response.read()
soup = BeautifulSoup(html,"html5lib")
text = soup.get_text(strip=True)

print (text)


# Hemos eliminado a través de BeautifulSoup aquellas referencias html del texto.  Seguimos.  Extraemos los tokens del texto.

# In[34]:


tokens = [t for t in text.split()]
tokens [1:20]


# Contamos la frecuencia de palabras...

# In[35]:


freq = nltk.FreqDist(tokens)

for key,val in freq.items():
    print (str(key) + ':' + str(val))


# ¿Cuáles son los token más frecuentes?

# In[36]:


freq.plot(20, cumulative=False)


# Eliminamos StopWords

# In[38]:


from nltk.corpus import stopwords
stopwords.words('english')


# Convertimos todo a minúsculas...

# In[39]:


tokens = [x.lower() for x in tokens]


# In[57]:


tokens_limpios = tokens[:]
sr = stopwords.words('english')
for token in tokens:
    if token in stopwords.words('english'):
        tokens_limpios.remove(token)


# Extraemos los tokens más habituales...

# In[58]:


freq = nltk.FreqDist(tokens_limpios)
for key,val in freq.items():
    print (str(key) + ':' + str(val))


# In[59]:


freq.plot(20,cumulative=False)


# ## 5.Leer pdfs, docs y txt con Python y generar un corpus

# Es necesario instalar para este caso, la librería PyPDF2 y la librería word

# In[ ]:


#pip install docx2python


# In[ ]:


#pip install PyPDF2
## pip install word
## pip install pdfminer.six


# In[ ]:


#pip install pdfminer


# Importamos las clases que nos permiten leer pdf y docs

# In[60]:


import os
#import PyPDF2
import pdfminer as pdfm
from docx2python import docx2python
#from PyPDF2 import PdfFileReader
from nltk.corpus.reader.plaintext import PlaintextCorpusReader


# Generamos una función que nos devuelva el contenido de texto de un fichero de texto.

# In[61]:


def obtener_texto(nombre_txt):
    archivo = open (nombre_txt, 'rb') # apertura del fichero en modo binario
    return archivo.read()
ruta = get_ipython().run_line_magic('pwd', '')


# In[65]:


txt1


# In[64]:


txt1 = obtener_texto(ruta + '/datos/reading/ejemplo_feed.txt')


# In[66]:


from io import StringIO

from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfparser import PDFParser

output_string = StringIO()
with open(ruta +'/datos/reading/ejemplo-una-linea.pdf', 'rb') as in_file:
    parser = PDFParser(in_file)
    doc = PDFDocument(parser)
    rsrcmgr = PDFResourceManager()
    device = TextConverter(rsrcmgr, output_string, laparams=LAParams())
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    for page in PDFPage.create_pages(doc):
        interpreter.process_page(page)

txt2 = output_string.getvalue()


# In[69]:


txt2


# In[68]:


# Ejecutar sólo en linux/mac
txt2 = txt2 [:-4]


# In[74]:


txt3 = docx2python(ruta + '/datos/reading/ejemplo-una-linea.docx')
txt3.body


# In[75]:


txt3 = txt3.body[0][0][0][0]


# In[76]:


txt3


# Guardamos el resultado en nuestro equipo en una carpeta llamada _micorpus_.

# In[77]:


nuevocorpus = 'micorpus/'
if not os.path.isdir(nuevocorpus): # existe nuevocorpus?
    os.mkdir(nuevocorpus)


# Guardamos los 3 ficheros cargados previamente.

# In[78]:


files = [txt1,txt2,txt3]  # Generación array con objetos a usar en la iteración.
files


# In[79]:


for idx, f in enumerate(files):  
    with open(nuevocorpus+str(idx)+'.txt', 'w') as fileout:
        fileout.write(str(f))


# Generamos el corpus.  Aquí se identifican internamente los párrafos, sentencias, palabras, ...

# In[80]:


corpus = PlaintextCorpusReader (ruta +'/datos/reading/', '.*')


# In[81]:


print (corpus.words())


# Para poder extraer las sentencias del corpus, necesitamos instalar un paquete...

# In[82]:


import nltk
nltk.download('punkt')


# In[87]:


corpus.fileids()[0]


# In[90]:


print(corpus.paras(corpus.fileids()[0]))


# ## 6. Tokenizar textos que no están en inglés.

# In[91]:


from nltk.tokenize import sent_tokenize
texto = "Bonjour M. Adam, comment allez-vous? J'espère que tout va bien. Aujourd'hui est un bon jour."

print(sent_tokenize(texto,"french"))


# # 7. Obtención de sinónimos (Wordnet)

# In[97]:


from nltk.corpus import wordnet

syn = wordnet.synsets("happy")
print(syn[0].definition())
print(syn[0].examples())


# In[94]:


syn = wordnet.synsets("python")
print(syn[0].definition())
print(syn[0].examples())


# In[104]:


sinonimos = []

for syn in wordnet.synsets('happy'):
    for lemma in syn.lemmas():
        sinonimos.append(lemma.name())

print(sinonimos)


# # 8. Extracción de lemas usando Wordnet

# In[105]:


from nltk.stem import WordNetLemmatizer

lema = WordNetLemmatizer()
print(lema.lemmatize('increases'))


# Puede suceder que una misma palabra sea un sustantivo o verbo en función del contexto.  Podemos indicarle al lematizador, que nos devuelva el lema para una palabra que sea un verbo.

# In[106]:


print(lema.lemmatize('playing', pos="v"))


# ¿Y si le pedimos que nos devuelva los lemas en función de los diferentes contextos?...

# In[107]:


print(lema.lemmatize('playing', pos="v")) # verbos
print(lema.lemmatize('playing', pos="n")) # sustantivos
print(lema.lemmatize('playing', pos="a")) # adjetivos


# # 9. Extracción de steams para otro idioma distinto al inglés.

# In[108]:


from nltk.stem import SnowballStemmer
print(SnowballStemmer.languages)


# In[109]:


castellano = SnowballStemmer('english')
print(castellano.stem("Palabra"))


# No ha funcionado.

# In[110]:


castellano = SnowballStemmer('spanish')
print(castellano.stem("Palabra"))


# # 10. Extracción de steams (formas canónicas) y lemas

# In[111]:


from nltk.stem import WordNetLemmatizer
from nltk.stem import PorterStemmer

steams = PorterStemmer()
lemas = WordNetLemmatizer()

print(steams.stem('stones'))
print(steams.stem('speaking'))
print(steams.stem('bedroom'))
print(steams.stem('jokes'))
print(steams.stem('lisa'))
print(steams.stem('purple'))
print('----------------------')
print(lemas.lemmatize('stones'))
print(lemas.lemmatize('speaking'))
print(lemas.lemmatize('bedroom'))
print(lemas.lemmatize('jokes'))
print(lemas.lemmatize('lisa'))
print(lemas.lemmatize('purple'))

