|
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
|
|
|
|
|
|
nltk.download('stopwords')
|
|
nltk.download('punkt')
|
|
|
|
|
|
sentiment_analysis = pipeline('sentiment-analysis', model='dccuchile/bert-base-spanish-wwm-uncased')
|
|
|
|
|
|
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
|
|
|
|
|
|
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)
|
|
|
|
|
|
def extraer_bigrams_trigrams(mensaje):
|
|
tokens = nltk.word_tokenize(mensaje.lower())
|
|
bigrams = list(ngrams(tokens, 2))
|
|
trigrams = list(ngrams(tokens, 3))
|
|
return bigrams, trigrams
|
|
|
|
|
|
def urgencia_por_autor(autor):
|
|
autores_prioritarios = ["Jefe", "Hijo", "Mam谩", "Pap谩", "Esposa"]
|
|
|
|
if any(char in autor for char in ["鉂わ笍", "馃挅", "馃挊", "馃挐", "馃挄"]):
|
|
return 2
|
|
|
|
return 2 if autor in autores_prioritarios else 0
|
|
|
|
|
|
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
|
|
|
|
|
|
def urgencia_por_sentimiento(sentimiento):
|
|
if sentimiento == 'LABEL_4':
|
|
return 3
|
|
elif sentimiento == 'LABEL_3':
|
|
return 2
|
|
elif sentimiento == 'LABEL_2':
|
|
return 1
|
|
elif sentimiento == 'LABEL_1':
|
|
return 1
|
|
elif sentimiento == 'LABEL_0':
|
|
return 0
|
|
return 0
|
|
|
|
|
|
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
|
|
return 0
|
|
|
|
|
|
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
|
|
return 0
|
|
|
|
|
|
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')
|
|
|
|
|
|
|
|
def calcular_urgencia(row):
|
|
mensaje_filtrado = quitar_stopwords(row['Mensaje'])
|
|
|
|
|
|
bigrams, trigrams = extraer_bigrams_trigrams(mensaje_filtrado)
|
|
|
|
|
|
result = sentiment_analysis(mensaje_filtrado)
|
|
sentimiento = result[0]['label']
|
|
probabilidad = result[0]['score']
|
|
|
|
|
|
sentimiento_legible = mapear_sentimiento(sentimiento)
|
|
|
|
|
|
if probabilidad < 0.6:
|
|
sentimiento_legible = 'Neutro'
|
|
|
|
|
|
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'])
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
def mostrar_grafica_bigrams_trigrams(bigramas, trigramas):
|
|
|
|
bigram_count = Counter(bigramas).most_common(10)
|
|
trigram_count = Counter(trigramas).most_common(10)
|
|
|
|
|
|
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")
|
|
|
|
|
|
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
|
|
|
|
|
|
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())
|
|
|
|
|
|
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']])
|
|
|
|
|
|
bigramas, trigramas = obtener_bigrams_trigrams(df_chat)
|
|
|
|
|
|
fig_bigram, fig_trigram = mostrar_grafica_bigrams_trigrams(bigramas, trigramas)
|
|
|
|
st.plotly_chart(fig_bigram)
|
|
st.plotly_chart(fig_trigram)
|
|
|
|
|
|
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.")
|
|
|