from sklearn.feature_extraction.text import CountVectorizer
from nltk.corpus import stopwords
import re
from math import log

main_dir = '/Users/jonathanestrella/Documents/MineriaDeDatos/data/'
file = main_dir+'archivo.txt'
d = main_dir+'archivo_prueba.txt'
C = ['H', 'M'] # Tipos de género

def ExtractVocabulary(D): # Extraer el vocabulario
    stop_words = stopwords.words('spanish') # Cargar lista precompilada de stop words
    file=D
    corpus = []
    with open(file, 'r', encoding='utf-8') as content_file: # Lectura de archivo
        for line in content_file:
            line = line.rstrip().lower() # Separar por espacios y convertir en minusculas
            words = re.findall('[a-záéíóúñ]+', line) # Recuperar palabras por medio de expresiones regulares
            text = ' '.join(word for word in words if word not in stop_words and len(word)>3) # eliminar stop words y palabras de una longitud menor a 3
            corpus.append(text) # Agregar texto de vocabulario a corpus 
        vec = CountVectorizer()
        tdm = vec.fit_transform(corpus) # Obtener matriz 
        vocabulary = vec.vocabulary_  # Obtner vocabulario en forma de diccionario
        lista= list(vocabulary.keys()) # Obtener vocabulario en forma de lista
    return(lista)

def CountDocsInClass(D, c): # Contar documentos por género
    counter = 0
    with open(D, 'r', encoding='utf-8') as content_file: # Lectura de archivo
        for line in content_file:
            if (line[0] == c): # Si el primer caracter es M o H
                counter += 1 
    return counter

def ConcaTextOfAllDocsInClass(D, c): # Concatenar todas las publicaciones de un genero
    text = ""
    with open(D, 'r', encoding = 'utf-8') as i_r: # Lectura de archivo
        for line in i_r:
            if line[0] == c: # Si el primer caracter es M o H
                text += line[1:] # Solo concatenar desde el segundo caracter porque no se quiere recuperar H o M
        
    text = text.lower()
    text = text.replace('\n', ' ') # Eliminar saltos de linea
    return text

def CountTokensOfTerm(textc, t): # Contar el número de veces que aparece una palabra en una cadena de caracteres
    val = textc.count(t) # contar todas las coincidencias de una serie de caracteres dentro de una cadena de caracteres
    return val

def TrainMultinomialNB(C, D):
    V = ExtractVocabulary(D) # Extraer vocabulario
    N = sum(1 for line in open (file, encoding='utf-8')) # Count docs
    
    prior = {'H': 0, 'M': 0} # Diccionario de tipos de genero
    condprob = []
    
    for v in range(len(V)):
        condprob.append([]) # Inicializar condprob como lista de listas
    
    for c in C: # Iterar sobre cada género
        Tct = []
        Nc = CountDocsInClass(D, c) # Contar documentos de un género
        prior[c] = Nc / N # calcular prioridad de un genero
        textc = ConcaTextOfAllDocsInClass(D, c) # concatenar publicaciones de un género
        for i in range(len(V)): # Iterar sobre el vocabulario
            Tct.append(CountTokensOfTerm(textc, V[i])) # Agregar el valor de cuantas veces aparece un termino en la concatenacion de las publicaciones de un genero
        
        for i in range(len(V)): # Iterar sobre el vocabulario
            condprob[i].append((Tct[i] + 1) / (sum(Tct) + 1)) # Calcular el valor de condprob para cada palabra del vocabulario
    
    return V, prior, condprob


def applyB(C,V,prior,contprob,d):
    W=[]
    score=[]
    
    with open(d,'r',encoding='utf-8') as content_file:# Lectura de documento a analizar
        text=content_file.read()
    text=text.lower() # documento que se va a analizar en minusculas
    
    for p in V:
        cont=text.count(p) # Saber si alguna palabra del vocabulario aparece en el documento a analizar
        if cont>0:
            W.append(p) # Si aparece, agregarla a W
    
    for c in range(len(C)): # Iterar sobre cada género
        
        if prior[c]==0: # Esto es debido a que el log(0) no existe
            score.append(0)
        else:
            score.append(log(prior[c])) # calcular el logaritmo de cada género
        
        for t in W: # Iterar sobre la lista W
            score[c]+=log(contprob[V.index(t)][c]) # Sumar el logaritmo de condprob de acuerdo a la palabra y género
    
    # print(score)
    return(C[score.index(max(score))]) # Regresar el género donde la probabilidad es mas alta


# PRUEBA
Voc, prior, condprob = TrainMultinomialNB(C, file) # Llamar a la función de entrenamiento y almacenar los valores de retorno

prior_lista = [prior['H'], prior['M']] # Convertir los valores del diccionario a una lista 
result=applyB(C, Voc, prior_lista, condprob ,d) # Aplicar el algoritmo sobre un documento de prueba
print(result) # Imprimir a que género podría pertenecer el documento
