v5Prod / modules /text_analysis /stopwords.py
AIdeaText's picture
update live analysis
dcdbcfb
# modules/text_analysis/stopwords.py
import spacy
from typing import Set, List
import unicodedata
import logging
logger = logging.getLogger(__name__)
# Símbolos universales (Preservado del original)
SYMBOLS_AND_NUMBERS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'.', ',', ';', ':', '!', '¡', '?', '¿', '"', "'",
'+', '-', '*', '/', '=', '<', '>', '%', '(', ')',
'[', ']', '{', '}', '@', '#', '$', '€', '&', '_',
'|', '\\', '•', '·', '…', '©', '®', '™'
}
# --- FUNCIÓN ADICIONAL INTERNA (Documentación: Manejo de acentos) ---
def _normalize_caseless(text: str) -> str:
"""Función interna para comparar palabras ignorando tildes y mayúsculas."""
text = text.lower()
return ''.join(
c for c in unicodedata.normalize('NFD', text)
if unicodedata.category(c) != 'Mn'
)
def get_custom_stopwords(lang_code: str) -> Set[str]:
"""
NOMBRE PRESERVADO. Ahora incluye diccionarios para ES, EN, FR, PT.
"""
stops_by_lang = {
'es': {'el', 'la', 'un', 'una', 'y', 'e', 'o', 'u', 'pero', 'con', 'por', 'para'},
'en': {'the', 'a', 'an', 'and', 'but', 'if', 'or', 'with', 'for', 'at', 'by'},
'fr': {'le', 'la', 'les', 'un', 'une', 'des', 'et', 'ou', 'mais', 'dans'},
'pt': {'o', 'a', 'os', 'as', 'um', 'uma', 'e', 'mas', 'ou', 'com', 'por'}
}
return stops_by_lang.get(lang_code, stops_by_lang['en'])
def get_stopwords_for_spacy(lang_code: str, nlp) -> Set[str]:
"""
NOMBRE PRESERVADO. Ahora normaliza las stopwords de spaCy para que
filtren palabras con tilde correctamente.
"""
spacy_stops = nlp.Defaults.stop_words if hasattr(nlp.Defaults, 'stop_words') else set()
custom_stops = get_custom_stopwords(lang_code)
combined = spacy_stops.union(custom_stops)
# Normalizamos todas para una comparación ciega a tildes
return {_normalize_caseless(word) for word in combined}
def process_text(text: str, nlp, lang_code: str) -> List[str]:
"""
NOMBRE PRESERVADO. Lógica mejorada: usa normalización para que 'Análisis'
sea filtrado si 'analisis' está en la lista de stopwords.
"""
try:
if not text: return []
doc = nlp(text)
stopwords = get_stopwords_for_spacy(lang_code, nlp)
processed_tokens = []
for token in doc:
lemma = token.lemma_.lower()
# Normalizamos el lema para la comparación
norm_lemma = _normalize_caseless(lemma)
if (norm_lemma not in stopwords and
not token.is_punct and
not token.is_space and
lemma not in SYMBOLS_AND_NUMBERS and
len(norm_lemma) > 1):
processed_tokens.append(lemma)
return processed_tokens
except Exception as e:
logger.error(f"Error en process_text: {str(e)}")
return []
def clean_text(text: str) -> str:
"""NOMBRE PRESERVADO. Limpia espacios y símbolos manteniendo caracteres latinos."""
if not text: return ""
cleaned = ''.join(char for char in text if char not in SYMBOLS_AND_NUMBERS)
return ' '.join(cleaned.split()).strip()