bertrend 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. bertrend/BERTopicModel.py +320 -0
  2. bertrend/BERTrend.py +1211 -0
  3. bertrend/__init__.py +59 -0
  4. bertrend/config/bertopic_default_config.toml +48 -0
  5. bertrend/config/bertrend_default_config.toml +16 -0
  6. bertrend/config/parameters.py +92 -0
  7. bertrend/config/services_default_config.toml +32 -0
  8. bertrend/demos/__init__.py +4 -0
  9. bertrend/demos/demos_utils/__init__.py +13 -0
  10. bertrend/demos/demos_utils/data_loading_component.py +267 -0
  11. bertrend/demos/demos_utils/embed_documents_component.py +52 -0
  12. bertrend/demos/demos_utils/icons.py +41 -0
  13. bertrend/demos/demos_utils/messages.py +11 -0
  14. bertrend/demos/demos_utils/parameters_component.py +162 -0
  15. bertrend/demos/demos_utils/state_utils.py +99 -0
  16. bertrend/demos/start_summarizer_app.sh +9 -0
  17. bertrend/demos/start_topic_analysis_app.sh +9 -0
  18. bertrend/demos/start_weak_signals_app.sh +9 -0
  19. bertrend/demos/summarization/__init__.py +4 -0
  20. bertrend/demos/summarization/summarizer_app.py +98 -0
  21. bertrend/demos/topic_analysis/__init__.py +4 -0
  22. bertrend/demos/topic_analysis/app.py +87 -0
  23. bertrend/demos/topic_analysis/app_utils.py +69 -0
  24. bertrend/demos/topic_analysis/demo_pages/__init__.py +4 -0
  25. bertrend/demos/topic_analysis/demo_pages/explore_topics.py +455 -0
  26. bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +178 -0
  27. bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +573 -0
  28. bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +280 -0
  29. bertrend/demos/topic_analysis/demo_pages/training_page.py +219 -0
  30. bertrend/demos/topic_analysis/messages.py +13 -0
  31. bertrend/demos/weak_signals/__init__.py +4 -0
  32. bertrend/demos/weak_signals/app.py +510 -0
  33. bertrend/demos/weak_signals/messages.py +32 -0
  34. bertrend/demos/weak_signals/visualizations_utils.py +403 -0
  35. bertrend/llm_utils/__init__.py +4 -0
  36. bertrend/llm_utils/newsletter.css +131 -0
  37. bertrend/llm_utils/newsletter_features.py +272 -0
  38. bertrend/llm_utils/openai_client.py +235 -0
  39. bertrend/llm_utils/prompts.py +94 -0
  40. bertrend/metrics/__init__.py +4 -0
  41. bertrend/metrics/temporal_metrics_embedding.py +861 -0
  42. bertrend/metrics/topic_metrics.py +199 -0
  43. bertrend/resources/common_ngrams.json +23 -0
  44. bertrend/resources/stopwords-en.json +3 -0
  45. bertrend/resources/stopwords-fr.json +1 -0
  46. bertrend/resources/stopwords-rte.json +15 -0
  47. bertrend/services/__init__.py +4 -0
  48. bertrend/services/authentication.py +66 -0
  49. bertrend/services/embedding_client.py +154 -0
  50. bertrend/services/embedding_service.py +380 -0
  51. bertrend/services/summarizer.py +45 -0
  52. bertrend/services/summary/__init__.py +4 -0
  53. bertrend/services/summary/abstractive_summarizer.py +74 -0
  54. bertrend/services/summary/chatgpt_summarizer.py +67 -0
  55. bertrend/services/summary/extractive_summarizer.py +368 -0
  56. bertrend/services/summary/lexrank.py +126 -0
  57. bertrend/services/summary/prompts.py +43 -0
  58. bertrend/tests/__init__.py +4 -0
  59. bertrend/tests/test_abstractive_summarizer.py +140 -0
  60. bertrend/tests/test_bertrend.py +1091 -0
  61. bertrend/tests/test_cache_utils.py +111 -0
  62. bertrend/tests/test_chatgpt_summarizer.py +150 -0
  63. bertrend/tests/test_config_utils.py +54 -0
  64. bertrend/tests/test_data_loading.py +326 -0
  65. bertrend/tests/test_data_structure.py +53 -0
  66. bertrend/tests/test_embedding_client.py +546 -0
  67. bertrend/tests/test_extractive_summarizer.py +250 -0
  68. bertrend/tests/test_installation.py +183 -0
  69. bertrend/tests/test_newsletter_features.py +354 -0
  70. bertrend/tests/test_openai_client.py +210 -0
  71. bertrend/tests/test_representative_docs.py +125 -0
  72. bertrend/tests/test_summarizer.py +115 -0
  73. bertrend/tests/test_temporal_metrics_embedding.py +349 -0
  74. bertrend/tests/test_topic_description.py +159 -0
  75. bertrend/tests/test_topic_metrics.py +233 -0
  76. bertrend/tests/test_topic_model.py +285 -0
  77. bertrend/tests/test_trend_analysis_data_structure.py +174 -0
  78. bertrend/tests/test_trend_analysis_prompts.py +225 -0
  79. bertrend/tests/test_trend_analysis_visualizations.py +307 -0
  80. bertrend/tests/test_trend_analysis_weak_signals.py +598 -0
  81. bertrend/tests/test_utils_init.py +70 -0
  82. bertrend/tests/test_visualizations.py +306 -0
  83. bertrend/topic_analysis/__init__.py +4 -0
  84. bertrend/topic_analysis/data_structure.py +13 -0
  85. bertrend/topic_analysis/prompts.py +42 -0
  86. bertrend/topic_analysis/representative_docs.py +57 -0
  87. bertrend/topic_analysis/topic_description.py +66 -0
  88. bertrend/topic_analysis/visualizations.py +315 -0
  89. bertrend/trend_analysis/__init__.py +4 -0
  90. bertrend/trend_analysis/data_structure.py +78 -0
  91. bertrend/trend_analysis/prompts.py +229 -0
  92. bertrend/trend_analysis/signal_llm_template_en.html +181 -0
  93. bertrend/trend_analysis/signal_llm_template_fr.html +181 -0
  94. bertrend/trend_analysis/visualizations.py +605 -0
  95. bertrend/trend_analysis/weak_signals.py +425 -0
  96. bertrend/utils/__init__.py +31 -0
  97. bertrend/utils/cache_utils.py +31 -0
  98. bertrend/utils/config_utils.py +41 -0
  99. bertrend/utils/data_loading.py +329 -0
  100. bertrend-0.3.1.dist-info/AUTHORS.txt +15 -0
  101. bertrend-0.3.1.dist-info/LICENSE.md +373 -0
  102. bertrend-0.3.1.dist-info/METADATA +400 -0
  103. bertrend-0.3.1.dist-info/RECORD +181 -0
  104. bertrend-0.3.1.dist-info/WHEEL +4 -0
  105. bertrend_apps/__init__.py +4 -0
  106. bertrend_apps/common/__init__.py +4 -0
  107. bertrend_apps/common/crontab_utils.py +113 -0
  108. bertrend_apps/common/mail_utils.py +91 -0
  109. bertrend_apps/config/feeds/arxiv_feed.toml +15 -0
  110. bertrend_apps/config/feeds/decarbonation_feed.toml +15 -0
  111. bertrend_apps/config/feeds/llm_en_feed.toml +15 -0
  112. bertrend_apps/config/feeds/llm_feed.toml +16 -0
  113. bertrend_apps/config/feeds/nlp_feed.toml +15 -0
  114. bertrend_apps/config/feeds/rte_feed.toml +15 -0
  115. bertrend_apps/config/gmail_credentials.json +1 -0
  116. bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +39 -0
  117. bertrend_apps/config/newsletters/arxiv_newsletter_from_scratch.toml +39 -0
  118. bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +38 -0
  119. bertrend_apps/config/newsletters/decarbonation_newsletter_from_scratch.toml +38 -0
  120. bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +38 -0
  121. bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +39 -0
  122. bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +38 -0
  123. bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +38 -0
  124. bertrend_apps/config/newsletters/rte_newsletter_from_scratch.toml +38 -0
  125. bertrend_apps/data_provider/__init__.py +9 -0
  126. bertrend_apps/data_provider/__main__.py +237 -0
  127. bertrend_apps/data_provider/arxiv_provider.py +161 -0
  128. bertrend_apps/data_provider/bing_news_provider.py +82 -0
  129. bertrend_apps/data_provider/curebot_provider.py +105 -0
  130. bertrend_apps/data_provider/data_provider.py +194 -0
  131. bertrend_apps/data_provider/google_news_provider.py +94 -0
  132. bertrend_apps/data_provider/newscatcher_provider.py +74 -0
  133. bertrend_apps/data_provider/utils.py +165 -0
  134. bertrend_apps/exploration/__init__.py +4 -0
  135. bertrend_apps/exploration/curebot/__init__.py +4 -0
  136. bertrend_apps/exploration/curebot/app.py +45 -0
  137. bertrend_apps/exploration/curebot/app_utils.py +317 -0
  138. bertrend_apps/exploration/curebot/config.toml +33 -0
  139. bertrend_apps/exploration/curebot/import_extraction_article.ipynb +108 -0
  140. bertrend_apps/exploration/curebot/old_veille_analyse.py +298 -0
  141. bertrend_apps/exploration/curebot/prompts.py +24 -0
  142. bertrend_apps/exploration/curebot/start_newsletter_generator.sh +8 -0
  143. bertrend_apps/exploration/curebot/tabs/tab1.py +150 -0
  144. bertrend_apps/exploration/curebot/tabs/tab2.py +95 -0
  145. bertrend_apps/exploration/curebot/tabs/tab3.py +125 -0
  146. bertrend_apps/exploration/dataviz/Main_page.py +10 -0
  147. bertrend_apps/exploration/dataviz/__init__.py +4 -0
  148. bertrend_apps/exploration/dataviz/pages/1_Data_feeds.py +105 -0
  149. bertrend_apps/exploration/geolocalization/__init__.py +4 -0
  150. bertrend_apps/exploration/geolocalization/entity_utils.py +118 -0
  151. bertrend_apps/exploration/geolocalization/spacy/__init__.py +4 -0
  152. bertrend_apps/exploration/geolocalization/spacy/common_factory.py +142 -0
  153. bertrend_apps/exploration/geolocalization/spacy/dbpedia.py +206 -0
  154. bertrend_apps/exploration/geolocalization/spacy/nlp.config +37 -0
  155. bertrend_apps/exploration/geolocalization/spacy/utils.py +39 -0
  156. bertrend_apps/install_feeds_and_newsletters.py +29 -0
  157. bertrend_apps/newsletters/__init__.py +4 -0
  158. bertrend_apps/newsletters/__main__.py +277 -0
  159. bertrend_apps/prospective_demo/.streamlit/config.toml +3 -0
  160. bertrend_apps/prospective_demo/.streamlit/secrets.toml +9 -0
  161. bertrend_apps/prospective_demo/__init__.py +70 -0
  162. bertrend_apps/prospective_demo/app.py +111 -0
  163. bertrend_apps/prospective_demo/authentication.py +44 -0
  164. bertrend_apps/prospective_demo/dashboard_analysis.py +132 -0
  165. bertrend_apps/prospective_demo/dashboard_common.py +79 -0
  166. bertrend_apps/prospective_demo/dashboard_signals.py +226 -0
  167. bertrend_apps/prospective_demo/feeds_common.py +38 -0
  168. bertrend_apps/prospective_demo/feeds_config.py +261 -0
  169. bertrend_apps/prospective_demo/feeds_data.py +95 -0
  170. bertrend_apps/prospective_demo/i18n.py +136 -0
  171. bertrend_apps/prospective_demo/llm_utils.py +34 -0
  172. bertrend_apps/prospective_demo/models_info.py +361 -0
  173. bertrend_apps/prospective_demo/perf_utils.py +23 -0
  174. bertrend_apps/prospective_demo/process_new_data.py +348 -0
  175. bertrend_apps/prospective_demo/report_generation.py +214 -0
  176. bertrend_apps/prospective_demo/report_template.html +148 -0
  177. bertrend_apps/prospective_demo/report_utils.py +132 -0
  178. bertrend_apps/prospective_demo/start_demo.sh +9 -0
  179. bertrend_apps/prospective_demo/streamlit_utils.py +37 -0
  180. bertrend_apps/start_apps.sh +15 -0
  181. bertrend_apps/stop_apps.sh +11 -0
@@ -0,0 +1,510 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ import torch
7
+
8
+ # workaround with streamlit to avoid errors Examining the path of torch.classes raised: Tried to instantiate class 'path.path’, but it does not exist! Ensure that it is registered via torch::class
9
+ torch.classes.__path__ = []
10
+
11
+ import pickle
12
+ import shutil
13
+ from typing import Literal
14
+
15
+ import streamlit as st
16
+ import numpy as np
17
+ import pandas as pd
18
+ import plotly.graph_objects as go
19
+ from loguru import logger
20
+
21
+ from bertrend import ZEROSHOT_TOPICS_DATA_DIR, CACHE_PATH
22
+ from bertrend.BERTrend import BERTrend
23
+ from bertrend.demos.demos_utils import is_admin_mode
24
+ from bertrend.demos.demos_utils.data_loading_component import (
25
+ display_data_loading_component,
26
+ )
27
+ from bertrend.demos.demos_utils.embed_documents_component import (
28
+ display_embed_documents_component,
29
+ )
30
+ from bertrend.demos.demos_utils.icons import (
31
+ WARNING_ICON,
32
+ SUCCESS_ICON,
33
+ SETTINGS_ICON,
34
+ TOPIC_ICON,
35
+ TREND_ICON,
36
+ ERROR_ICON,
37
+ ANALYSIS_ICON,
38
+ MODEL_TRAINING_ICON,
39
+ DATA_LOADING_ICON,
40
+ EMBEDDING_ICON,
41
+ )
42
+ from bertrend.demos.demos_utils.messages import (
43
+ NO_EMBEDDINGS_WARNING_MESSAGE,
44
+ MODEL_TRAINING_COMPLETE_MESSAGE,
45
+ )
46
+ from bertrend.demos.demos_utils.parameters_component import (
47
+ display_bertopic_hyperparameters,
48
+ display_bertrend_hyperparameters,
49
+ display_embedding_hyperparameters,
50
+ )
51
+ from bertrend.BERTopicModel import BERTopicModel
52
+ from bertrend.demos.weak_signals.messages import (
53
+ MODEL_MERGING_COMPLETE_MESSAGE,
54
+ NO_CACHE_WARNING,
55
+ CACHE_PURGED_MESSAGE,
56
+ MODELS_RESTORED_MESSAGE,
57
+ NO_DATA_WARNING,
58
+ STATE_SAVED_MESSAGE,
59
+ STATE_RESTORED_MESSAGE,
60
+ MODELS_SAVED_MESSAGE,
61
+ NO_MODELS_WARNING,
62
+ NO_STATE_WARNING,
63
+ EMBED_WARNING,
64
+ EMBED_TRAIN_WARNING,
65
+ TRAIN_WARNING,
66
+ MERGE_WARNING,
67
+ )
68
+ from bertrend.trend_analysis.weak_signals import detect_weak_signals_zeroshot
69
+
70
+ from bertrend.utils.data_loading import (
71
+ group_by_days,
72
+ TEXT_COLUMN,
73
+ )
74
+ from bertrend.config.parameters import *
75
+ from bertrend.demos.demos_utils.state_utils import SessionStateManager
76
+ from bertrend.trend_analysis.visualizations import (
77
+ plot_size_outliers,
78
+ plot_num_topics,
79
+ )
80
+ from bertrend.demos.weak_signals.visualizations_utils import (
81
+ PLOTLY_BUTTON_SAVE_CONFIG,
82
+ display_sankey_diagram,
83
+ display_topics_per_timestamp,
84
+ display_newly_emerged_topics,
85
+ display_popularity_evolution,
86
+ save_signal_evolution,
87
+ display_signal_analysis,
88
+ retrieve_topic_counts,
89
+ display_signal_types,
90
+ )
91
+
92
+ # UI Settings
93
+ PAGE_TITLE = "BERTrend - Retrospective Trend Analysis demo"
94
+ LAYOUT: Literal["centered", "wide"] = "wide"
95
+
96
+
97
+ # TODO: handle uploaded files
98
+ def save_state():
99
+ """Save the application state"""
100
+ state_file = CACHE_PATH / STATE_FILE
101
+ embeddings_file = CACHE_PATH / EMBEDDINGS_FILE
102
+
103
+ # Save the selected files (list of filenames)
104
+ selected_files = SessionStateManager.get("selected_files", [])
105
+
106
+ state = SessionStateManager.get_multiple(
107
+ "selected_files",
108
+ "min_chars",
109
+ "split_by_paragraph",
110
+ "timeframe_slider",
111
+ "language",
112
+ "embedding_model_name",
113
+ "embedding_model",
114
+ "sample_size",
115
+ "min_similarity",
116
+ "zeroshot_min_similarity",
117
+ "embedding_dtype",
118
+ "data_embedded",
119
+ )
120
+
121
+ state["selected_files"] = selected_files
122
+
123
+ with open(state_file, "wb") as f:
124
+ pickle.dump(state, f)
125
+
126
+ np.save(embeddings_file, SessionStateManager.get_embeddings())
127
+ st.success(STATE_SAVED_MESSAGE, icon=SUCCESS_ICON)
128
+
129
+
130
+ # TODO: handle uploaded files
131
+ def restore_state():
132
+ """Restore the application state"""
133
+ state_file = CACHE_PATH / STATE_FILE
134
+ embeddings_file = CACHE_PATH / EMBEDDINGS_FILE
135
+
136
+ if state_file.exists() and embeddings_file.exists():
137
+ with open(state_file, "rb") as f:
138
+ state = pickle.load(f)
139
+
140
+ # Restore the selected files
141
+ selected_files = state.get("selected_files", [])
142
+ SessionStateManager.set("selected_files", selected_files)
143
+
144
+ # Restore other states
145
+ SessionStateManager.set_multiple(**state)
146
+ SessionStateManager.set("embeddings", np.load(embeddings_file))
147
+ st.success(STATE_RESTORED_MESSAGE, icon=SUCCESS_ICON)
148
+
149
+ # Update the multiselect widget with restored selected files
150
+ st.session_state["selected_files"] = selected_files
151
+ else:
152
+ st.warning(NO_STATE_WARNING, icon=WARNING_ICON)
153
+
154
+
155
+ def purge_cache():
156
+ """Purge cache data"""
157
+ if CACHE_PATH.exists():
158
+ shutil.rmtree(CACHE_PATH)
159
+ st.success(CACHE_PURGED_MESSAGE, icon=SUCCESS_ICON)
160
+ else:
161
+ st.warning(NO_CACHE_WARNING, icon=WARNING_ICON)
162
+
163
+
164
+ def load_data_page():
165
+ st.header("Data Loading and Preprocessing")
166
+
167
+ display_data_loading_component()
168
+
169
+ if "time_filtered_df" in st.session_state:
170
+ try:
171
+ display_embed_documents_component()
172
+ if SessionStateManager.get("data_embedded", False):
173
+ save_state()
174
+ except Exception as e:
175
+ logger.error(f"An error occurred while embedding documents: {e}")
176
+ st.error(
177
+ f"An error occurred while embedding documents: {e}",
178
+ icon=ERROR_ICON,
179
+ )
180
+
181
+
182
+ def training_page():
183
+ st.header("Model Training")
184
+
185
+ if not SessionStateManager.get("data_embedded"):
186
+ st.warning(NO_EMBEDDINGS_WARNING_MESSAGE, icon=WARNING_ICON)
187
+ st.stop()
188
+
189
+ # Show documents per grouped timestamp
190
+ with st.expander("Documents per Timestamp", expanded=True):
191
+ st.write(f"Granularity: {st.session_state['granularity']}")
192
+ grouped_data = group_by_days(
193
+ SessionStateManager.get_dataframe("time_filtered_df"),
194
+ day_granularity=st.session_state["granularity"],
195
+ )
196
+ non_empty_timestamps = [
197
+ timestamp for timestamp, group in grouped_data.items() if not group.empty
198
+ ]
199
+ if non_empty_timestamps:
200
+ selected_timestamp = st.select_slider(
201
+ "Select Timestamp",
202
+ options=non_empty_timestamps,
203
+ key="timestamp_slider",
204
+ )
205
+ selected_docs = grouped_data[selected_timestamp]
206
+ st.dataframe(
207
+ selected_docs[
208
+ ["timestamp", TEXT_COLUMN, "document_id", "source", "url"]
209
+ ],
210
+ use_container_width=True,
211
+ )
212
+ else:
213
+ st.warning(NO_DATA_WARNING, icon=WARNING_ICON)
214
+
215
+ if not SessionStateManager.get("data_embedded", False):
216
+ st.warning(
217
+ EMBED_WARNING,
218
+ icon=WARNING_ICON,
219
+ )
220
+ st.stop()
221
+ else:
222
+ # Zero-shot topic definition
223
+ zeroshot_topic_list = st.text_input(
224
+ "Enter zero-shot topics (separated by /)", value=""
225
+ )
226
+ zeroshot_topic_list = [
227
+ topic.strip() for topic in zeroshot_topic_list.split("/") if topic.strip()
228
+ ]
229
+ SessionStateManager.set("zeroshot_topic_list", zeroshot_topic_list)
230
+
231
+ if st.button("Train Models", type="primary"):
232
+ with st.spinner("Training models..."):
233
+ # FIXME: called twice (see above)
234
+ grouped_data = group_by_days(
235
+ SessionStateManager.get_dataframe("time_filtered_df"),
236
+ day_granularity=st.session_state["granularity"],
237
+ )
238
+
239
+ # Initialize topic model
240
+ topic_model = BERTopicModel(st.session_state["bertopic_config"])
241
+
242
+ # Created BERTrend object
243
+ bertrend = BERTrend(
244
+ config_file=st.session_state["bertrend_config"],
245
+ topic_model=topic_model,
246
+ )
247
+ # Train topic models on data
248
+ bertrend.train_topic_models(
249
+ grouped_data=grouped_data,
250
+ embedding_model=SessionStateManager.get("embedding_model"),
251
+ embeddings=SessionStateManager.get_embeddings(),
252
+ )
253
+ st.success(MODEL_TRAINING_COMPLETE_MESSAGE, icon=SUCCESS_ICON)
254
+
255
+ # Save trained models
256
+ bertrend.save_model()
257
+ st.success(MODELS_SAVED_MESSAGE, icon=SUCCESS_ICON)
258
+
259
+ # Compute signal popularity
260
+ bertrend.calculate_signal_popularity()
261
+ SessionStateManager.set("popularity_computed", True)
262
+
263
+ # Store bertrend object
264
+ SessionStateManager.set("bertrend", bertrend)
265
+
266
+ st.success(MODEL_MERGING_COMPLETE_MESSAGE, icon=SUCCESS_ICON)
267
+
268
+
269
+ def analysis_page():
270
+ st.header("Results Analysis")
271
+
272
+ if not SessionStateManager.get("data_embedded"):
273
+ st.warning(
274
+ EMBED_TRAIN_WARNING,
275
+ icon=WARNING_ICON,
276
+ )
277
+ st.stop()
278
+
279
+ elif (
280
+ not SessionStateManager.get("bertrend")
281
+ or not SessionStateManager.get("bertrend")._is_fitted
282
+ ):
283
+ st.warning(
284
+ TRAIN_WARNING,
285
+ icon=WARNING_ICON,
286
+ )
287
+ st.stop()
288
+
289
+ else:
290
+ topic_models = SessionStateManager.get("bertrend").restore_topic_models()
291
+ with st.expander("Topic Overview", expanded=False):
292
+ # Number of Topics Detected for each topic model
293
+ st.plotly_chart(
294
+ plot_num_topics(topic_models),
295
+ config=PLOTLY_BUTTON_SAVE_CONFIG,
296
+ use_container_width=True,
297
+ )
298
+ # Size of Outlier Topic for each topic model
299
+ st.plotly_chart(
300
+ plot_size_outliers(topic_models),
301
+ config=PLOTLY_BUTTON_SAVE_CONFIG,
302
+ use_container_width=True,
303
+ )
304
+
305
+ display_topics_per_timestamp(topic_models)
306
+
307
+ # Display zeroshot signal trend
308
+ zeroshot_topic_list = SessionStateManager.get("zeroshot_topic_list", None)
309
+ if zeroshot_topic_list:
310
+ st.subheader("Zero-shot Weak Signal Trends")
311
+ weak_signal_trends = detect_weak_signals_zeroshot(
312
+ topic_models,
313
+ zeroshot_topic_list,
314
+ st.session_state["granularity"],
315
+ )
316
+ with st.expander("Zero-shot Weak Signal Trends", expanded=False):
317
+ fig_trend = go.Figure()
318
+ for topic, weak_signal_trend in weak_signal_trends.items():
319
+ timestamps = list(weak_signal_trend.keys())
320
+ popularity = [
321
+ weak_signal_trend[timestamp]["Document_Count"]
322
+ for timestamp in timestamps
323
+ ]
324
+ hovertext = [
325
+ f"Topic: {topic}<br>Timestamp: {timestamp}<br>Popularity: {weak_signal_trend[timestamp]['Document_Count']}<br>Representation: {weak_signal_trend[timestamp]['Representation']}"
326
+ for timestamp in timestamps
327
+ ]
328
+ fig_trend.add_trace(
329
+ go.Scatter(
330
+ x=timestamps,
331
+ y=popularity,
332
+ mode="lines+markers",
333
+ name=topic,
334
+ hovertext=hovertext,
335
+ hoverinfo="text",
336
+ )
337
+ )
338
+ fig_trend.update_layout(
339
+ title="Popularity of Zero-Shot Topics",
340
+ xaxis_title="Timestamp",
341
+ yaxis_title="Popularity",
342
+ )
343
+ st.plotly_chart(
344
+ fig_trend,
345
+ config=PLOTLY_BUTTON_SAVE_CONFIG,
346
+ use_container_width=True,
347
+ )
348
+
349
+ # Display the dataframe with zeroshot topics information
350
+ zeroshot_topics_data = [
351
+ {
352
+ "Topic": topic,
353
+ "Timestamp": timestamp,
354
+ "Representation": data["Representation"],
355
+ "Representative_Docs": data["Representative_Docs"],
356
+ "Count": data["Count"],
357
+ "Document_Count": data["Document_Count"],
358
+ }
359
+ for topic, weak_signal_trend in weak_signal_trends.items()
360
+ for timestamp, data in weak_signal_trend.items()
361
+ ]
362
+ zeroshot_topics_df = pd.DataFrame(zeroshot_topics_data)
363
+ st.dataframe(zeroshot_topics_df, use_container_width=True)
364
+
365
+ # Save the zeroshot topics data to a JSON file
366
+ json_file_path = ZEROSHOT_TOPICS_DATA_DIR
367
+ json_file_path.mkdir(parents=True, exist_ok=True)
368
+
369
+ zeroshot_topics_df.to_json(
370
+ json_file_path / ZEROSHOT_TOPICS_DATA_FILE,
371
+ orient="records",
372
+ date_format="iso",
373
+ indent=4,
374
+ )
375
+ st.success(
376
+ f"Zeroshot topics data saved to {json_file_path}",
377
+ icon=SUCCESS_ICON,
378
+ )
379
+
380
+ if not SessionStateManager.get("popularity_computed", False):
381
+ st.warning(
382
+ MERGE_WARNING,
383
+ icon=WARNING_ICON,
384
+ )
385
+ st.stop()
386
+
387
+ else:
388
+ # Display merged signal trend
389
+ with st.expander("Topic Size Evolution", expanded=False):
390
+ st.dataframe(
391
+ SessionStateManager.get("bertrend").all_merge_histories_df[
392
+ [
393
+ "Timestamp",
394
+ "Topic1",
395
+ "Topic2",
396
+ "Representation1",
397
+ "Representation2",
398
+ "Document_Count1",
399
+ "Document_Count2",
400
+ ]
401
+ ]
402
+ )
403
+
404
+ # Display topic popularity evolution
405
+ with st.expander("Topic Popularity Evolution", expanded=True):
406
+ display_popularity_evolution()
407
+ # Save Signal Evolution Data to investigate later on in a separate notebook
408
+ save_signal_evolution()
409
+
410
+ # Show weak/strong signals
411
+ display_signal_types()
412
+
413
+ # Analyze signal
414
+ with st.expander("Signal Analysis", expanded=True):
415
+ st.subheader("Signal Analysis")
416
+ topic_number = st.number_input(
417
+ "Enter a topic number to take a closer look:", min_value=0, step=1
418
+ )
419
+ if st.button("Analyze signal", type="primary"):
420
+ try:
421
+ display_signal_analysis(topic_number)
422
+ except Exception as e:
423
+ st.error(
424
+ f"Error while trying to generate signal summary: {e}",
425
+ icon=ERROR_ICON,
426
+ )
427
+
428
+ # Create the Sankey Diagram
429
+ st.subheader("Topic Evolution")
430
+ display_sankey_diagram(
431
+ SessionStateManager.get("bertrend").all_merge_histories_df
432
+ )
433
+
434
+ # Newly emerged topics
435
+ if SessionStateManager.get("bertrend").all_new_topics_df is not None:
436
+ st.subheader("Newly Emerged Topics")
437
+ display_newly_emerged_topics(
438
+ SessionStateManager.get("bertrend").all_new_topics_df
439
+ )
440
+
441
+ if st.button("Retrieve Topic Counts"):
442
+ with st.spinner("Retrieving topic counts..."):
443
+ # Number of topics per individual topic model
444
+ retrieve_topic_counts(topic_models)
445
+
446
+
447
+ def main():
448
+ st.set_page_config(
449
+ page_title=PAGE_TITLE,
450
+ layout=LAYOUT,
451
+ initial_sidebar_state="expanded" if is_admin_mode() else "collapsed",
452
+ page_icon=":part_alternation_mark:",
453
+ )
454
+
455
+ st.title(":part_alternation_mark: " + PAGE_TITLE)
456
+
457
+ # Set the main flags
458
+ SessionStateManager.get_or_set("data_embedded", False)
459
+ SessionStateManager.get_or_set("popularity_computed", False)
460
+
461
+ # Sidebar
462
+ with st.sidebar:
463
+ st.header(SETTINGS_ICON + " Settings and Controls")
464
+
465
+ # State Management
466
+ st.subheader("State Management")
467
+
468
+ if st.button("Restore Previous Run", use_container_width=True):
469
+ restore_state()
470
+ try:
471
+ SessionStateManager.set("bertrend", BERTrend.restore_model())
472
+ st.success(MODELS_RESTORED_MESSAGE, icon=SUCCESS_ICON)
473
+ except Exception as e:
474
+ st.warning(NO_MODELS_WARNING, icon=WARNING_ICON)
475
+
476
+ if st.button("Purge Cache", use_container_width=True):
477
+ purge_cache()
478
+
479
+ if st.button("Clear session state", use_container_width=True):
480
+ SessionStateManager.clear()
481
+
482
+ # BERTopic Hyperparameters
483
+ st.subheader(EMBEDDING_ICON + " Embedding Hyperparameters")
484
+ display_embedding_hyperparameters()
485
+ st.subheader(TOPIC_ICON + " BERTopic Hyperparameters")
486
+ display_bertopic_hyperparameters()
487
+ st.subheader(TREND_ICON + " BERTrend Hyperparameters")
488
+ display_bertrend_hyperparameters()
489
+
490
+ # Main content
491
+ tab1, tab2, tab3 = st.tabs(
492
+ [
493
+ DATA_LOADING_ICON + " Data Loading",
494
+ MODEL_TRAINING_ICON + " Model Training",
495
+ ANALYSIS_ICON + " Results Analysis",
496
+ ]
497
+ )
498
+
499
+ with tab1:
500
+ load_data_page()
501
+
502
+ with tab2:
503
+ training_page()
504
+
505
+ with tab3:
506
+ analysis_page()
507
+
508
+
509
+ if __name__ == "__main__":
510
+ main()
@@ -0,0 +1,32 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ # Success Messages
7
+ STATE_SAVED_MESSAGE = "Application state saved."
8
+ STATE_RESTORED_MESSAGE = "Application state restored."
9
+ MODELS_SAVED_MESSAGE = "Models saved."
10
+ MODELS_RESTORED_MESSAGE = "Models restored."
11
+ MODEL_MERGING_COMPLETE_MESSAGE = "Model merging complete!"
12
+ TOPIC_COUNTS_SAVED_MESSAGE = "Topic and signal counts saved to {file_path}"
13
+ CACHE_PURGED_MESSAGE = "Cache purged."
14
+
15
+ PROGRESS_BAR_DESCRIPTION = "Batches processed"
16
+
17
+ # Error Messages
18
+ NO_DATA_WARNING = "No data available for the selected granularity."
19
+ NO_STATE_WARNING = "No saved state found."
20
+ NO_MODELS_WARNING = "No saved models found."
21
+ NO_CACHE_WARNING = "No cache found."
22
+ EMBED_WARNING = "Please embed data before proceeding to model training."
23
+ EMBED_TRAIN_WARNING = (
24
+ "Please embed data and train models before proceeding to analysis."
25
+ )
26
+ TRAIN_WARNING = "Please train models before proceeding to analysis."
27
+ MERGE_WARNING = "Please merge models to view additional analyses."
28
+ TOPIC_NOT_FOUND_WARNING = (
29
+ "Topic {topic_number} not found in the merge histories within the specified window."
30
+ )
31
+ NO_GRANULARITY_WARNING = "Granularity value not found."
32
+ HTML_GENERATION_FAILED_WARNING = "HTML generation failed. Displaying markdown instead."