File size: 10,413 Bytes
7bf61c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612da4e
 
7bf61c5
612da4e
7bf61c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612da4e
7bf61c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612da4e
7bf61c5
612da4e
7bf61c5
 
 
 
 
 
612da4e
7bf61c5
 
612da4e
 
 
7bf61c5
612da4e
7bf61c5
612da4e
 
7bf61c5
 
612da4e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
import streamlit as st
import pandas as pd
import re
import nltk
import numpy as np
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from nltk.stem import SnowballStemmer
from transformers import pipeline
from secciones.model_utils import cargar_modelo_y_vectorizador

# Descargar recursos de NLTK
nltk.download('punkt')
nltk.download('stopwords')


# Funci贸n de limpieza y preprocesamiento de texto
def clean_text_stemming(text):
    """
    Realiza la limpieza y preprocesamiento de un texto. Convierte el texto a min煤sculas,
    elimina caracteres no alfab茅ticos, tokeniza, remueve stopwords y aplica stemming.

    :param text: El texto a limpiar y preprocesar.
    :return: Texto procesado y limpio.
    """
    text = text.lower()
    text = re.sub(r'[^a-z帽谩茅铆贸煤]', ' ', text)
    words = word_tokenize(text)
    stop_words = set(stopwords.words('spanish'))
    stemmer = SnowballStemmer('spanish')
    stemmed_text = ' '.join([stemmer.stem(word) for word in words if word not in stop_words])
    return stemmed_text


# Cargar modelos de Hugging Face para an谩lisis de sentimientos y emociones
clasificador_sentimiento = pipeline('sentiment-analysis',
                                    model='citizenlab/twitter-xlm-roberta-base-sentiment-finetunned')
clasificador_emociones = pipeline("text-classification",
                                  model="maxpe/bertin-roberta-base-spanish_sem_eval_2018_task_1")


# Funciones para analizar sentimiento y emociones
def analizar_sentimiento(texto):
    """
    Analiza el sentimiento de un texto dado utilizando un modelo preentrenado de Hugging Face.

    :param texto: El texto a analizar.
    :return: La etiqueta del sentimiento detectado (ej. "Positive", "Negative", "Neutral").
             Retorna None si ocurre un error.
    """
    try:
        results = clasificador_sentimiento(texto, truncation=True, max_length=512)
        top_result = max(results, key=lambda x: x['score'])
        return top_result['label']
    except Exception as e:
        print(f"Error al procesar el texto: {e}")
        return None


def analizar_emociones(texto):
    """
    Analiza las emociones presentes en un texto utilizando un modelo preentrenado de Hugging Face.

    :param texto: El texto a analizar.
    :return: La etiqueta de la emoci贸n detectada (ej. "alegr铆a", "tristeza").
             Retorna None si ocurre un error.
    """
    try:
        results = clasificador_emociones(texto, truncation=True, max_length=512)
        top_result = max(results, key=lambda x: x['score'])
        return top_result['label']
    except Exception as e:
        print(f"Error al procesar el texto: {e}")
        return None


# Normalizaci贸n de la longitud del texto
def normalizar_longitud(texto):
    """
    Normaliza la longitud de un texto dividiendo la longitud del texto por una longitud m谩xima observada.

    :param texto: El texto cuya longitud se va a normalizar.
    :return: Longitud normalizada del texto.
    """
    longitud_maxima_observada = 53352
    return len(texto) / longitud_maxima_observada


# Obtener caracter铆sticas de polaridad y emociones
def obtener_caracteristicas_polaridad(texto):
    """
    Obtiene caracter铆sticas de polaridad (positiva, negativa, neutral) de un texto analizando su sentimiento.

    :param texto: El texto a analizar.
    :return: Un diccionario con las caracter铆sticas de polaridad en formato binario.
    """
    polaridad = analizar_sentimiento(texto)
    return {
        "polarity_Negative": 1 if polaridad == "Negative" else 0,
        "polarity_Neutral": 1 if polaridad == "Neutral" else 0,
        "polarity_Positive": 1 if polaridad == "Positive" else 0
    }


def obtener_caracteristicas_emociones(texto):
    """
    Obtiene caracter铆sticas de emociones (ira, anticipaci贸n, miedo, alegr铆a, tristeza) de un texto.

    :param texto: El texto a analizar.
    :return: Un diccionario con las caracter铆sticas de emociones en formato binario.
    """
    emocion = analizar_emociones(texto)
    categorias = ["anger", "anticipation", "fear", "joy", "sadness"]
    return {f"emotions_{cat}": 1 if emocion == cat else 0 for cat in categorias}


# Funci贸n para realizar predicciones con informaci贸n adicional
def predecir_suicidio_con_info(texto, modelo, tfidf_vectorizador):
    """
    Realiza la predicci贸n de comportamiento suicida en un texto dado, utilizando un modelo
    de machine learning y un vectorizador TF-IDF. Incluye caracter铆sticas adicionales
    como longitud del texto, polaridad y emociones.

    :param texto: Texto a analizar.
    :param modelo: Modelo de machine learning para realizar la predicci贸n.
    :param tfidf_vectorizador: Vectorizador TF-IDF para procesar el texto.
    :return: Un diccionario con los resultados de la clasificaci贸n y an谩lisis.
    """
    texto_limpio = clean_text_stemming(texto)

    # Obtener caracter铆sticas
    longitud_normalizada = normalizar_longitud(texto)
    caracteristicas_polaridad = obtener_caracteristicas_polaridad(texto)
    caracteristicas_emociones = obtener_caracteristicas_emociones(texto)
    polaridad = analizar_sentimiento(texto)
    emocion = analizar_emociones(texto)

    # Vectorizar texto y combinar caracter铆sticas
    texto_tfidf = tfidf_vectorizador.transform([texto_limpio]).toarray()
    caracteristicas_adicionales = np.array(
        [longitud_normalizada] + list(caracteristicas_polaridad.values()) + list(caracteristicas_emociones.values()))
    features = np.hstack((texto_tfidf, caracteristicas_adicionales.reshape(1, -1)))

    # Aseg煤rate de que el DataFrame tenga los mismos nombres de columnas que se usaron durante el entrenamiento
    column_names = tfidf_vectorizador.get_feature_names_out().tolist() + ['text_length', 'polarity_Negative',
                                                                          'polarity_Neutral', 'polarity_Positive',
                                                                          'emotions_anger', 'emotions_anticipation',
                                                                          'emotions_fear', 'emotions_joy',
                                                                          'emotions_sadness']
    features_df = pd.DataFrame(features, columns=column_names)

    # Realizar la predicci贸n
    pred = modelo.predict(features_df)
    pred_proba = modelo.predict_proba(features_df)[0]

    # Confianza y nivel de riesgo
    confianza = abs(pred_proba[1] - pred_proba[0])
    nivel_riesgo = 'ALTO' if pred_proba[1] > 0.75 else 'MODERADO' if pred_proba[1] > 0.5 else 'BAJO'
    accion = 'Se recomienda buscar ayuda profesional inmediatamente.' if nivel_riesgo == 'ALTO' \
        else 'Se sugiere monitorear los sentimientos y considerar hablar con un profesional.' \
        if nivel_riesgo == 'MODERADO' \
        else 'Probablemente no hay riesgo inmediato, pero mant茅n una actitud positiva.'

    # Traducciones de las etiquetas de polaridad y emociones al espa帽ol
    traducciones_polaridad = {
        "Negative": "Negativa",
        "Neutral": "Neutral",
        "Positive": "Positiva"
    }
    traducciones_emociones = {
        "anger": "ira",
        "anticipation": "Anticipaci贸n",
        "fear": "Miedo",
        "joy": "Alegr铆a",
        "sadness": "Tristeza"
    }

    # Traducir polaridad y emociones
    polaridad_traducida = traducciones_polaridad.get(polaridad, polaridad)
    emocion_traducida = traducciones_emociones.get(emocion, emocion)

    return {
        "clasificacion": 'Alerta de riego suicida' if pred[0] == 1 else 'Ausencia de riego de suicidio',
        "probabilidad_suicidio": pred_proba[1],
        "confianza": confianza,
        "nivel_riesgo": nivel_riesgo,
        "sugerencia_accion": accion,
        "polaridad": polaridad_traducida,
        "emocion": emocion_traducida
    }


def procesar_textos():

    """
    Funci贸n principal para procesar textos en la aplicaci贸n Streamlit. Permite al usuario
    ingresar o subir un texto, y luego utiliza el modelo y el vectorizador para analizarlo.
    """
    st.title("Analizar Texto")
    modelo, tfidf_vectorizador = cargar_modelo_y_vectorizador()
    # Opci贸n para ingresar texto manualmente
    text_input = st.text_area("Ingrese su texto aqu铆:")

    # Opci贸n para subir un archivo
    uploaded_file = st.file_uploader("O suba un archivo de texto:", type=["txt"])

    # Bot贸n para procesar el texto
    if st.button("Procesar Texto"):
        text_to_process = ""
        if uploaded_file is not None:
            # Leer el archivo subido y almacenar su contenido
            text_to_process = uploaded_file.read().decode("utf-8")
        elif text_input:
            # Utilizar el texto ingresado manualmente
            text_to_process = text_input

        if text_to_process:
            # Llamar a la funci贸n de predicci贸n con el texto procesado
            resultado = predecir_suicidio_con_info(text_to_process, modelo, tfidf_vectorizador)

            # Estilos personalizados seg煤n el nivel de riesgo
            if resultado['nivel_riesgo'] == 'ALTO':
                color = "red"
            elif resultado['nivel_riesgo'] == 'MODERADO':
                color = "orange"
            else:
                color = "green"

            # Mostrar los resultados con estilos
            st.markdown(f"<h2 style='color: {color};'>Resultado del An谩lisis:</h2>", unsafe_allow_html=True)
            st.markdown(f"<b>Grado de Riesgo:</b> <span style='color: {color};'>{resultado['nivel_riesgo']}</span>",
                        unsafe_allow_html=True)
            st.markdown(f"<b>Polaridad:</b> {resultado['polaridad']}", unsafe_allow_html=True)
            st.markdown(f"<b>Emoci贸n dominante:</b> {resultado['emocion']}", unsafe_allow_html=True)
            st.markdown(f"<b>Clasificaci贸n de riesgo:</b> {resultado['clasificacion']}", unsafe_allow_html=True)
            st.markdown(f"<b>Indice de riesgo de suicidio:</b> {resultado['probabilidad_suicidio']:.4f}",
                        unsafe_allow_html=True)
            st.markdown(f"<b>Fiabilidad del an谩lisis:</b> {resultado['confianza']:.4f}", unsafe_allow_html=True)

            st.markdown(f"<b>Recomendaciones de seguimiento:</b> {resultado['sugerencia_accion']}",
                        unsafe_allow_html=True)
        else:
            st.warning("Por favor, ingrese texto o suba un archivo.")