File size: 3,264 Bytes
efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb efccf51 dcdbcfb | 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 | # 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() |