Update app.py
Browse filesse agrega lenmatizacion.
app.py
CHANGED
@@ -6,43 +6,48 @@ from transformers import pipeline
|
|
6 |
import nltk
|
7 |
from nltk.corpus import stopwords
|
8 |
from nltk.util import ngrams
|
|
|
|
|
9 |
from collections import Counter
|
10 |
import plotly.express as px
|
|
|
11 |
|
12 |
-
# Descargar
|
13 |
nltk.download('stopwords')
|
14 |
nltk.download('punkt')
|
15 |
-
nltk.download('
|
16 |
|
17 |
-
#
|
|
|
18 |
sentiment_analysis = pipeline('sentiment-analysis', model='dccuchile/bert-base-spanish-wwm-uncased')
|
19 |
|
|
|
|
|
|
|
|
|
20 |
# Funci贸n para procesar el archivo .txt de WhatsApp
|
21 |
def cargar_chat_txt(file):
|
22 |
content = file.getvalue().decode('utf-8')
|
23 |
lines = content.splitlines()
|
24 |
-
|
25 |
fechas = []
|
26 |
autores = []
|
27 |
mensajes = []
|
28 |
|
29 |
-
pattern = r"(\d{1,2}/\d{1,2}/\d{4}), (\d{1,2}:\d{2}\s?[ap]
|
30 |
-
|
31 |
for line in lines:
|
32 |
if "cifrados de extremo a extremo" in line:
|
33 |
continue
|
34 |
-
|
35 |
match = re.match(pattern, line.strip())
|
36 |
-
|
37 |
if match:
|
38 |
fecha = match.group(1)
|
39 |
hora = match.group(2)
|
40 |
autor = match.group(3).strip()
|
41 |
mensaje = match.group(4).strip()
|
42 |
|
43 |
-
hora = hora.replace("\u202f", "").strip()
|
44 |
-
hora = hora.replace(".", "")
|
45 |
-
|
46 |
fecha_hora_str = f"{fecha} {hora}"
|
47 |
|
48 |
try:
|
@@ -54,189 +59,127 @@ def cargar_chat_txt(file):
|
|
54 |
fechas.append(fecha_hora)
|
55 |
autores.append(autor)
|
56 |
mensajes.append(mensaje)
|
57 |
-
|
58 |
df = pd.DataFrame({
|
59 |
'FechaHora': fechas,
|
60 |
'Autor': autores,
|
61 |
'Mensaje': mensajes
|
62 |
})
|
63 |
-
|
64 |
-
if
|
65 |
df['FechaHora'] = pd.to_datetime(df['FechaHora'])
|
66 |
return df
|
67 |
else:
|
68 |
return None
|
69 |
|
70 |
-
#
|
71 |
-
def
|
72 |
stop_words = set(stopwords.words('spanish'))
|
73 |
-
|
74 |
-
|
75 |
-
return ' '.join(
|
76 |
|
77 |
-
#
|
78 |
def extraer_bigrams_trigrams(mensaje):
|
79 |
-
tokens =
|
80 |
bigrams = list(ngrams(tokens, 2))
|
81 |
trigrams = list(ngrams(tokens, 3))
|
82 |
return bigrams, trigrams
|
83 |
|
84 |
-
#
|
85 |
def urgencia_por_autor(autor):
|
86 |
autores_prioritarios = ["Jefe", "Hijo", "Mam谩", "Pap谩", "Esposa"]
|
87 |
-
|
88 |
if any(char in autor for char in ["鉂わ笍", "馃挅", "馃挊", "馃挐", "馃挄"]):
|
89 |
-
return 2
|
90 |
-
|
91 |
return 2 if autor in autores_prioritarios else 0
|
92 |
|
93 |
-
# Funci贸n para clasificar la urgencia basada en la hora
|
94 |
def urgencia_por_hora(hora):
|
95 |
hora = datetime.strptime(hora, "%H:%M")
|
96 |
if hora >= datetime.strptime("20:00", "%H:%M") or hora <= datetime.strptime("05:00", "%H:%M"):
|
97 |
return 1
|
98 |
return 0
|
99 |
|
100 |
-
# Funci贸n para clasificar la urgencia basada en el sentimiento
|
101 |
def urgencia_por_sentimiento(sentimiento):
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
return 2 # Urgencia moderada
|
106 |
-
elif sentimiento == 'LABEL_2': # Neutro
|
107 |
-
return 1 # Baja urgencia
|
108 |
-
elif sentimiento == 'LABEL_1': # Positivo
|
109 |
-
return 1 # Baja urgencia
|
110 |
-
elif sentimiento == 'LABEL_0': # Muy positivo
|
111 |
-
return 0 # Sin urgencia
|
112 |
-
return 0 # Si no coincide con ning煤n sentimiento, asignar baja urgencia
|
113 |
-
|
114 |
-
# Funci贸n para verificar si el mensaje contiene palabras clave de urgencia
|
115 |
def urgencia_por_palabras_clave(mensaje):
|
116 |
-
|
117 |
mensaje = mensaje.lower()
|
118 |
-
|
119 |
-
for palabra in palabras_clave:
|
120 |
-
if palabra in mensaje:
|
121 |
-
return 1 # Incrementa urgencia
|
122 |
-
return 0 # No hay urgencia en las palabras clave
|
123 |
|
124 |
-
# Funci贸n para verificar si el mensaje contiene palabras negativas
|
125 |
def urgencia_por_palabras_negativas(mensaje):
|
126 |
-
|
127 |
mensaje = mensaje.lower()
|
128 |
-
|
129 |
-
for palabra in palabras_negativas:
|
130 |
-
if palabra in mensaje:
|
131 |
-
return 2 # Incrementa urgencia por negatividad
|
132 |
-
return 0 # No hay urgencia en las palabras negativas
|
133 |
|
134 |
-
# Funci贸n para mapear las etiquetas del an谩lisis de sentimientos a etiquetas legibles
|
135 |
def mapear_sentimiento(sentimiento):
|
136 |
-
|
137 |
-
|
138 |
-
'LABEL_1': 'Positivo',
|
139 |
-
'LABEL_2': 'Neutro',
|
140 |
-
'LABEL_3': 'Negativo',
|
141 |
-
'LABEL_4': 'Muy Negativo'
|
142 |
-
}
|
143 |
-
return sentimiento_mapeado.get(sentimiento, 'Desconocido') # Si no encuentra la etiqueta, regresa "Desconocido"
|
144 |
-
|
145 |
|
146 |
-
# Funci贸n para calcular el nivel de urgencia
|
147 |
def calcular_urgencia(row):
|
148 |
-
mensaje_filtrado =
|
149 |
-
|
150 |
-
# Extraer bigramas y trigramas
|
151 |
bigrams, trigrams = extraer_bigrams_trigrams(mensaje_filtrado)
|
152 |
-
|
153 |
-
# An谩lisis de sentimiento
|
154 |
result = sentiment_analysis(mensaje_filtrado)
|
155 |
sentimiento = result[0]['label']
|
156 |
probabilidad = result[0]['score']
|
157 |
-
|
158 |
-
# Mapear el sentimiento a texto legible
|
159 |
sentimiento_legible = mapear_sentimiento(sentimiento)
|
160 |
-
|
161 |
-
# Si la probabilidad es baja, clasificar como "Neutro"
|
162 |
if probabilidad < 0.6:
|
163 |
sentimiento_legible = 'Neutro'
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
|
|
|
|
|
|
|
|
|
|
172 |
for bigram in bigrams + trigrams:
|
173 |
bigram_str = ' '.join(bigram)
|
174 |
-
|
175 |
-
urgencia += urgencia_por_sentimiento(
|
176 |
-
|
177 |
-
return min(5, urgencia), sentimiento_legible # Regresamos la urgencia y el sentimiento legible
|
178 |
|
|
|
179 |
|
180 |
-
#
|
181 |
def obtener_bigrams_trigrams(df):
|
182 |
-
bigramas = []
|
183 |
-
trigramas = []
|
184 |
-
|
185 |
for mensaje in df['Mensaje']:
|
186 |
-
|
187 |
-
bigramas.extend([' '.join(
|
188 |
-
trigramas.extend([' '.join(
|
189 |
-
|
190 |
return bigramas, trigramas
|
191 |
|
192 |
-
# Funci贸n para visualizar bigramas y trigramas en un gr谩fico
|
193 |
def mostrar_grafica_bigrams_trigrams(bigramas, trigramas):
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
# Bigramas
|
199 |
-
bigram_df = pd.DataFrame(bigram_count, columns=['Bigram', 'Frecuencia'])
|
200 |
-
fig_bigram = px.bar(bigram_df, x='Bigram', y='Frecuencia', title="Top 10 Bigramas m谩s comunes")
|
201 |
-
|
202 |
-
# Trigramas
|
203 |
-
trigram_df = pd.DataFrame(trigram_count, columns=['Trigram', 'Frecuencia'])
|
204 |
-
fig_trigram = px.bar(trigram_df, x='Trigram', y='Frecuencia', title="Top 10 Trigramas m谩s comunes")
|
205 |
-
|
206 |
-
return fig_bigram, fig_trigram
|
207 |
-
|
208 |
-
# Streamlit application code
|
209 |
-
st.title("An谩lisis de Chat de WhatsApp")
|
210 |
|
|
|
|
|
211 |
uploaded_file = st.file_uploader("Sube un archivo TXT de chat de WhatsApp", type=["txt"])
|
212 |
|
213 |
if uploaded_file is not None:
|
214 |
df_chat = cargar_chat_txt(uploaded_file)
|
215 |
-
|
216 |
if df_chat is not None and not df_chat.empty:
|
217 |
-
st.write("Primeros mensajes del chat:")
|
218 |
st.dataframe(df_chat.head())
|
219 |
-
|
220 |
-
# Calcular la urgencia y sentimiento
|
221 |
df_chat[['Urgencia', 'Sentimiento']] = df_chat.apply(calcular_urgencia, axis=1, result_type='expand')
|
222 |
-
|
223 |
-
st.write("Mensajes con su nivel de urgencia y sentimiento:")
|
224 |
st.dataframe(df_chat[['FechaHora', 'Autor', 'Mensaje', 'Urgencia', 'Sentimiento']])
|
225 |
|
226 |
-
# Extraer bigramas y trigramas
|
227 |
bigramas, trigramas = obtener_bigrams_trigrams(df_chat)
|
228 |
-
|
229 |
-
# Mostrar gr谩ficas
|
230 |
fig_bigram, fig_trigram = mostrar_grafica_bigrams_trigrams(bigramas, trigramas)
|
231 |
-
|
232 |
st.plotly_chart(fig_bigram)
|
233 |
st.plotly_chart(fig_trigram)
|
234 |
|
235 |
-
# Visualizaci贸n del nivel de urgencia
|
236 |
if st.button('Mostrar Gr谩fico de Urgencia'):
|
237 |
urgencia_count = df_chat['Urgencia'].value_counts().reset_index()
|
238 |
urgencia_count.columns = ['Urgencia', 'Cantidad']
|
239 |
-
fig = px.bar(urgencia_count, x='Urgencia', y='Cantidad', title="Distribuci贸n de
|
240 |
st.plotly_chart(fig)
|
241 |
else:
|
242 |
-
st.write("No se encontraron mensajes
|
|
|
6 |
import nltk
|
7 |
from nltk.corpus import stopwords
|
8 |
from nltk.util import ngrams
|
9 |
+
from nltk.stem import WordNetLemmatizer
|
10 |
+
from nltk.tokenize import word_tokenize
|
11 |
from collections import Counter
|
12 |
import plotly.express as px
|
13 |
+
import emoji
|
14 |
|
15 |
+
# Descargar recursos de nltk
|
16 |
nltk.download('stopwords')
|
17 |
nltk.download('punkt')
|
18 |
+
nltk.download('wordnet')
|
19 |
|
20 |
+
# Inicializar herramientas NLP
|
21 |
+
lemmatizer = WordNetLemmatizer()
|
22 |
sentiment_analysis = pipeline('sentiment-analysis', model='dccuchile/bert-base-spanish-wwm-uncased')
|
23 |
|
24 |
+
# Funci贸n para detectar emojis
|
25 |
+
def contiene_emojis(texto):
|
26 |
+
return any(char in emoji.EMOJI_DATA for char in texto)
|
27 |
+
|
28 |
# Funci贸n para procesar el archivo .txt de WhatsApp
|
29 |
def cargar_chat_txt(file):
|
30 |
content = file.getvalue().decode('utf-8')
|
31 |
lines = content.splitlines()
|
32 |
+
|
33 |
fechas = []
|
34 |
autores = []
|
35 |
mensajes = []
|
36 |
|
37 |
+
pattern = r"(\d{1,2}/\d{1,2}/\d{4}), (\d{1,2}:\d{2}\s?[ap]\.\s?[m]\.) - (.*?):(.*)"
|
38 |
+
|
39 |
for line in lines:
|
40 |
if "cifrados de extremo a extremo" in line:
|
41 |
continue
|
42 |
+
|
43 |
match = re.match(pattern, line.strip())
|
|
|
44 |
if match:
|
45 |
fecha = match.group(1)
|
46 |
hora = match.group(2)
|
47 |
autor = match.group(3).strip()
|
48 |
mensaje = match.group(4).strip()
|
49 |
|
50 |
+
hora = hora.replace("\u202f", "").strip().replace(".", "")
|
|
|
|
|
51 |
fecha_hora_str = f"{fecha} {hora}"
|
52 |
|
53 |
try:
|
|
|
59 |
fechas.append(fecha_hora)
|
60 |
autores.append(autor)
|
61 |
mensajes.append(mensaje)
|
62 |
+
|
63 |
df = pd.DataFrame({
|
64 |
'FechaHora': fechas,
|
65 |
'Autor': autores,
|
66 |
'Mensaje': mensajes
|
67 |
})
|
68 |
+
|
69 |
+
if not df.empty:
|
70 |
df['FechaHora'] = pd.to_datetime(df['FechaHora'])
|
71 |
return df
|
72 |
else:
|
73 |
return None
|
74 |
|
75 |
+
# Quitar stopwords y aplicar lematizaci贸n
|
76 |
+
def quitar_stopwords_lemmatizar(mensaje):
|
77 |
stop_words = set(stopwords.words('spanish'))
|
78 |
+
tokens = word_tokenize(mensaje.lower())
|
79 |
+
tokens_filtrados = [lemmatizer.lemmatize(word) for word in tokens if word not in stop_words]
|
80 |
+
return ' '.join(tokens_filtrados)
|
81 |
|
82 |
+
# Extraer bigramas y trigramas
|
83 |
def extraer_bigrams_trigrams(mensaje):
|
84 |
+
tokens = word_tokenize(mensaje.lower())
|
85 |
bigrams = list(ngrams(tokens, 2))
|
86 |
trigrams = list(ngrams(tokens, 3))
|
87 |
return bigrams, trigrams
|
88 |
|
89 |
+
# Funciones de urgencia (autor, hora, sentimiento, palabras clave, etc.)
|
90 |
def urgencia_por_autor(autor):
|
91 |
autores_prioritarios = ["Jefe", "Hijo", "Mam谩", "Pap谩", "Esposa"]
|
|
|
92 |
if any(char in autor for char in ["鉂わ笍", "馃挅", "馃挊", "馃挐", "馃挄"]):
|
93 |
+
return 2
|
|
|
94 |
return 2 if autor in autores_prioritarios else 0
|
95 |
|
|
|
96 |
def urgencia_por_hora(hora):
|
97 |
hora = datetime.strptime(hora, "%H:%M")
|
98 |
if hora >= datetime.strptime("20:00", "%H:%M") or hora <= datetime.strptime("05:00", "%H:%M"):
|
99 |
return 1
|
100 |
return 0
|
101 |
|
|
|
102 |
def urgencia_por_sentimiento(sentimiento):
|
103 |
+
etiquetas = {'LABEL_4': 3, 'LABEL_3': 2, 'LABEL_2': 1, 'LABEL_1': 1, 'LABEL_0': 0}
|
104 |
+
return etiquetas.get(sentimiento, 0)
|
105 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
def urgencia_por_palabras_clave(mensaje):
|
107 |
+
claves = ["urgente", "es urgente", "es para hoy", "necesito ayuda", "por favor", "con urgencia"]
|
108 |
mensaje = mensaje.lower()
|
109 |
+
return 1 if any(clave in mensaje for clave in claves) else 0
|
|
|
|
|
|
|
|
|
110 |
|
|
|
111 |
def urgencia_por_palabras_negativas(mensaje):
|
112 |
+
negativas = ["malo", "no me gusta", "odio", "peor", "terrible", "desastroso", "fatal"]
|
113 |
mensaje = mensaje.lower()
|
114 |
+
return 2 if any(p in mensaje for p in negativas) else 0
|
|
|
|
|
|
|
|
|
115 |
|
|
|
116 |
def mapear_sentimiento(sentimiento):
|
117 |
+
mapeo = {'LABEL_0': 'Muy Positivo', 'LABEL_1': 'Positivo', 'LABEL_2': 'Neutro', 'LABEL_3': 'Negativo', 'LABEL_4': 'Muy Negativo'}
|
118 |
+
return mapeo.get(sentimiento, 'Desconocido')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
|
|
|
120 |
def calcular_urgencia(row):
|
121 |
+
mensaje_filtrado = quitar_stopwords_lemmatizar(row['Mensaje'])
|
|
|
|
|
122 |
bigrams, trigrams = extraer_bigrams_trigrams(mensaje_filtrado)
|
|
|
|
|
123 |
result = sentiment_analysis(mensaje_filtrado)
|
124 |
sentimiento = result[0]['label']
|
125 |
probabilidad = result[0]['score']
|
|
|
|
|
126 |
sentimiento_legible = mapear_sentimiento(sentimiento)
|
|
|
|
|
127 |
if probabilidad < 0.6:
|
128 |
sentimiento_legible = 'Neutro'
|
129 |
+
|
130 |
+
urgencia = (
|
131 |
+
urgencia_por_autor(row['Autor']) +
|
132 |
+
urgencia_por_hora(row['FechaHora'].strftime('%H:%M')) +
|
133 |
+
urgencia_por_sentimiento(sentimiento) +
|
134 |
+
urgencia_por_palabras_clave(row['Mensaje']) +
|
135 |
+
urgencia_por_palabras_negativas(row['Mensaje'])
|
136 |
+
)
|
137 |
+
|
138 |
+
# A帽adir urgencia si contiene emojis emocionales
|
139 |
+
if contiene_emojis(row['Mensaje']):
|
140 |
+
urgencia += 1
|
141 |
+
|
142 |
for bigram in bigrams + trigrams:
|
143 |
bigram_str = ' '.join(bigram)
|
144 |
+
sentimiento_bigram = sentiment_analysis(bigram_str)[0]['label']
|
145 |
+
urgencia += urgencia_por_sentimiento(sentimiento_bigram)
|
|
|
|
|
146 |
|
147 |
+
return min(5, urgencia), sentimiento_legible
|
148 |
|
149 |
+
# Funciones para mostrar bigramas y trigramas
|
150 |
def obtener_bigrams_trigrams(df):
|
151 |
+
bigramas, trigramas = [], []
|
|
|
|
|
152 |
for mensaje in df['Mensaje']:
|
153 |
+
bigs, trigs = extraer_bigrams_trigrams(mensaje)
|
154 |
+
bigramas.extend([' '.join(b) for b in bigs])
|
155 |
+
trigramas.extend([' '.join(t) for t in trigs])
|
|
|
156 |
return bigramas, trigramas
|
157 |
|
|
|
158 |
def mostrar_grafica_bigrams_trigrams(bigramas, trigramas):
|
159 |
+
bigram_df = pd.DataFrame(Counter(bigramas).most_common(10), columns=['Bigram', 'Frecuencia'])
|
160 |
+
trigram_df = pd.DataFrame(Counter(trigramas).most_common(10), columns=['Trigram', 'Frecuencia'])
|
161 |
+
return px.bar(bigram_df, x='Bigram', y='Frecuencia', title="Top 10 Bigramas"), px.bar(trigram_df, x='Trigram', y='Frecuencia', title="Top 10 Trigramas")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
|
163 |
+
# App de Streamlit
|
164 |
+
st.title("An谩lisis de Chat de WhatsApp con Urgencia y Emojis")
|
165 |
uploaded_file = st.file_uploader("Sube un archivo TXT de chat de WhatsApp", type=["txt"])
|
166 |
|
167 |
if uploaded_file is not None:
|
168 |
df_chat = cargar_chat_txt(uploaded_file)
|
|
|
169 |
if df_chat is not None and not df_chat.empty:
|
|
|
170 |
st.dataframe(df_chat.head())
|
|
|
|
|
171 |
df_chat[['Urgencia', 'Sentimiento']] = df_chat.apply(calcular_urgencia, axis=1, result_type='expand')
|
|
|
|
|
172 |
st.dataframe(df_chat[['FechaHora', 'Autor', 'Mensaje', 'Urgencia', 'Sentimiento']])
|
173 |
|
|
|
174 |
bigramas, trigramas = obtener_bigrams_trigrams(df_chat)
|
|
|
|
|
175 |
fig_bigram, fig_trigram = mostrar_grafica_bigrams_trigrams(bigramas, trigramas)
|
|
|
176 |
st.plotly_chart(fig_bigram)
|
177 |
st.plotly_chart(fig_trigram)
|
178 |
|
|
|
179 |
if st.button('Mostrar Gr谩fico de Urgencia'):
|
180 |
urgencia_count = df_chat['Urgencia'].value_counts().reset_index()
|
181 |
urgencia_count.columns = ['Urgencia', 'Cantidad']
|
182 |
+
fig = px.bar(urgencia_count, x='Urgencia', y='Cantidad', title="Distribuci贸n de Urgencia")
|
183 |
st.plotly_chart(fig)
|
184 |
else:
|
185 |
+
st.write("No se encontraron mensajes v谩lidos.")
|