Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -33,6 +33,7 @@ def load_raw_data(filepath):
|
|
| 33 |
DATA_PATH = "data.jsonl"
|
| 34 |
if not os.path.exists(DATA_PATH):
|
| 35 |
st.error("data.jsonl file not found. Please ensure it is in the same directory as this app.")
|
|
|
|
| 36 |
else:
|
| 37 |
raw_df = load_raw_data(DATA_PATH)
|
| 38 |
|
|
@@ -67,7 +68,7 @@ elif "title" in df.columns:
|
|
| 67 |
else:
|
| 68 |
text_col = None
|
| 69 |
|
| 70 |
-
# For hashtags: if not provided, extract from text using regex.
|
| 71 |
if "hashtags" not in df.columns:
|
| 72 |
def extract_hashtags(row):
|
| 73 |
text = ""
|
|
@@ -105,6 +106,7 @@ if timestamp_col in df.columns:
|
|
| 105 |
end_date = st.sidebar.date_input("End date", max_date, min_value=min_date, max_value=max_date)
|
| 106 |
if start_date > end_date:
|
| 107 |
st.sidebar.error("Error: End date must fall after start date.")
|
|
|
|
| 108 |
df = df[(df[timestamp_col].dt.date >= start_date) & (df[timestamp_col].dt.date <= end_date)]
|
| 109 |
except Exception as e:
|
| 110 |
st.sidebar.error("Error processing the timestamp column for filtering.")
|
|
@@ -142,15 +144,90 @@ if timestamp_col in df.columns:
|
|
| 142 |
df["date"] = df[timestamp_col].dt.date
|
| 143 |
time_series = df.groupby("date").size().reset_index(name="count")
|
| 144 |
time_series["7-day Moving Avg"] = time_series["count"].rolling(window=7).mean()
|
| 145 |
-
fig_time = px.line(
|
| 146 |
-
|
| 147 |
-
|
|
|
|
|
|
|
| 148 |
st.plotly_chart(fig_time)
|
| 149 |
else:
|
| 150 |
st.info("No timestamp data available for time series plot.")
|
| 151 |
|
| 152 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
community_col = "subreddit" if "subreddit" in df.columns else user_col
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
if community_col in df.columns:
|
| 155 |
st.markdown("### Top Communities/Accounts Contributions")
|
| 156 |
contributions = df[community_col].value_counts().reset_index()
|
|
@@ -162,6 +239,9 @@ if community_col in df.columns:
|
|
| 162 |
else:
|
| 163 |
st.info("No community or account data available for contributor pie chart.")
|
| 164 |
|
|
|
|
|
|
|
|
|
|
| 165 |
# Top Hashtags Bar Chart
|
| 166 |
if hashtags_col in df.columns:
|
| 167 |
st.markdown("### Top Hashtags")
|
|
@@ -170,9 +250,11 @@ if hashtags_col in df.columns:
|
|
| 170 |
top_hashtags = hashtags_exploded[hashtags_col].value_counts().reset_index()
|
| 171 |
top_hashtags.columns = ['hashtag', 'count']
|
| 172 |
if not top_hashtags.empty:
|
| 173 |
-
fig_hashtags = px.bar(
|
| 174 |
-
|
| 175 |
-
|
|
|
|
|
|
|
| 176 |
st.plotly_chart(fig_hashtags)
|
| 177 |
else:
|
| 178 |
st.info("No hashtag data available.")
|
|
@@ -183,19 +265,19 @@ else:
|
|
| 183 |
if text_col is not None and text_col in df.columns:
|
| 184 |
st.markdown("### Sentiment Analysis")
|
| 185 |
df['sentiment'] = df[text_col].apply(lambda x: TextBlob(x).sentiment.polarity if isinstance(x, str) else 0)
|
| 186 |
-
fig_sentiment = px.histogram(
|
| 187 |
-
|
| 188 |
-
|
|
|
|
|
|
|
| 189 |
st.plotly_chart(fig_sentiment)
|
| 190 |
else:
|
| 191 |
st.info(f"No '{text_col}' column available for sentiment analysis.")
|
| 192 |
|
| 193 |
# --------------------------------------------------------------------------------
|
| 194 |
-
#
|
| 195 |
# --------------------------------------------------------------------------------
|
| 196 |
-
|
| 197 |
-
# (a) Topic Embedding Visualization using LDA + TSNE
|
| 198 |
-
st.markdown("## Topic Embedding Visualization")
|
| 199 |
if text_col in df.columns:
|
| 200 |
texts = df[text_col].dropna().sample(n=min(500, len(df)), random_state=42).tolist()
|
| 201 |
vectorizer = CountVectorizer(stop_words='english', max_features=1000)
|
|
@@ -203,46 +285,57 @@ if text_col in df.columns:
|
|
| 203 |
lda = LatentDirichletAllocation(n_components=5, random_state=42)
|
| 204 |
topic_matrix = lda.fit_transform(X)
|
| 205 |
dominant_topic = topic_matrix.argmax(axis=1)
|
|
|
|
| 206 |
tsne_model = TSNE(n_components=2, random_state=42)
|
| 207 |
tsne_values = tsne_model.fit_transform(topic_matrix)
|
| 208 |
tsne_df = pd.DataFrame(tsne_values, columns=["x", "y"])
|
| 209 |
tsne_df["Dominant Topic"] = dominant_topic.astype(str)
|
| 210 |
-
|
| 211 |
-
|
|
|
|
|
|
|
|
|
|
| 212 |
st.plotly_chart(fig_topics)
|
| 213 |
else:
|
| 214 |
st.info("No text data available for topic embedding.")
|
| 215 |
|
| 216 |
-
#
|
|
|
|
|
|
|
| 217 |
st.markdown("## GenAI Summary for Time Series")
|
| 218 |
if timestamp_col in df.columns:
|
| 219 |
-
|
| 220 |
-
if not
|
| 221 |
-
start =
|
| 222 |
-
end =
|
| 223 |
-
avg_posts =
|
| 224 |
-
peak =
|
| 225 |
-
description = (
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
| 227 |
st.write("Time Series Description:")
|
| 228 |
st.write(description)
|
| 229 |
|
| 230 |
-
# Use a smaller, faster
|
| 231 |
-
ts_summarizer = pipeline("
|
| 232 |
try:
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
)[0]['
|
| 236 |
st.markdown("**GenAI Summary:**")
|
| 237 |
st.write(ts_summary)
|
| 238 |
except Exception as e:
|
| 239 |
-
st.error("Error generating time series summary
|
| 240 |
else:
|
| 241 |
-
st.info("
|
| 242 |
else:
|
| 243 |
st.info("No timestamp column available for time series summary.")
|
| 244 |
|
| 245 |
-
#
|
|
|
|
|
|
|
| 246 |
st.markdown("## Offline Events from Wikipedia")
|
| 247 |
wiki_topic = st.text_input("Enter a topic to fetch offline events (e.g., 'Russian invasion of Ukraine'):")
|
| 248 |
if wiki_topic:
|
|
@@ -251,39 +344,59 @@ if wiki_topic:
|
|
| 251 |
st.markdown(f"**Wikipedia Summary for '{wiki_topic}':**")
|
| 252 |
st.write(wiki_summary)
|
| 253 |
except Exception as e:
|
| 254 |
-
st.error("Error retrieving Wikipedia data
|
| 255 |
|
| 256 |
-
#
|
|
|
|
|
|
|
| 257 |
st.markdown("## Semantic Search on Posts")
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
st.markdown("## AI-Generated Summary of Posts")
|
| 281 |
if text_col in df.columns:
|
| 282 |
-
|
|
|
|
| 283 |
|
| 284 |
def generate_summary(text, summarizer, max_chunk_length=1000):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
chunks, current_chunk = [], ""
|
| 286 |
-
|
|
|
|
| 287 |
sentence = sentence.strip() + ". "
|
| 288 |
if len(current_chunk) + len(sentence) <= max_chunk_length:
|
| 289 |
current_chunk += sentence
|
|
@@ -293,21 +406,23 @@ if text_col in df.columns:
|
|
| 293 |
if current_chunk:
|
| 294 |
chunks.append(current_chunk.strip())
|
| 295 |
|
| 296 |
-
|
|
|
|
| 297 |
for chunk in chunks:
|
| 298 |
if len(chunk) > 50:
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
)
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
)[0]['
|
| 307 |
return final_summary
|
| 308 |
|
|
|
|
| 309 |
sample_text = " ".join(df[text_col].dropna().sample(n=min(10, len(df)), random_state=42).tolist())
|
| 310 |
-
if sample_text:
|
| 311 |
final_summary = generate_summary(sample_text, summarizer, max_chunk_length=1000)
|
| 312 |
st.write(final_summary)
|
| 313 |
else:
|
|
@@ -320,6 +435,15 @@ else:
|
|
| 320 |
# --------------------------------------------------------------------------------
|
| 321 |
st.markdown("### End of Dashboard")
|
| 322 |
st.markdown("""
|
| 323 |
-
This dashboard is a prototype
|
| 324 |
-
It demonstrates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 325 |
""")
|
|
|
|
| 33 |
DATA_PATH = "data.jsonl"
|
| 34 |
if not os.path.exists(DATA_PATH):
|
| 35 |
st.error("data.jsonl file not found. Please ensure it is in the same directory as this app.")
|
| 36 |
+
st.stop()
|
| 37 |
else:
|
| 38 |
raw_df = load_raw_data(DATA_PATH)
|
| 39 |
|
|
|
|
| 68 |
else:
|
| 69 |
text_col = None
|
| 70 |
|
| 71 |
+
# For hashtags: if not provided, extract them from text using regex.
|
| 72 |
if "hashtags" not in df.columns:
|
| 73 |
def extract_hashtags(row):
|
| 74 |
text = ""
|
|
|
|
| 106 |
end_date = st.sidebar.date_input("End date", max_date, min_value=min_date, max_value=max_date)
|
| 107 |
if start_date > end_date:
|
| 108 |
st.sidebar.error("Error: End date must fall after start date.")
|
| 109 |
+
# Filter df between selected dates
|
| 110 |
df = df[(df[timestamp_col].dt.date >= start_date) & (df[timestamp_col].dt.date <= end_date)]
|
| 111 |
except Exception as e:
|
| 112 |
st.sidebar.error("Error processing the timestamp column for filtering.")
|
|
|
|
| 144 |
df["date"] = df[timestamp_col].dt.date
|
| 145 |
time_series = df.groupby("date").size().reset_index(name="count")
|
| 146 |
time_series["7-day Moving Avg"] = time_series["count"].rolling(window=7).mean()
|
| 147 |
+
fig_time = px.line(
|
| 148 |
+
time_series, x="date", y=["count", "7-day Moving Avg"],
|
| 149 |
+
labels={"date": "Date", "value": "Number of Posts"},
|
| 150 |
+
title="Posts Over Time with 7-day Moving Average"
|
| 151 |
+
)
|
| 152 |
st.plotly_chart(fig_time)
|
| 153 |
else:
|
| 154 |
st.info("No timestamp data available for time series plot.")
|
| 155 |
|
| 156 |
+
# --------------------------------------------------------------------------------
|
| 157 |
+
# --------------------------- Network Diagram (Above Pie) -------------------------
|
| 158 |
+
# --------------------------------------------------------------------------------
|
| 159 |
+
"""
|
| 160 |
+
We'll create a user <-> community network from the top users and top subreddits.
|
| 161 |
+
For simplicity, we only include each user/subreddit once to avoid extremely large networks.
|
| 162 |
+
"""
|
| 163 |
+
st.markdown("### Network Diagram")
|
| 164 |
+
|
| 165 |
community_col = "subreddit" if "subreddit" in df.columns else user_col
|
| 166 |
+
|
| 167 |
+
# Build a small network of user->community edges
|
| 168 |
+
if community_col in df.columns and user_col in df.columns:
|
| 169 |
+
# Let's focus on top communities
|
| 170 |
+
top_communities_df = df[community_col].value_counts().nlargest(5) # top 5 subreddits or communities
|
| 171 |
+
top_communities = set(top_communities_df.index)
|
| 172 |
+
|
| 173 |
+
# For each row, if subreddit in top_communities, link author->subreddit
|
| 174 |
+
# For performance, take a sample of the entire dataset or filter only relevant rows.
|
| 175 |
+
sub_df = df[df[community_col].isin(top_communities)].copy()
|
| 176 |
+
sub_df = sub_df.dropna(subset=[user_col, community_col])
|
| 177 |
+
sub_df = sub_df.sample(min(500, len(sub_df)), random_state=42) # sample to reduce network size
|
| 178 |
+
|
| 179 |
+
net = Network(height="600px", width="100%", notebook=False, bgcolor="#ffffff", font_color="black")
|
| 180 |
+
|
| 181 |
+
# We'll track which nodes we've added to avoid duplicates
|
| 182 |
+
added_users = set()
|
| 183 |
+
added_comms = set()
|
| 184 |
+
|
| 185 |
+
for _, row in sub_df.iterrows():
|
| 186 |
+
user = str(row[user_col])
|
| 187 |
+
comm = str(row[community_col])
|
| 188 |
+
|
| 189 |
+
if user not in added_users:
|
| 190 |
+
net.add_node(user, label=user, color="#FFAAAA") # user node
|
| 191 |
+
added_users.add(user)
|
| 192 |
+
|
| 193 |
+
if comm not in added_comms:
|
| 194 |
+
net.add_node(comm, label=comm, color="#AAAACC") # community node
|
| 195 |
+
added_comms.add(comm)
|
| 196 |
+
|
| 197 |
+
net.add_edge(user, comm)
|
| 198 |
+
|
| 199 |
+
net.set_options("""
|
| 200 |
+
var options = {
|
| 201 |
+
"nodes": {
|
| 202 |
+
"scaling": {
|
| 203 |
+
"min": 10,
|
| 204 |
+
"max": 30
|
| 205 |
+
}
|
| 206 |
+
},
|
| 207 |
+
"edges": {
|
| 208 |
+
"smooth": {
|
| 209 |
+
"type": "continuous"
|
| 210 |
+
}
|
| 211 |
+
},
|
| 212 |
+
"physics": {
|
| 213 |
+
"barnesHut": {
|
| 214 |
+
"gravitationalConstant": -8000,
|
| 215 |
+
"springLength": 250
|
| 216 |
+
}
|
| 217 |
+
}
|
| 218 |
+
}
|
| 219 |
+
""")
|
| 220 |
+
# Generate network HTML
|
| 221 |
+
net.save_graph("network.html")
|
| 222 |
+
html_file = open("network.html", "r", encoding="utf-8")
|
| 223 |
+
components.html(html_file.read(), height=620)
|
| 224 |
+
html_file.close()
|
| 225 |
+
else:
|
| 226 |
+
st.info("Cannot build a network diagram without both user and community/subreddit columns.")
|
| 227 |
+
|
| 228 |
+
# --------------------------------------------------------------------------------
|
| 229 |
+
# --------------------------- Pie Chart of Top Contributors -----------------------
|
| 230 |
+
# --------------------------------------------------------------------------------
|
| 231 |
if community_col in df.columns:
|
| 232 |
st.markdown("### Top Communities/Accounts Contributions")
|
| 233 |
contributions = df[community_col].value_counts().reset_index()
|
|
|
|
| 239 |
else:
|
| 240 |
st.info("No community or account data available for contributor pie chart.")
|
| 241 |
|
| 242 |
+
# --------------------------------------------------------------------------------
|
| 243 |
+
# ---------------------- Top Hashtags & Sentiment Analysis -----------------------
|
| 244 |
+
# --------------------------------------------------------------------------------
|
| 245 |
# Top Hashtags Bar Chart
|
| 246 |
if hashtags_col in df.columns:
|
| 247 |
st.markdown("### Top Hashtags")
|
|
|
|
| 250 |
top_hashtags = hashtags_exploded[hashtags_col].value_counts().reset_index()
|
| 251 |
top_hashtags.columns = ['hashtag', 'count']
|
| 252 |
if not top_hashtags.empty:
|
| 253 |
+
fig_hashtags = px.bar(
|
| 254 |
+
top_hashtags.head(10), x='hashtag', y='count',
|
| 255 |
+
labels={'hashtag': 'Hashtag', 'count': 'Frequency'},
|
| 256 |
+
title="Top 10 Hashtags"
|
| 257 |
+
)
|
| 258 |
st.plotly_chart(fig_hashtags)
|
| 259 |
else:
|
| 260 |
st.info("No hashtag data available.")
|
|
|
|
| 265 |
if text_col is not None and text_col in df.columns:
|
| 266 |
st.markdown("### Sentiment Analysis")
|
| 267 |
df['sentiment'] = df[text_col].apply(lambda x: TextBlob(x).sentiment.polarity if isinstance(x, str) else 0)
|
| 268 |
+
fig_sentiment = px.histogram(
|
| 269 |
+
df, x='sentiment', nbins=30,
|
| 270 |
+
labels={'sentiment': 'Sentiment Polarity'},
|
| 271 |
+
title="Sentiment Polarity Distribution"
|
| 272 |
+
)
|
| 273 |
st.plotly_chart(fig_sentiment)
|
| 274 |
else:
|
| 275 |
st.info(f"No '{text_col}' column available for sentiment analysis.")
|
| 276 |
|
| 277 |
# --------------------------------------------------------------------------------
|
| 278 |
+
# ------------------------- Topic Embedding Visualization -------------------------
|
| 279 |
# --------------------------------------------------------------------------------
|
| 280 |
+
st.markdown("## Topic Embedding Visualization (LDA + TSNE)")
|
|
|
|
|
|
|
| 281 |
if text_col in df.columns:
|
| 282 |
texts = df[text_col].dropna().sample(n=min(500, len(df)), random_state=42).tolist()
|
| 283 |
vectorizer = CountVectorizer(stop_words='english', max_features=1000)
|
|
|
|
| 285 |
lda = LatentDirichletAllocation(n_components=5, random_state=42)
|
| 286 |
topic_matrix = lda.fit_transform(X)
|
| 287 |
dominant_topic = topic_matrix.argmax(axis=1)
|
| 288 |
+
|
| 289 |
tsne_model = TSNE(n_components=2, random_state=42)
|
| 290 |
tsne_values = tsne_model.fit_transform(topic_matrix)
|
| 291 |
tsne_df = pd.DataFrame(tsne_values, columns=["x", "y"])
|
| 292 |
tsne_df["Dominant Topic"] = dominant_topic.astype(str)
|
| 293 |
+
|
| 294 |
+
fig_topics = px.scatter(
|
| 295 |
+
tsne_df, x="x", y="y", color="Dominant Topic",
|
| 296 |
+
title="TSNE Embedding of Topics"
|
| 297 |
+
)
|
| 298 |
st.plotly_chart(fig_topics)
|
| 299 |
else:
|
| 300 |
st.info("No text data available for topic embedding.")
|
| 301 |
|
| 302 |
+
# --------------------------------------------------------------------------------
|
| 303 |
+
# ----------------------- GenAI Summary for Time Series Plot ---------------------
|
| 304 |
+
# --------------------------------------------------------------------------------
|
| 305 |
st.markdown("## GenAI Summary for Time Series")
|
| 306 |
if timestamp_col in df.columns:
|
| 307 |
+
time_df = df.groupby(df[timestamp_col].dt.date).size().reset_index(name="count")
|
| 308 |
+
if not time_df.empty:
|
| 309 |
+
start = time_df[timestamp_col].min()
|
| 310 |
+
end = time_df[timestamp_col].max()
|
| 311 |
+
avg_posts = time_df["count"].mean()
|
| 312 |
+
peak = time_df.loc[time_df["count"].idxmax()]
|
| 313 |
+
description = (
|
| 314 |
+
f"From {start} to {end}, the average number of posts per day was {avg_posts:.1f}. "
|
| 315 |
+
f"The highest activity was on {peak[timestamp_col]} with {peak['count']} posts."
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
st.write("Time Series Description:")
|
| 319 |
st.write(description)
|
| 320 |
|
| 321 |
+
# Use a smaller, faster FLAN-T5 model
|
| 322 |
+
ts_summarizer = pipeline("text2text-generation", model="google/flan-t5-base")
|
| 323 |
try:
|
| 324 |
+
# We'll prompt it in a summarization style for clarity
|
| 325 |
+
prompt = f"Summarize this data description: {description}"
|
| 326 |
+
ts_summary = ts_summarizer(prompt, max_length=80, do_sample=False)[0]['generated_text']
|
| 327 |
st.markdown("**GenAI Summary:**")
|
| 328 |
st.write(ts_summary)
|
| 329 |
except Exception as e:
|
| 330 |
+
st.error(f"Error generating time series summary: {e}")
|
| 331 |
else:
|
| 332 |
+
st.info("No data available for time series summarization.")
|
| 333 |
else:
|
| 334 |
st.info("No timestamp column available for time series summary.")
|
| 335 |
|
| 336 |
+
# --------------------------------------------------------------------------------
|
| 337 |
+
# ----------------------- Offline Events from Wikipedia --------------------------
|
| 338 |
+
# --------------------------------------------------------------------------------
|
| 339 |
st.markdown("## Offline Events from Wikipedia")
|
| 340 |
wiki_topic = st.text_input("Enter a topic to fetch offline events (e.g., 'Russian invasion of Ukraine'):")
|
| 341 |
if wiki_topic:
|
|
|
|
| 344 |
st.markdown(f"**Wikipedia Summary for '{wiki_topic}':**")
|
| 345 |
st.write(wiki_summary)
|
| 346 |
except Exception as e:
|
| 347 |
+
st.error(f"Error retrieving Wikipedia data: {e}")
|
| 348 |
|
| 349 |
+
# --------------------------------------------------------------------------------
|
| 350 |
+
# ----------------- Semantic Search on Posts using Sentence Transformers ---------
|
| 351 |
+
# --------------------------------------------------------------------------------
|
| 352 |
st.markdown("## Semantic Search on Posts")
|
| 353 |
+
if text_col and text_col in df.columns:
|
| 354 |
+
search_query = st.text_input("Enter your semantic search query:")
|
| 355 |
+
if search_query:
|
| 356 |
+
@st.cache_data
|
| 357 |
+
def get_post_embeddings(texts):
|
| 358 |
+
# Use a smaller, faster model
|
| 359 |
+
model = SentenceTransformer("sentence-transformers/all-distilroberta-v1")
|
| 360 |
+
return model.encode(texts, convert_to_tensor=True)
|
| 361 |
+
|
| 362 |
+
posts = df[text_col].dropna().tolist()
|
| 363 |
+
|
| 364 |
+
if posts:
|
| 365 |
+
embeddings = get_post_embeddings(posts)
|
| 366 |
+
model = SentenceTransformer("sentence-transformers/all-distilroberta-v1")
|
| 367 |
+
query_embedding = model.encode(search_query, convert_to_tensor=True)
|
| 368 |
+
|
| 369 |
+
cos_scores = util.cos_sim(query_embedding, embeddings)[0]
|
| 370 |
+
top_results = cos_scores.topk(5)
|
| 371 |
+
|
| 372 |
+
st.markdown("**Top Matching Posts:**")
|
| 373 |
+
for score, idx in zip(top_results.values, top_results.indices):
|
| 374 |
+
st.write(f"Score: {score.item():.3f}")
|
| 375 |
+
st.write(posts[idx])
|
| 376 |
+
st.write("---")
|
| 377 |
+
else:
|
| 378 |
+
st.info("No text data available for semantic search.")
|
| 379 |
+
else:
|
| 380 |
+
st.info("No text column available to perform semantic search.")
|
| 381 |
+
|
| 382 |
+
# --------------------------------------------------------------------------------
|
| 383 |
+
# ------------------------ AI-Generated Summary of Posts -------------------------
|
| 384 |
+
# --------------------------------------------------------------------------------
|
| 385 |
st.markdown("## AI-Generated Summary of Posts")
|
| 386 |
if text_col in df.columns:
|
| 387 |
+
# Use the same FLAN-T5 base model or DistilBart for summarization
|
| 388 |
+
summarizer = pipeline("text2text-generation", model="google/flan-t5-base")
|
| 389 |
|
| 390 |
def generate_summary(text, summarizer, max_chunk_length=1000):
|
| 391 |
+
"""
|
| 392 |
+
Break text into chunks of up to max_chunk_length,
|
| 393 |
+
and pass them through the summarizer in sequence,
|
| 394 |
+
then do a final summarization pass on the combined summary.
|
| 395 |
+
"""
|
| 396 |
+
sentences = text.split('. ')
|
| 397 |
chunks, current_chunk = [], ""
|
| 398 |
+
|
| 399 |
+
for sentence in sentences:
|
| 400 |
sentence = sentence.strip() + ". "
|
| 401 |
if len(current_chunk) + len(sentence) <= max_chunk_length:
|
| 402 |
current_chunk += sentence
|
|
|
|
| 406 |
if current_chunk:
|
| 407 |
chunks.append(current_chunk.strip())
|
| 408 |
|
| 409 |
+
# Summarize each chunk
|
| 410 |
+
interim_summaries = []
|
| 411 |
for chunk in chunks:
|
| 412 |
if len(chunk) > 50:
|
| 413 |
+
prompt = f"Summarize this text: {chunk}"
|
| 414 |
+
summary_chunk = summarizer(prompt, max_length=150, do_sample=False)[0]['generated_text']
|
| 415 |
+
interim_summaries.append(summary_chunk)
|
| 416 |
+
|
| 417 |
+
# Summarize the combined interim summary
|
| 418 |
+
combined_summary = " ".join(interim_summaries)
|
| 419 |
+
final_prompt = f"Summarize this overall text: {combined_summary}"
|
| 420 |
+
final_summary = summarizer(final_prompt, max_length=150, do_sample=False)[0]['generated_text']
|
| 421 |
return final_summary
|
| 422 |
|
| 423 |
+
# Take a sample of up to 10 random posts
|
| 424 |
sample_text = " ".join(df[text_col].dropna().sample(n=min(10, len(df)), random_state=42).tolist())
|
| 425 |
+
if sample_text.strip():
|
| 426 |
final_summary = generate_summary(sample_text, summarizer, max_chunk_length=1000)
|
| 427 |
st.write(final_summary)
|
| 428 |
else:
|
|
|
|
| 435 |
# --------------------------------------------------------------------------------
|
| 436 |
st.markdown("### End of Dashboard")
|
| 437 |
st.markdown("""
|
| 438 |
+
This dashboard is a prototype for analyzing Reddit social media data.
|
| 439 |
+
It demonstrates:
|
| 440 |
+
- Trend analysis with a 7-day moving average
|
| 441 |
+
- A user-to-community network diagram
|
| 442 |
+
- Top contributors and hashtags
|
| 443 |
+
- Sentiment analysis
|
| 444 |
+
- Topic embeddings with LDA + t-SNE
|
| 445 |
+
- **GenAI time series summary** (FLAN-T5)
|
| 446 |
+
- **Offline Wikipedia events** integration
|
| 447 |
+
- **Semantic search** with Sentence Transformers
|
| 448 |
+
- **Full AI-generated summary** of posts
|
| 449 |
""")
|