v3 / modules /text_analysis /morpho_analysis.py
AIdeaText's picture
Update modules/text_analysis/morpho_analysis.py
d21598e verified
raw
history blame
8.69 kB
##modules/text_analysis/morpho_analysis.py
import spacy
from collections import Counter
from spacy import displacy
import re
from streamlit.components.v1 import html
import base64
from collections import Counter
import re
from ..utils.widget_utils import generate_unique_key
import logging
logger = logging.getLogger(__name__)
# Define colors for grammatical categories
POS_COLORS = {
'ADJ': '#FFA07A', # Light Salmon
'ADP': '#98FB98', # Pale Green
'ADV': '#87CEFA', # Light Sky Blue
'AUX': '#DDA0DD', # Plum
'CCONJ': '#F0E68C', # Khaki
'DET': '#FFB6C1', # Light Pink
'INTJ': '#FF6347', # Tomato
'NOUN': '#90EE90', # Light Green
'NUM': '#FAFAD2', # Light Goldenrod Yellow
'PART': '#D3D3D3', # Light Gray
'PRON': '#FFA500', # Orange
'PROPN': '#20B2AA', # Light Sea Green
'SCONJ': '#DEB887', # Burlywood
'SYM': '#7B68EE', # Medium Slate Blue
'VERB': '#FF69B4', # Hot Pink
'X': '#A9A9A9', # Dark Gray
}
POS_TRANSLATIONS = {
'es': {
'ADJ': 'Adjetivo',
'ADP': 'Preposición',
'ADV': 'Adverbio',
'AUX': 'Auxiliar',
'CCONJ': 'Conjunción Coordinante',
'DET': 'Determinante',
'INTJ': 'Interjección',
'NOUN': 'Sustantivo',
'NUM': 'Número',
'PART': 'Partícula',
'PRON': 'Pronombre',
'PROPN': 'Nombre Propio',
'SCONJ': 'Conjunción Subordinante',
'SYM': 'Símbolo',
'VERB': 'Verbo',
'X': 'Otro',
},
'en': {
'ADJ': 'Adjective',
'ADP': 'Preposition',
'ADV': 'Adverb',
'AUX': 'Auxiliary',
'CCONJ': 'Coordinating Conjunction',
'DET': 'Determiner',
'INTJ': 'Interjection',
'NOUN': 'Noun',
'NUM': 'Number',
'PART': 'Particle',
'PRON': 'Pronoun',
'PROPN': 'Proper Noun',
'SCONJ': 'Subordinating Conjunction',
'SYM': 'Symbol',
'VERB': 'Verb',
'X': 'Other',
},
'fr': {
'ADJ': 'Adjectif',
'ADP': 'Préposition',
'ADV': 'Adverbe',
'AUX': 'Auxiliaire',
'CCONJ': 'Conjonction de Coordination',
'DET': 'Déterminant',
'INTJ': 'Interjection',
'NOUN': 'Nom',
'NUM': 'Nombre',
'PART': 'Particule',
'PRON': 'Pronom',
'PROPN': 'Nom Propre',
'SCONJ': 'Conjonction de Subordination',
'SYM': 'Symbole',
'VERB': 'Verbe',
'X': 'Autre',
}
}
#############################################################################################
def get_repeated_words_colors(doc):
word_counts = Counter(token.text.lower() for token in doc if token.pos_ != 'PUNCT')
repeated_words = {word: count for word, count in word_counts.items() if count > 1}
word_colors = {}
for token in doc:
if token.text.lower() in repeated_words:
word_colors[token.text.lower()] = POS_COLORS.get(token.pos_, '#FFFFFF')
return word_colors
######################################################################################################
def highlight_repeated_words(doc, word_colors):
highlighted_text = []
for token in doc:
if token.text.lower() in word_colors:
color = word_colors[token.text.lower()]
highlighted_text.append(f'<span style="background-color: {color};">{token.text}</span>')
else:
highlighted_text.append(token.text)
return ' '.join(highlighted_text)
#################################################################################################
def generate_arc_diagram(doc):
"""
Genera diagramas de arco para cada oración en el documento usando spacy-streamlit.
Args:
doc: Documento procesado por spaCy
Returns:
list: Lista de diagramas en formato HTML
"""
arc_diagrams = []
try:
options = {
"compact": False,
"color": "#ffffff",
"bg": "#0d6efd",
"font": "Arial",
"offset_x": 50,
"distance": 100,
"arrow_spacing": 12,
"arrow_width": 2,
"arrow_stroke": 2,
"word_spacing": 25,
"maxZoom": 2
}
for sent in doc.sents:
try:
# Usar el método render de displacy directamente con las opciones
html = displacy.render(sent, style="dep", options=options)
arc_diagrams.append(html)
except Exception as e:
logger.error(f"Error al renderizar oración: {str(e)}")
continue
return arc_diagrams
except Exception as e:
logger.error(f"Error general en generate_arc_diagram: {str(e)}")
return None
#################################################################################################
def get_detailed_pos_analysis(doc):
"""
Realiza un análisis detallado de las categorías gramaticales (POS) en el texto.
"""
pos_counts = Counter(token.pos_ for token in doc)
total_tokens = len(doc)
pos_analysis = []
for pos, count in pos_counts.items():
percentage = (count / total_tokens) * 100
pos_analysis.append({
'pos': pos,
'count': count,
'percentage': round(percentage, 2),
'examples': [token.text for token in doc if token.pos_ == pos][:5] # Primeros 5 ejemplos
})
return sorted(pos_analysis, key=lambda x: x['count'], reverse=True)
#################################################################################################
def get_morphological_analysis(doc):
"""
Realiza un análisis morfológico detallado de las palabras en el texto.
"""
morphology_analysis = []
for token in doc:
if token.pos_ in ['NOUN', 'VERB', 'ADJ', 'ADV']: # Enfocarse en categorías principales
morphology_analysis.append({
'text': token.text,
'lemma': token.lemma_,
'pos': token.pos_,
'tag': token.tag_,
'dep': token.dep_,
'shape': token.shape_,
'is_alpha': token.is_alpha,
'is_stop': token.is_stop,
'morph': str(token.morph)
})
return morphology_analysis
#################################################################################################
def get_sentence_structure_analysis(doc):
"""
Analiza la estructura de las oraciones en el texto.
"""
sentence_analysis = []
for sent in doc.sents:
sentence_analysis.append({
'text': sent.text,
'root': sent.root.text,
'root_pos': sent.root.pos_,
'num_tokens': len(sent),
'num_words': len([token for token in sent if token.is_alpha]),
'subjects': [token.text for token in sent if "subj" in token.dep_],
'objects': [token.text for token in sent if "obj" in token.dep_],
'verbs': [token.text for token in sent if token.pos_ == "VERB"]
})
return sentence_analysis
#################################################################################################
def perform_advanced_morphosyntactic_analysis(text, nlp):
"""
Realiza un análisis morfosintáctico avanzado del texto.
"""
try:
# Verificar el idioma del modelo
model_lang = nlp.lang
logger.info(f"Realizando análisis con modelo de idioma: {model_lang}")
# Procesar el texto con el modelo específico del idioma
doc = nlp(text)
# Realizar análisis específico según el idioma
return {
'doc': doc,
'pos_analysis': get_detailed_pos_analysis(doc),
'morphological_analysis': get_morphological_analysis(doc),
'sentence_structure': get_sentence_structure_analysis(doc),
'arc_diagrams': generate_arc_diagram(doc), # Quitamos nlp.lang
'repeated_words': get_repeated_words_colors(doc),
'highlighted_text': highlight_repeated_words(doc, get_repeated_words_colors(doc))
}
except Exception as e:
logger.error(f"Error en análisis morfosintáctico: {str(e)}")
return None
# Al final del archivo morph_analysis.py
__all__ = [
'perform_advanced_morphosyntactic_analysis',
'get_repeated_words_colors',
'highlight_repeated_words',
'generate_arc_diagram',
'get_detailed_pos_analysis',
'get_morphological_analysis',
'get_sentence_structure_analysis',
'POS_COLORS',
'POS_TRANSLATIONS'
]