AIdeaText commited on
Commit
724e476
verified
1 Parent(s): 3f98e79

Update modules/text_analysis/semantic_analysis.py

Browse files
modules/text_analysis/semantic_analysis.py CHANGED
@@ -1,251 +1,263 @@
1
- #semantic_analysis.py
2
- import streamlit as st
3
- import spacy
4
- import networkx as nx
5
- import matplotlib.pyplot as plt
6
- import io
7
- import base64
8
- from collections import Counter, defaultdict
9
- from sklearn.feature_extraction.text import TfidfVectorizer
10
- from sklearn.metrics.pairwise import cosine_similarity
11
- import logging
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
-
16
- # Define colors for grammatical categories
17
- POS_COLORS = {
18
- 'ADJ': '#FFA07A', 'ADP': '#98FB98', 'ADV': '#87CEFA', 'AUX': '#DDA0DD',
19
- 'CCONJ': '#F0E68C', 'DET': '#FFB6C1', 'INTJ': '#FF6347', 'NOUN': '#90EE90',
20
- 'NUM': '#FAFAD2', 'PART': '#D3D3D3', 'PRON': '#FFA500', 'PROPN': '#20B2AA',
21
- 'SCONJ': '#DEB887', 'SYM': '#7B68EE', 'VERB': '#FF69B4', 'X': '#A9A9A9',
22
- }
23
-
24
- POS_TRANSLATIONS = {
25
- 'es': {
26
- 'ADJ': 'Adjetivo', 'ADP': 'Preposici贸n', 'ADV': 'Adverbio', 'AUX': 'Auxiliar',
27
- 'CCONJ': 'Conjunci贸n Coordinante', 'DET': 'Determinante', 'INTJ': 'Interjecci贸n',
28
- 'NOUN': 'Sustantivo', 'NUM': 'N煤mero', 'PART': 'Part铆cula', 'PRON': 'Pronombre',
29
- 'PROPN': 'Nombre Propio', 'SCONJ': 'Conjunci贸n Subordinante', 'SYM': 'S铆mbolo',
30
- 'VERB': 'Verbo', 'X': 'Otro',
31
- },
32
- 'en': {
33
- 'ADJ': 'Adjective', 'ADP': 'Preposition', 'ADV': 'Adverb', 'AUX': 'Auxiliary',
34
- 'CCONJ': 'Coordinating Conjunction', 'DET': 'Determiner', 'INTJ': 'Interjection',
35
- 'NOUN': 'Noun', 'NUM': 'Number', 'PART': 'Particle', 'PRON': 'Pronoun',
36
- 'PROPN': 'Proper Noun', 'SCONJ': 'Subordinating Conjunction', 'SYM': 'Symbol',
37
- 'VERB': 'Verb', 'X': 'Other',
38
- },
39
- 'fr': {
40
- 'ADJ': 'Adjectif', 'ADP': 'Pr茅position', 'ADV': 'Adverbe', 'AUX': 'Auxiliaire',
41
- 'CCONJ': 'Conjonction de Coordination', 'DET': 'D茅terminant', 'INTJ': 'Interjection',
42
- 'NOUN': 'Nom', 'NUM': 'Nombre', 'PART': 'Particule', 'PRON': 'Pronom',
43
- 'PROPN': 'Nom Propre', 'SCONJ': 'Conjonction de Subordination', 'SYM': 'Symbole',
44
- 'VERB': 'Verbe', 'X': 'Autre',
45
- }
46
- }
47
-
48
- ENTITY_LABELS = {
49
- 'es': {
50
- "Personas": "lightblue",
51
- "Lugares": "lightcoral",
52
- "Inventos": "lightgreen",
53
- "Fechas": "lightyellow",
54
- "Conceptos": "lightpink"
55
- },
56
- 'en': {
57
- "People": "lightblue",
58
- "Places": "lightcoral",
59
- "Inventions": "lightgreen",
60
- "Dates": "lightyellow",
61
- "Concepts": "lightpink"
62
- },
63
- 'fr': {
64
- "Personnes": "lightblue",
65
- "Lieux": "lightcoral",
66
- "Inventions": "lightgreen",
67
- "Dates": "lightyellow",
68
- "Concepts": "lightpink"
69
- }
70
- }
71
-
72
- ##############################################################################################################
73
- def perform_semantic_analysis(text, nlp, lang_code):
74
- logger.info(f"Starting semantic analysis for language: {lang_code}")
75
- try:
76
- doc = nlp(text)
77
- key_concepts = identify_key_concepts(doc)
78
- concept_graph = create_concept_graph(doc, key_concepts)
79
- concept_graph_fig = visualize_concept_graph(concept_graph, lang_code)
80
- entities = extract_entities(doc, lang_code)
81
- entity_graph = create_entity_graph(entities)
82
- entity_graph_fig = visualize_entity_graph(entity_graph, lang_code)
83
-
84
- # Convertir figuras a bytes
85
- concept_graph_bytes = fig_to_bytes(concept_graph_fig)
86
- entity_graph_bytes = fig_to_bytes(entity_graph_fig)
87
-
88
- logger.info("Semantic analysis completed successfully")
89
- return {
90
- 'key_concepts': key_concepts,
91
- 'concept_graph': concept_graph_bytes,
92
- 'entities': entities,
93
- 'entity_graph': entity_graph_bytes
94
- }
95
- except Exception as e:
96
- logger.error(f"Error in perform_semantic_analysis: {str(e)}")
97
- raise
98
-
99
-
100
- def fig_to_bytes(fig):
101
- buf = io.BytesIO()
102
- fig.savefig(buf, format='png')
103
- buf.seek(0)
104
- return buf.getvalue()
105
-
106
-
107
- def fig_to_html(fig):
108
- buf = io.BytesIO()
109
- fig.savefig(buf, format='png')
110
- buf.seek(0)
111
- img_str = base64.b64encode(buf.getvalue()).decode()
112
- return f'<img src="data:image/png;base64,{img_str}" />'
113
-
114
-
115
-
116
- def identify_key_concepts(doc):
117
- logger.info("Identifying key concepts")
118
- word_freq = Counter([token.lemma_.lower() for token in doc if token.pos_ in ['NOUN', 'VERB'] and not token.is_stop])
119
- key_concepts = word_freq.most_common(10)
120
- return [(concept, float(freq)) for concept, freq in key_concepts]
121
-
122
-
123
- def create_concept_graph(doc, key_concepts):
124
- G = nx.Graph()
125
- for concept, freq in key_concepts:
126
- G.add_node(concept, weight=freq)
127
- for sent in doc.sents:
128
- sent_concepts = [token.lemma_.lower() for token in sent if token.lemma_.lower() in dict(key_concepts)]
129
- for i, concept1 in enumerate(sent_concepts):
130
- for concept2 in sent_concepts[i+1:]:
131
- if G.has_edge(concept1, concept2):
132
- G[concept1][concept2]['weight'] += 1
133
- else:
134
- G.add_edge(concept1, concept2, weight=1)
135
- return G
136
-
137
- def visualize_concept_graph(G, lang_code):
138
- fig, ax = plt.subplots(figsize=(12, 8))
139
- pos = nx.spring_layout(G, k=0.5, iterations=50)
140
- node_sizes = [G.nodes[node]['weight'] * 100 for node in G.nodes()]
141
- nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='lightblue', alpha=0.8, ax=ax)
142
- nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold", ax=ax)
143
- edge_weights = [G[u][v]['weight'] for u, v in G.edges()]
144
- nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.5, ax=ax)
145
- title = {
146
- 'es': "Relaciones entre Conceptos Clave",
147
- 'en': "Key Concept Relations",
148
- 'fr': "Relations entre Concepts Cl茅s"
149
- }
150
- ax.set_title(title[lang_code], fontsize=16)
151
- ax.axis('off')
152
- plt.tight_layout()
153
- return fig
154
-
155
- def create_entity_graph(entities):
156
- G = nx.Graph()
157
- for entity_type, entity_list in entities.items():
158
- for entity in entity_list:
159
- G.add_node(entity, type=entity_type)
160
- for i, entity1 in enumerate(entity_list):
161
- for entity2 in entity_list[i+1:]:
162
- G.add_edge(entity1, entity2)
163
- return G
164
-
165
- def visualize_entity_graph(G, lang_code):
166
- fig, ax = plt.subplots(figsize=(12, 8))
167
- pos = nx.spring_layout(G)
168
- for entity_type, color in ENTITY_LABELS[lang_code].items():
169
- node_list = [node for node, data in G.nodes(data=True) if data['type'] == entity_type]
170
- nx.draw_networkx_nodes(G, pos, nodelist=node_list, node_color=color, node_size=500, alpha=0.8, ax=ax)
171
- nx.draw_networkx_edges(G, pos, width=1, alpha=0.5, ax=ax)
172
- nx.draw_networkx_labels(G, pos, font_size=8, font_weight="bold", ax=ax)
173
- ax.set_title(f"Relaciones entre Entidades ({lang_code})", fontsize=16)
174
- ax.axis('off')
175
- plt.tight_layout()
176
- return fig
177
-
178
-
179
- #################################################################################
180
- def create_topic_graph(topics, doc):
181
- G = nx.Graph()
182
- for topic in topics:
183
- G.add_node(topic, weight=doc.text.count(topic))
184
- for i, topic1 in enumerate(topics):
185
- for topic2 in topics[i+1:]:
186
- weight = sum(1 for sent in doc.sents if topic1 in sent.text and topic2 in sent.text)
187
- if weight > 0:
188
- G.add_edge(topic1, topic2, weight=weight)
189
- return G
190
-
191
- def visualize_topic_graph(G, lang_code):
192
- fig, ax = plt.subplots(figsize=(12, 8))
193
- pos = nx.spring_layout(G)
194
- node_sizes = [G.nodes[node]['weight'] * 100 for node in G.nodes()]
195
- nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='lightgreen', alpha=0.8, ax=ax)
196
- nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold", ax=ax)
197
- edge_weights = [G[u][v]['weight'] for u, v in G.edges()]
198
- nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.5, ax=ax)
199
- ax.set_title(f"Relaciones entre Temas ({lang_code})", fontsize=16)
200
- ax.axis('off')
201
- plt.tight_layout()
202
- return fig
203
-
204
- ###########################################################################################
205
- def generate_summary(doc, lang_code):
206
- sentences = list(doc.sents)
207
- summary = sentences[:3] # Toma las primeras 3 oraciones como resumen
208
- return " ".join([sent.text for sent in summary])
209
-
210
- def extract_entities(doc, lang_code):
211
- entities = defaultdict(list)
212
- for ent in doc.ents:
213
- if ent.label_ in ENTITY_LABELS[lang_code]:
214
- entities[ent.label_].append(ent.text)
215
- return dict(entities)
216
-
217
- def analyze_sentiment(doc, lang_code):
218
- positive_words = sum(1 for token in doc if token.sentiment > 0)
219
- negative_words = sum(1 for token in doc if token.sentiment < 0)
220
- total_words = len(doc)
221
- if positive_words > negative_words:
222
- return "Positivo"
223
- elif negative_words > positive_words:
224
- return "Negativo"
225
- else:
226
- return "Neutral"
227
-
228
- def extract_topics(doc, lang_code):
229
- vectorizer = TfidfVectorizer(stop_words='english', max_features=5)
230
- tfidf_matrix = vectorizer.fit_transform([doc.text])
231
- feature_names = vectorizer.get_feature_names_out()
232
- return list(feature_names)
233
-
234
- # Aseg煤rate de que todas las funciones necesarias est茅n exportadas
235
- __all__ = [
236
- 'perform_semantic_analysis',
237
- 'identify_key_concepts',
238
- 'create_concept_graph',
239
- 'visualize_concept_graph',
240
- 'create_entity_graph',
241
- 'visualize_entity_graph',
242
- 'generate_summary',
243
- 'extract_entities',
244
- 'analyze_sentiment',
245
- 'create_topic_graph',
246
- 'visualize_topic_graph',
247
- 'extract_topics',
248
- 'ENTITY_LABELS',
249
- 'POS_COLORS',
250
- 'POS_TRANSLATIONS'
 
 
 
 
 
 
 
 
 
 
 
 
251
  ]
 
1
+ # modules/text_analysis/semantic_analysis.py
2
+ # [Mantener todas las importaciones y constantes existentes...]
3
+
4
+ import streamlit as st
5
+ import spacy
6
+ import networkx as nx
7
+ import matplotlib.pyplot as plt
8
+ import io
9
+ import base64
10
+ from collections import Counter, defaultdict
11
+ from sklearn.feature_extraction.text import TfidfVectorizer
12
+ from sklearn.metrics.pairwise import cosine_similarity
13
+ import logging
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ # Define colors for grammatical categories
19
+ POS_COLORS = {
20
+ 'ADJ': '#FFA07A', 'ADP': '#98FB98', 'ADV': '#87CEFA', 'AUX': '#DDA0DD',
21
+ 'CCONJ': '#F0E68C', 'DET': '#FFB6C1', 'INTJ': '#FF6347', 'NOUN': '#90EE90',
22
+ 'NUM': '#FAFAD2', 'PART': '#D3D3D3', 'PRON': '#FFA500', 'PROPN': '#20B2AA',
23
+ 'SCONJ': '#DEB887', 'SYM': '#7B68EE', 'VERB': '#FF69B4', 'X': '#A9A9A9',
24
+ }
25
+
26
+ POS_TRANSLATIONS = {
27
+ 'es': {
28
+ 'ADJ': 'Adjetivo', 'ADP': 'Preposici贸n', 'ADV': 'Adverbio', 'AUX': 'Auxiliar',
29
+ 'CCONJ': 'Conjunci贸n Coordinante', 'DET': 'Determinante', 'INTJ': 'Interjecci贸n',
30
+ 'NOUN': 'Sustantivo', 'NUM': 'N煤mero', 'PART': 'Part铆cula', 'PRON': 'Pronombre',
31
+ 'PROPN': 'Nombre Propio', 'SCONJ': 'Conjunci贸n Subordinante', 'SYM': 'S铆mbolo',
32
+ 'VERB': 'Verbo', 'X': 'Otro',
33
+ },
34
+ 'en': {
35
+ 'ADJ': 'Adjective', 'ADP': 'Preposition', 'ADV': 'Adverb', 'AUX': 'Auxiliary',
36
+ 'CCONJ': 'Coordinating Conjunction', 'DET': 'Determiner', 'INTJ': 'Interjection',
37
+ 'NOUN': 'Noun', 'NUM': 'Number', 'PART': 'Particle', 'PRON': 'Pronoun',
38
+ 'PROPN': 'Proper Noun', 'SCONJ': 'Subordinating Conjunction', 'SYM': 'Symbol',
39
+ 'VERB': 'Verb', 'X': 'Other',
40
+ },
41
+ 'fr': {
42
+ 'ADJ': 'Adjectif', 'ADP': 'Pr茅position', 'ADV': 'Adverbe', 'AUX': 'Auxiliaire',
43
+ 'CCONJ': 'Conjonction de Coordination', 'DET': 'D茅terminant', 'INTJ': 'Interjection',
44
+ 'NOUN': 'Nom', 'NUM': 'Nombre', 'PART': 'Particule', 'PRON': 'Pronom',
45
+ 'PROPN': 'Nom Propre', 'SCONJ': 'Conjonction de Subordination', 'SYM': 'Symbole',
46
+ 'VERB': 'Verbe', 'X': 'Autre',
47
+ }
48
+ }
49
+
50
+ ENTITY_LABELS = {
51
+ 'es': {
52
+ "Personas": "lightblue",
53
+ "Lugares": "lightcoral",
54
+ "Inventos": "lightgreen",
55
+ "Fechas": "lightyellow",
56
+ "Conceptos": "lightpink"
57
+ },
58
+ 'en': {
59
+ "People": "lightblue",
60
+ "Places": "lightcoral",
61
+ "Inventions": "lightgreen",
62
+ "Dates": "lightyellow",
63
+ "Concepts": "lightpink"
64
+ },
65
+ 'fr': {
66
+ "Personnes": "lightblue",
67
+ "Lieux": "lightcoral",
68
+ "Inventions": "lightgreen",
69
+ "Dates": "lightyellow",
70
+ "Concepts": "lightpink"
71
+ }
72
+ }
73
+
74
+ ##############################################################################################################
75
+ def perform_semantic_analysis(text, nlp, lang_code):
76
+ """
77
+ Realiza el an谩lisis sem谩ntico completo del texto.
78
+ Args:
79
+ text: Texto a analizar
80
+ nlp: Modelo de spaCy
81
+ lang_code: C贸digo del idioma
82
+ Returns:
83
+ dict: Resultados del an谩lisis
84
+ """
85
+
86
+ logger.info(f"Starting semantic analysis for language: {lang_code}")
87
+ try:
88
+ doc = nlp(text)
89
+ key_concepts = identify_key_concepts(doc)
90
+ concept_graph = create_concept_graph(doc, key_concepts)
91
+ concept_graph_fig = visualize_concept_graph(concept_graph, lang_code)
92
+ entities = extract_entities(doc, lang_code)
93
+ entity_graph = create_entity_graph(entities)
94
+ entity_graph_fig = visualize_entity_graph(entity_graph, lang_code)
95
+
96
+ # Convertir figuras a bytes
97
+ concept_graph_bytes = fig_to_bytes(concept_graph_fig)
98
+ entity_graph_bytes = fig_to_bytes(entity_graph_fig)
99
+
100
+ logger.info("Semantic analysis completed successfully")
101
+ return {
102
+ 'key_concepts': key_concepts,
103
+ 'concept_graph': concept_graph_bytes,
104
+ 'entities': entities,
105
+ 'entity_graph': entity_graph_bytes
106
+ }
107
+ except Exception as e:
108
+ logger.error(f"Error in perform_semantic_analysis: {str(e)}")
109
+ raise
110
+
111
+
112
+ def fig_to_bytes(fig):
113
+ buf = io.BytesIO()
114
+ fig.savefig(buf, format='png')
115
+ buf.seek(0)
116
+ return buf.getvalue()
117
+
118
+
119
+ def fig_to_html(fig):
120
+ buf = io.BytesIO()
121
+ fig.savefig(buf, format='png')
122
+ buf.seek(0)
123
+ img_str = base64.b64encode(buf.getvalue()).decode()
124
+ return f'<img src="data:image/png;base64,{img_str}" />'
125
+
126
+
127
+
128
+ def identify_key_concepts(doc):
129
+ logger.info("Identifying key concepts")
130
+ word_freq = Counter([token.lemma_.lower() for token in doc if token.pos_ in ['NOUN', 'VERB'] and not token.is_stop])
131
+ key_concepts = word_freq.most_common(10)
132
+ return [(concept, float(freq)) for concept, freq in key_concepts]
133
+
134
+
135
+ def create_concept_graph(doc, key_concepts):
136
+ G = nx.Graph()
137
+ for concept, freq in key_concepts:
138
+ G.add_node(concept, weight=freq)
139
+ for sent in doc.sents:
140
+ sent_concepts = [token.lemma_.lower() for token in sent if token.lemma_.lower() in dict(key_concepts)]
141
+ for i, concept1 in enumerate(sent_concepts):
142
+ for concept2 in sent_concepts[i+1:]:
143
+ if G.has_edge(concept1, concept2):
144
+ G[concept1][concept2]['weight'] += 1
145
+ else:
146
+ G.add_edge(concept1, concept2, weight=1)
147
+ return G
148
+
149
+ def visualize_concept_graph(G, lang_code):
150
+ fig, ax = plt.subplots(figsize=(12, 8))
151
+ pos = nx.spring_layout(G, k=0.5, iterations=50)
152
+ node_sizes = [G.nodes[node]['weight'] * 100 for node in G.nodes()]
153
+ nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='lightblue', alpha=0.8, ax=ax)
154
+ nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold", ax=ax)
155
+ edge_weights = [G[u][v]['weight'] for u, v in G.edges()]
156
+ nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.5, ax=ax)
157
+ title = {
158
+ 'es': "Relaciones entre Conceptos Clave",
159
+ 'en': "Key Concept Relations",
160
+ 'fr': "Relations entre Concepts Cl茅s"
161
+ }
162
+ ax.set_title(title[lang_code], fontsize=16)
163
+ ax.axis('off')
164
+ plt.tight_layout()
165
+ return fig
166
+
167
+ def create_entity_graph(entities):
168
+ G = nx.Graph()
169
+ for entity_type, entity_list in entities.items():
170
+ for entity in entity_list:
171
+ G.add_node(entity, type=entity_type)
172
+ for i, entity1 in enumerate(entity_list):
173
+ for entity2 in entity_list[i+1:]:
174
+ G.add_edge(entity1, entity2)
175
+ return G
176
+
177
+ def visualize_entity_graph(G, lang_code):
178
+ fig, ax = plt.subplots(figsize=(12, 8))
179
+ pos = nx.spring_layout(G)
180
+ for entity_type, color in ENTITY_LABELS[lang_code].items():
181
+ node_list = [node for node, data in G.nodes(data=True) if data['type'] == entity_type]
182
+ nx.draw_networkx_nodes(G, pos, nodelist=node_list, node_color=color, node_size=500, alpha=0.8, ax=ax)
183
+ nx.draw_networkx_edges(G, pos, width=1, alpha=0.5, ax=ax)
184
+ nx.draw_networkx_labels(G, pos, font_size=8, font_weight="bold", ax=ax)
185
+ ax.set_title(f"Relaciones entre Entidades ({lang_code})", fontsize=16)
186
+ ax.axis('off')
187
+ plt.tight_layout()
188
+ return fig
189
+
190
+
191
+ #################################################################################
192
+ def create_topic_graph(topics, doc):
193
+ G = nx.Graph()
194
+ for topic in topics:
195
+ G.add_node(topic, weight=doc.text.count(topic))
196
+ for i, topic1 in enumerate(topics):
197
+ for topic2 in topics[i+1:]:
198
+ weight = sum(1 for sent in doc.sents if topic1 in sent.text and topic2 in sent.text)
199
+ if weight > 0:
200
+ G.add_edge(topic1, topic2, weight=weight)
201
+ return G
202
+
203
+ def visualize_topic_graph(G, lang_code):
204
+ fig, ax = plt.subplots(figsize=(12, 8))
205
+ pos = nx.spring_layout(G)
206
+ node_sizes = [G.nodes[node]['weight'] * 100 for node in G.nodes()]
207
+ nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='lightgreen', alpha=0.8, ax=ax)
208
+ nx.draw_networkx_labels(G, pos, font_size=10, font_weight="bold", ax=ax)
209
+ edge_weights = [G[u][v]['weight'] for u, v in G.edges()]
210
+ nx.draw_networkx_edges(G, pos, width=edge_weights, alpha=0.5, ax=ax)
211
+ ax.set_title(f"Relaciones entre Temas ({lang_code})", fontsize=16)
212
+ ax.axis('off')
213
+ plt.tight_layout()
214
+ return fig
215
+
216
+ ###########################################################################################
217
+ def generate_summary(doc, lang_code):
218
+ sentences = list(doc.sents)
219
+ summary = sentences[:3] # Toma las primeras 3 oraciones como resumen
220
+ return " ".join([sent.text for sent in summary])
221
+
222
+ def extract_entities(doc, lang_code):
223
+ entities = defaultdict(list)
224
+ for ent in doc.ents:
225
+ if ent.label_ in ENTITY_LABELS[lang_code]:
226
+ entities[ent.label_].append(ent.text)
227
+ return dict(entities)
228
+
229
+ def analyze_sentiment(doc, lang_code):
230
+ positive_words = sum(1 for token in doc if token.sentiment > 0)
231
+ negative_words = sum(1 for token in doc if token.sentiment < 0)
232
+ total_words = len(doc)
233
+ if positive_words > negative_words:
234
+ return "Positivo"
235
+ elif negative_words > positive_words:
236
+ return "Negativo"
237
+ else:
238
+ return "Neutral"
239
+
240
+ def extract_topics(doc, lang_code):
241
+ vectorizer = TfidfVectorizer(stop_words='english', max_features=5)
242
+ tfidf_matrix = vectorizer.fit_transform([doc.text])
243
+ feature_names = vectorizer.get_feature_names_out()
244
+ return list(feature_names)
245
+
246
+ # Aseg煤rate de que todas las funciones necesarias est茅n exportadas
247
+ __all__ = [
248
+ 'perform_semantic_analysis',
249
+ 'identify_key_concepts',
250
+ 'create_concept_graph',
251
+ 'visualize_concept_graph',
252
+ 'create_entity_graph',
253
+ 'visualize_entity_graph',
254
+ 'generate_summary',
255
+ 'extract_entities',
256
+ 'analyze_sentiment',
257
+ 'create_topic_graph',
258
+ 'visualize_topic_graph',
259
+ 'extract_topics',
260
+ 'ENTITY_LABELS',
261
+ 'POS_COLORS',
262
+ 'POS_TRANSLATIONS'
263
  ]