File size: 9,235 Bytes
e125fdd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import pandas as pd
import re
from datetime import datetime
import streamlit as st
from transformers import pipeline
import nltk
from nltk.corpus import stopwords
from nltk.util import ngrams
from collections import Counter
import plotly.express as px

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

# Cargar el pipeline de an谩lisis de sentimientos
sentiment_analysis = pipeline('sentiment-analysis', model='dccuchile/bert-base-spanish-wwm-uncased')

# Funci贸n para procesar el archivo .txt de WhatsApp
def cargar_chat_txt(file):
    content = file.getvalue().decode('utf-8')
    lines = content.splitlines()
    
    fechas = []
    autores = []
    mensajes = []

    pattern = r"(\d{1,2}/\d{1,2}/\d{4}), (\d{1,2}:\d{2}\s?[ap]\.?\s?[m]\.?) - (.*?):(.*)"
    
    for line in lines:
        if "cifrados de extremo a extremo" in line:
            continue
        
        match = re.match(pattern, line.strip())
        
        if match:
            fecha = match.group(1)
            hora = match.group(2)
            autor = match.group(3).strip()
            mensaje = match.group(4).strip()

            hora = hora.replace("\u202f", "").strip()
            hora = hora.replace(".", "")
            
            fecha_hora_str = f"{fecha} {hora}"

            try:
                fecha_hora = datetime.strptime(fecha_hora_str, "%d/%m/%Y %I:%M%p")
            except ValueError as e:
                print(f"Error al parsear la fecha y hora: {fecha_hora_str} - {e}")
                continue

            fechas.append(fecha_hora)
            autores.append(autor)
            mensajes.append(mensaje)
    
    df = pd.DataFrame({
        'FechaHora': fechas,
        'Autor': autores,
        'Mensaje': mensajes
    })
    
    if 'FechaHora' in df.columns and 'Autor' in df.columns and 'Mensaje' in df.columns:
        df['FechaHora'] = pd.to_datetime(df['FechaHora'])
        return df
    else:
        return None

# Funci贸n para quitar las stopwords de los mensajes
def quitar_stopwords(mensaje):
    stop_words = set(stopwords.words('spanish'))
    mensaje_tokens = nltk.word_tokenize(mensaje.lower())
    mensaje_filtrado = [word for word in mensaje_tokens if word not in stop_words]
    return ' '.join(mensaje_filtrado)

# Funci贸n para extraer bigramas y trigramas
def extraer_bigrams_trigrams(mensaje):
    tokens = nltk.word_tokenize(mensaje.lower())
    bigrams = list(ngrams(tokens, 2))
    trigrams = list(ngrams(tokens, 3))
    return bigrams, trigrams

# Funci贸n para clasificar la urgencia basada en el autor
def urgencia_por_autor(autor):
    autores_prioritarios = ["Jefe", "Hijo", "Mam谩", "Pap谩", "Esposa"]
    
    if any(char in autor for char in ["鉂わ笍", "馃挅", "馃挊", "馃挐", "馃挄"]):
        return 2  # Asignar urgencia alta si el autor tiene coraz贸n en su nombre
    
    return 2 if autor in autores_prioritarios else 0

# Funci贸n para clasificar la urgencia basada en la hora
def urgencia_por_hora(hora):
    hora = datetime.strptime(hora, "%H:%M")
    if hora >= datetime.strptime("20:00", "%H:%M") or hora <= datetime.strptime("05:00", "%H:%M"):
        return 1
    return 0

# Funci贸n para clasificar la urgencia basada en el sentimiento
def urgencia_por_sentimiento(sentimiento):
    if sentimiento == 'LABEL_4':  # Muy negativo
        return 3  # Alta urgencia
    elif sentimiento == 'LABEL_3':  # Negativo
        return 2  # Urgencia moderada
    elif sentimiento == 'LABEL_2':  # Neutro
        return 1  # Baja urgencia
    elif sentimiento == 'LABEL_1':  # Positivo
        return 1  # Baja urgencia
    elif sentimiento == 'LABEL_0':  # Muy positivo
        return 0  # Sin urgencia
    return 0  # Si no coincide con ning煤n sentimiento, asignar baja urgencia

# Funci贸n para verificar si el mensaje contiene palabras clave de urgencia
def urgencia_por_palabras_clave(mensaje):
    palabras_clave = ["urgente", "es urgente", "es para hoy", "necesito ayuda", "por favor", "con urgencia"]
    mensaje = mensaje.lower()
    
    for palabra in palabras_clave:
        if palabra in mensaje:
            return 1  # Incrementa urgencia
    return 0  # No hay urgencia en las palabras clave

# Funci贸n para verificar si el mensaje contiene palabras negativas
def urgencia_por_palabras_negativas(mensaje):
    palabras_negativas = ["malo", "no me gusta", "odio", "peor", "terrible", "desastroso", "fatal"]
    mensaje = mensaje.lower()
    
    for palabra in palabras_negativas:
        if palabra in mensaje:
            return 2  # Incrementa urgencia por negatividad
    return 0  # No hay urgencia en las palabras negativas

# Funci贸n para mapear las etiquetas del an谩lisis de sentimientos a etiquetas legibles
def mapear_sentimiento(sentimiento):
    sentimiento_mapeado = {
        'LABEL_0': 'Muy Positivo',
        'LABEL_1': 'Positivo',
        'LABEL_2': 'Neutro',
        'LABEL_3': 'Negativo',
        'LABEL_4': 'Muy Negativo'
    }
    return sentimiento_mapeado.get(sentimiento, 'Desconocido')  # Si no encuentra la etiqueta, regresa "Desconocido"


# Funci贸n para calcular el nivel de urgencia
def calcular_urgencia(row):
    mensaje_filtrado = quitar_stopwords(row['Mensaje'])
    
    # Extraer bigramas y trigramas
    bigrams, trigrams = extraer_bigrams_trigrams(mensaje_filtrado)

    # An谩lisis de sentimiento
    result = sentiment_analysis(mensaje_filtrado)
    sentimiento = result[0]['label']
    probabilidad = result[0]['score']

    # Mapear el sentimiento a texto legible
    sentimiento_legible = mapear_sentimiento(sentimiento)

    # Si la probabilidad es baja, clasificar como "Neutro"
    if probabilidad < 0.6:
        sentimiento_legible = 'Neutro'
    
    # Clasificaci贸n de urgencia
    urgencia = urgencia_por_autor(row['Autor']) + urgencia_por_hora(row['FechaHora'].strftime('%H:%M')) + urgencia_por_sentimiento(sentimiento)
    
    urgencia += urgencia_por_palabras_clave(row['Mensaje'])
    urgencia += urgencia_por_palabras_negativas(row['Mensaje'])
    
    # Ahora, tambi茅n vamos a agregar la urgencia por los bigramas y trigramas
    for bigram in bigrams + trigrams:
        bigram_str = ' '.join(bigram)
        sentiment_bigram = sentiment_analysis(bigram_str)[0]['label']
        urgencia += urgencia_por_sentimiento(sentiment_bigram)

    return min(5, urgencia), sentimiento_legible  # Regresamos la urgencia y el sentimiento legible


# Funci贸n para extraer y contar bigramas y trigramas de un DataFrame
def obtener_bigrams_trigrams(df):
    bigramas = []
    trigramas = []
    
    for mensaje in df['Mensaje']:
        bigrams, trigrams = extraer_bigrams_trigrams(mensaje)
        bigramas.extend([' '.join(bigram) for bigram in bigrams])
        trigramas.extend([' '.join(trigram) for trigram in trigrams])
    
    return bigramas, trigramas

# Funci贸n para visualizar bigramas y trigramas en un gr谩fico
def mostrar_grafica_bigrams_trigrams(bigramas, trigramas):
    # Contamos las ocurrencias de bigramas y trigramas
    bigram_count = Counter(bigramas).most_common(10)
    trigram_count = Counter(trigramas).most_common(10)

    # Bigramas
    bigram_df = pd.DataFrame(bigram_count, columns=['Bigram', 'Frecuencia'])
    fig_bigram = px.bar(bigram_df, x='Bigram', y='Frecuencia', title="Top 10 Bigramas m谩s comunes")
    
    # Trigramas
    trigram_df = pd.DataFrame(trigram_count, columns=['Trigram', 'Frecuencia'])
    fig_trigram = px.bar(trigram_df, x='Trigram', y='Frecuencia', title="Top 10 Trigramas m谩s comunes")
    
    return fig_bigram, fig_trigram

# Streamlit application code
st.title("An谩lisis de Chat de WhatsApp")

uploaded_file = st.file_uploader("Sube un archivo TXT de chat de WhatsApp", type=["txt"])

if uploaded_file is not None:
    df_chat = cargar_chat_txt(uploaded_file)

    if df_chat is not None and not df_chat.empty:
        st.write("Primeros mensajes del chat:")
        st.dataframe(df_chat.head())

        # Calcular la urgencia y sentimiento
        df_chat[['Urgencia', 'Sentimiento']] = df_chat.apply(calcular_urgencia, axis=1, result_type='expand')

        st.write("Mensajes con su nivel de urgencia y sentimiento:")
        st.dataframe(df_chat[['FechaHora', 'Autor', 'Mensaje', 'Urgencia', 'Sentimiento']])

        # Extraer bigramas y trigramas
        bigramas, trigramas = obtener_bigrams_trigrams(df_chat)

        # Mostrar gr谩ficas
        fig_bigram, fig_trigram = mostrar_grafica_bigrams_trigrams(bigramas, trigramas)
        
        st.plotly_chart(fig_bigram)
        st.plotly_chart(fig_trigram)

        # Visualizaci贸n del nivel de urgencia
        if st.button('Mostrar Gr谩fico de Urgencia'):
            urgencia_count = df_chat['Urgencia'].value_counts().reset_index()
            urgencia_count.columns = ['Urgencia', 'Cantidad']
            fig = px.bar(urgencia_count, x='Urgencia', y='Cantidad', title="Distribuci贸n de Niveles de Urgencia")
            st.plotly_chart(fig)
    else:
        st.write("No se encontraron mensajes en el archivo.")