Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import spacy | |
| from textblob import TextBlob | |
| from transformers import pipeline | |
| import torch | |
| from io import BytesIO | |
| import numpy as np | |
| # Cargar modelos | |
| print("⏳ Cargando modelos...") | |
| # 1. Cargar modelo de spaCy para español | |
| try: | |
| nlp = spacy.load("es_core_news_sm") | |
| print("✅ Modelo spaCy cargado correctamente") | |
| except OSError: | |
| print("❌ Modelo spaCy no encontrado. Usando análisis básico...") | |
| nlp = None | |
| # 2. Cargar múltiples modelos de sentimiento | |
| models = {} | |
| # Modelo multilingüe principal | |
| try: | |
| models['multilingual'] = pipeline( | |
| "text-classification", | |
| model="tabularisai/multilingual-sentiment-analysis" | |
| ) | |
| print("✅ Modelo multilingüe cargado") | |
| except Exception as e: | |
| print(f"❌ Error con modelo multilingüe: {e}") | |
| models['multilingual'] = None | |
| # Modelo BERT para sentimiento detallado | |
| try: | |
| models['bert'] = pipeline( | |
| "sentiment-analysis", | |
| model="nlptown/bert-base-multilingual-uncased-sentiment", | |
| tokenizer="nlptown/bert-base-multilingual-uncased-sentiment" | |
| ) | |
| print("✅ Modelo BERT cargado") | |
| except Exception as e: | |
| print(f"❌ Error con modelo BERT: {e}") | |
| models['bert'] = None | |
| # Diccionario léxico mejorado para español | |
| palabras_positivas = { | |
| 'bueno', 'excelente', 'fantástico', 'maravilloso', 'perfecto', 'genial', | |
| 'increíble', 'amo', 'encanta', 'feliz', 'contento', 'satisfecho', 'agradable', | |
| 'recomiendo', 'magnífico', 'extraordinario', 'asombroso', 'increíble', 'estupendo', | |
| 'fabril', 'perfectamente', 'óptimo', 'superior', 'mejor', 'inmejorable', 'ideal' | |
| } | |
| palabras_negativas = { | |
| 'malo', 'terrible', 'horrible', 'pésimo', 'odio', 'decepcionado', 'fatal', | |
| 'triste', 'enojado', 'frustrado', 'pobre', 'deficiente', 'desastroso', | |
| 'insatisfecho', 'decepcionante', 'horroroso', 'desastroso', 'pésimo', 'malísimo', | |
| 'inútil', 'defectuoso', 'deplorable', 'lamentable', 'desagradable', 'terrible' | |
| } | |
| def analizar_sentimiento_multimodelo(texto): | |
| """Combina múltiples métodos para análisis más preciso""" | |
| resultados = {} | |
| # Método 1: Modelo multilingüe principal | |
| if models['multilingual']: | |
| try: | |
| resultado = models['multilingual'](texto)[0] | |
| resultados['multilingual'] = { | |
| 'label': resultado['label'], | |
| 'score': resultado['score'], | |
| 'normalized_score': resultado['score'] if resultado['label'] == 'POSITIVE' else -resultado['score'] | |
| } | |
| except Exception as e: | |
| resultados['multilingual'] = {'error': str(e)} | |
| # Método 2: Modelo BERT con estrellas | |
| if models['bert']: | |
| try: | |
| resultado = models['bert'](texto)[0] | |
| # Convertir estrellas a puntuación -1 a 1 | |
| star_mapping = { | |
| '1 star': -1.0, '2 stars': -0.5, '3 stars': 0.0, | |
| '4 stars': 0.5, '5 stars': 1.0 | |
| } | |
| normalized_score = star_mapping.get(resultado['label'], 0.0) | |
| resultados['bert'] = { | |
| 'label': resultado['label'], | |
| 'score': resultado['score'], | |
| 'normalized_score': normalized_score | |
| } | |
| except Exception as e: | |
| resultados['bert'] = {'error': str(e)} | |
| # Método 3: Análisis léxico mejorado | |
| if nlp: | |
| try: | |
| doc = nlp(texto.lower()) | |
| palabras = [token.lemma_ for token in doc if token.is_alpha and len(token.text) > 2] | |
| positivas = sum(1 for palabra in palabras if palabra in palabras_positivas) | |
| negativas = sum(1 for palabra in palabras if palabra in palabras_negativas) | |
| total_relevantes = len(palabras) | |
| if total_relevantes > 0: | |
| polaridad = (positivas - negativas) / total_relevantes | |
| # Normalizar a -1 a 1 | |
| polaridad = max(-1.0, min(1.0, polaridad * 5)) | |
| else: | |
| polaridad = 0.0 | |
| resultados['lexico'] = { | |
| 'positivas': positivas, | |
| 'negativas': negativas, | |
| 'total_palabras': total_relevantes, | |
| 'normalized_score': polaridad | |
| } | |
| except Exception as e: | |
| resultados['lexico'] = {'error': str(e)} | |
| # Método 4: TextBlob (para inglés y español básico) | |
| try: | |
| blob = TextBlob(texto) | |
| polaridad = blob.sentiment.polarity | |
| resultados['textblob'] = { | |
| 'polarity': polaridad, | |
| 'subjectivity': blob.sentiment.subjectivity, | |
| 'normalized_score': polaridad | |
| } | |
| except Exception as e: | |
| resultados['textblob'] = {'error': str(e)} | |
| return resultados | |
| def determinar_sentimiento_final(resultados): | |
| """Combina resultados de todos los métodos""" | |
| scores = [] | |
| pesos = {'multilingual': 0.4, 'bert': 0.3, 'lexico': 0.2, 'textblob': 0.1} | |
| for metodo, peso in pesos.items(): | |
| if metodo in resultados and 'normalized_score' in resultados[metodo]: | |
| scores.append(resultados[metodo]['normalized_score'] * peso) | |
| if scores: | |
| score_final = sum(scores) | |
| if score_final > 0.2: | |
| return "😊 POSITIVO", score_final, "green" | |
| elif score_final < -0.2: | |
| return "😠 NEGATIVO", score_final, "red" | |
| else: | |
| return "😐 NEUTRO", score_final, "gray" | |
| else: | |
| return "❓ INDETERMINADO", 0.0, "orange" | |
| def analizar_lingüistica(texto): | |
| """Análisis lingüístico con spaCy""" | |
| if not nlp: | |
| return {"error": "Modelo spaCy no disponible"} | |
| doc = nlp(texto) | |
| analisis = { | |
| 'estadisticas': { | |
| 'total_tokens': len(doc), | |
| 'total_palabras': len([token for token in doc if token.is_alpha]), | |
| 'total_oraciones': len(list(doc.sents)), | |
| 'total_entidades': len(doc.ents) | |
| }, | |
| 'tokens': [], | |
| 'entidades': [], | |
| 'oraciones': [sent.text for sent in doc.sents] | |
| } | |
| # Análisis de tokens | |
| for token in doc[:20]: # Mostrar solo primeros 20 tokens | |
| analisis['tokens'].append({ | |
| 'texto': token.text, | |
| 'lemma': token.lemma_, | |
| 'POS': token.pos_, | |
| 'explicacion': spacy.explain(token.pos_) or '' | |
| }) | |
| # Entidades nombradas | |
| for ent in doc.ents[:10]: # Mostrar solo primeras 10 entidades | |
| analisis['entidades'].append({ | |
| 'texto': ent.text, | |
| 'tipo': ent.label_, | |
| 'explicacion': spacy.explain(ent.label_) or '' | |
| }) | |
| return analisis | |
| def analizar_texto_completo(texto): | |
| """Función principal que combina todo el análisis""" | |
| if not texto.strip(): | |
| return "❌ Por favor ingresa un texto para analizar", "", "" | |
| # Análisis de sentimiento | |
| resultados = analizar_sentimiento_multimodelo(texto) | |
| sentimiento_final, score_final, color = determinar_sentimiento_final(resultados) | |
| # Análisis lingüístico | |
| analisis_ling = analizar_lingüistica(texto) | |
| # Crear resumen de resultados | |
| resumen_html = f""" | |
| <div style='background-color: {color}20; padding: 20px; border-radius: 10px; border-left: 5px solid {color};'> | |
| <h2 style='margin: 0; color: {color};'>{sentimiento_final}</h2> | |
| <p style='margin: 5px 0;'><strong>Puntuación final:</strong> {score_final:.3f}</p> | |
| <p style='margin: 5px 0;'><strong>Longitud del texto:</strong> {len(texto)} caracteres</p> | |
| </div> | |
| """ | |
| # Detalles por método | |
| detalles_html = "<h3>📊 Resultados por Método:</h3>" | |
| for metodo, resultado in resultados.items(): | |
| detalles_html += f"<div style='margin-bottom: 15px;'>" | |
| detalles_html += f"<strong>{metodo.upper()}:</strong><br>" | |
| if 'error' in resultado: | |
| detalles_html += f"<span style='color: red;'>Error: {resultado['error']}</span>" | |
| else: | |
| if 'label' in resultado: | |
| detalles_html += f"Etiqueta: {resultado['label']}<br>" | |
| if 'score' in resultado: | |
| detalles_html += f"Confianza: {resultado['score']:.3f}<br>" | |
| if 'normalized_score' in resultado: | |
| detalles_html += f"Puntuación: {resultado['normalized_score']:.3f}<br>" | |
| if 'positivas' in resultado: | |
| detalles_html += f"Palabras positivas: {resultado['positivas']}<br>" | |
| detalles_html += f"Palabras negativas: {resultado['negativas']}<br>" | |
| detalles_html += "</div>" | |
| # Análisis lingüístico | |
| ling_html = "<h3>📝 Análisis Lingüístico:</h3>" | |
| if 'error' in analisis_ling: | |
| ling_html += f"<p style='color: red;'>{analisis_ling['error']}</p>" | |
| else: | |
| stats = analisis_ling['estadisticas'] | |
| ling_html += f""" | |
| <p><strong>Estadísticas:</strong></p> | |
| <ul> | |
| <li>Total de tokens: {stats['total_tokens']}</li> | |
| <li>Palabras: {stats['total_palabras']}</li> | |
| <li>Oraciones: {stats['total_oraciones']}</li> | |
| <li>Entidades: {stats['total_entidades']}</li> | |
| </ul> | |
| """ | |
| if analisis_ling['entidades']: | |
| ling_html += "<p><strong>Entidades detectadas:</strong></p>" | |
| for ent in analisis_ling['entidades']: | |
| ling_html += f"• {ent['texto']} ({ent['tipo']})<br>" | |
| return resumen_html, detalles_html, ling_html | |
| def analizar_archivo_excel(archivo): | |
| """Analizar archivo Excel con textos""" | |
| try: | |
| df = pd.read_excel(archivo) | |
| # Buscar columnas con texto | |
| columnas_texto = [] | |
| for col in df.columns: | |
| if df[col].dtype == 'object': | |
| # Verificar si contiene texto | |
| muestras = df[col].dropna()[:5] | |
| if any(isinstance(x, str) and len(str(x).strip()) > 10 for x in muestras): | |
| columnas_texto.append(col) | |
| if not columnas_texto: | |
| return pd.DataFrame({"Resultado": ["❌ No se encontraron columnas con texto suficiente para analizar"]}) | |
| resultados = [] | |
| for col in columnas_texto[:2]: # Analizar máximo 2 columnas | |
| for idx, texto in enumerate(df[col].dropna()[:50]): # Máximo 50 filas por columna | |
| if isinstance(texto, str) and len(texto.strip()) > 5: | |
| resultado_sentimiento = analizar_sentimiento_multimodelo(texto) | |
| sentimiento, score, _ = determinar_sentimiento_final(resultado_sentimiento) | |
| resultados.append({ | |
| 'Columna': col, | |
| 'Fila': idx + 1, | |
| 'Texto': texto[:100] + '...' if len(texto) > 100 else texto, | |
| 'Sentimiento': sentimiento, | |
| 'Puntuación': f"{score:.3f}", | |
| 'Longitud': len(texto) | |
| }) | |
| if not resultados: | |
| return pd.DataFrame({"Resultado": ["❌ No se pudieron analizar los textos del archivo"]}) | |
| df_resultados = pd.DataFrame(resultados) | |
| return df_resultados | |
| except Exception as e: | |
| return pd.DataFrame({"Error": [f"❌ Error al procesar el archivo: {str(e)}"]}) | |
| # Interfaz Gradio simplificada | |
| with gr.Blocks(theme="soft", title="Análisis Completo de Texto") as demo: | |
| gr.Markdown(""" | |
| # 🎯 Análisis Completo de Texto - Multimodelo | |
| **Combina análisis lingüístico + múltiples métodos de sentimiento para máxima precisión** | |
| """) | |
| with gr.Tab("📝 Análisis de Texto Individual"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| texto_input = gr.Textbox( | |
| label="Ingresa tu texto", | |
| placeholder="Escribe aquí tu texto en español, inglés, francés o portugués...", | |
| lines=5 | |
| ) | |
| analizar_btn = gr.Button("🔍 Analizar Texto", variant="primary") | |
| gr.Markdown("### Ejemplos rápidos:") | |
| ejemplos = gr.Examples( | |
| examples=[ | |
| ["Me encanta este producto! Es excelente y superó mis expectativas completamente."], | |
| ["No estoy para nada satisfecho, la calidad es pésima y el servicio terrible."], | |
| ["El producto cumple su función básica, pero podría mejorar en algunos aspectos."], | |
| ["I love this amazing product! It works perfectly and the quality is outstanding."], | |
| ["C'est un produit fantastique, je le recommande vivement à tous mes amis."], | |
| ["Este serviço é muito bom, atendimento excelente e qualidade superior."] | |
| ], | |
| inputs=texto_input | |
| ) | |
| with gr.Column(): | |
| resultado_resumen = gr.HTML(label="🎯 Resultado Principal") | |
| resultado_detalles = gr.HTML(label="📊 Detalles por Método") | |
| resultado_linguistica = gr.HTML(label="📝 Análisis Lingüístico") | |
| with gr.Tab("📊 Análisis de Archivos Excel"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| archivo_input = gr.File( | |
| label="Sube tu archivo Excel", | |
| file_types=[".xlsx", ".xls"] | |
| ) | |
| analizar_excel_btn = gr.Button("📈 Analizar Archivo", variant="primary") | |
| gr.Markdown(""" | |
| **Formato esperado:** | |
| - Archivo Excel (.xlsx o .xls) | |
| - Columnas con texto (reseñas, comentarios, etc.) | |
| - Mínimo 5-10 palabras por texto para mejor análisis | |
| """) | |
| with gr.Column(): | |
| resultado_excel = gr.Dataframe(label="Resultados del Análisis") | |
| with gr.Tab("ℹ️ Información del Sistema"): | |
| gr.Markdown(""" | |
| ## 🔧 Métodos de Análisis Utilizados | |
| ### 🤖 Modelos de Machine Learning | |
| - **Multilingual Sentiment**: Modelo especializado en múltiples idiomas | |
| - **BERT Multilingual**: Modelo transformer con clasificación por estrellas (1-5) | |
| ### 📚 Análisis Léxico | |
| - Diccionario de palabras positivas/negativas en español | |
| - Lematización y análisis morfológico | |
| ### 📊 TextBlob | |
| - Análisis tradicional de sentimientos | |
| - Compatible con múltiples idiomas | |
| ### 📝 Análisis Lingüístico (spaCy) | |
| - Tokenización y POS tagging | |
| - Reconocimiento de entidades | |
| - Análisis de dependencias | |
| ## 🎯 Combinación Inteligente | |
| Los resultados se combinan usando pesos ponderados para mayor precisión | |
| """) | |
| # Conectar eventos | |
| analizar_btn.click( | |
| fn=analizar_texto_completo, | |
| inputs=texto_input, | |
| outputs=[resultado_resumen, resultado_detalles, resultado_linguistica] | |
| ) | |
| texto_input.submit( | |
| fn=analizar_texto_completo, | |
| inputs=texto_input, | |
| outputs=[resultado_resumen, resultado_detalles, resultado_linguistica] | |
| ) | |
| analizar_excel_btn.click( | |
| fn=analizar_archivo_excel, | |
| inputs=archivo_input, | |
| outputs=resultado_excel | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |