tjl8 commited on
Commit
c422248
·
verified ·
1 Parent(s): 2d134c2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -131
app.py CHANGED
@@ -155,6 +155,7 @@ import streamlit as st
155
  # Set page config as the first Streamlit command
156
  st.set_page_config(layout="wide", page_title="AskReddit Data Explorer")
157
 
 
158
  import pandas as pd
159
  import matplotlib.pyplot as plt
160
  from nltk.sentiment.vader import SentimentIntensityAnalyzer
@@ -164,73 +165,51 @@ import altair as alt
164
  from datetime import datetime
165
  import praw
166
 
167
- # Custom CSS for a more aesthetic dashboard
168
  st.markdown("""
169
  <style>
170
- /* Main title styling */
171
- .main-title {
172
- color: #2196F3;
173
- text-align: center;
174
- font-size: 3em;
175
- font-weight: bold;
176
- padding: 20px 0;
177
- margin-bottom: 30px;
178
  }
179
-
180
- /* Plot container styling */
181
- .plot-container {
182
- background: white;
183
  padding: 20px;
184
  border-radius: 10px;
185
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
186
- margin: 10px;
187
- border: 2px solid #2196F3;
188
  }
189
-
190
- /* Plot title styling */
191
- .plot-title {
192
- color: #1976D2;
193
- font-size: 1.5em;
194
- font-weight: bold;
195
- margin-bottom: 20px;
 
 
196
  padding-bottom: 10px;
197
- border-bottom: 2px solid #E3F2FD;
198
  }
199
-
200
- /* Button styling */
201
- .stButton > button {
202
- background-color: #2196F3;
203
  color: white;
204
- border: none;
205
- border-radius: 5px;
206
- padding: 10px 20px;
207
- font-weight: bold;
208
  }
209
-
210
- .stButton > button:hover {
211
- background-color: #1976D2;
212
- }
213
-
214
- /* Selectbox styling */
215
- .stSelectbox {
216
- border: 2px solid #2196F3;
217
- border-radius: 5px;
218
- }
219
-
220
- /* Chart border styling */
221
- .chart-wrapper {
222
- border: 2px solid #2196F3;
223
  border-radius: 5px;
224
- padding: 10px;
225
- margin-top: 10px;
226
  }
227
  </style>
228
  """, unsafe_allow_html=True)
229
 
230
- # Title with custom styling
231
- st.markdown('<h1 class="main-title">AskReddit Dashboard</h1>', unsafe_allow_html=True)
232
 
233
- # Initialize Reddit API client
 
 
 
 
234
  reddit = praw.Reddit(
235
  client_id="dFhdA1_6NWavjmAEt83r2A",
236
  client_secret="l4UsnweG9HdHLffJ9ZbAow0-HRocWA",
@@ -245,6 +224,8 @@ def fetch_askreddit_posts(limit=100):
245
  posts.append({
246
  'title': post.title,
247
  'score': post.score,
 
 
248
  'num_comments': post.num_comments,
249
  'num_crossposts': post.num_crossposts,
250
  'created_utc': datetime.fromtimestamp(post.created_utc),
@@ -255,40 +236,34 @@ def fetch_askreddit_posts(limit=100):
255
  })
256
  return pd.DataFrame(posts)
257
 
 
258
  df = fetch_askreddit_posts()
259
 
260
- # Create layout
261
  row1_col1, row1_col2 = st.columns(2)
262
  row2_col1, row2_col2 = st.columns(2)
263
 
264
- # Plot 1: Time Series
265
  with row1_col1:
266
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
267
- st.markdown('<p class="plot-title">Number of Posts vs Time of Day</p>', unsafe_allow_html=True)
268
  days_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
269
  selected_day = st.selectbox('Select Day of the Week:', days_order)
270
-
271
  plot_data = df[df['day_of_week'] == selected_day].groupby('hour').size().reset_index(name='count')
272
- chart = alt.Chart(plot_data).mark_line(
273
- point=True,
274
- color='#2196F3'
275
- ).encode(
276
  x=alt.X('hour:Q', title='Hour of Day'),
277
  y=alt.Y('count:Q', title='Number of Posts'),
278
  tooltip=['hour', 'count']
279
- ).properties(height=300).configure_view(
280
- strokeWidth=2,
281
- stroke='#E3F2FD'
282
- )
283
- st.markdown('<div class="chart-wrapper">', unsafe_allow_html=True)
284
  st.altair_chart(chart, use_container_width=True)
285
  st.markdown('</div>', unsafe_allow_html=True)
286
- st.markdown('</div>', unsafe_allow_html=True)
287
 
288
- # Plot 2: Scatter Plot
289
  with row1_col2:
290
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
291
- st.markdown('<p class="plot-title">Comments vs Score Analysis</p>', unsafe_allow_html=True)
292
 
293
  col1, col2 = st.columns(2)
294
  with col1:
@@ -298,105 +273,59 @@ with row1_col2:
298
 
299
  x_axis = 'num_crossposts' if crossposts_button else 'num_comments'
300
 
301
- scatter_chart = alt.Chart(df).mark_circle(
302
- color='#2196F3'
303
- ).encode(
304
  x=alt.X(f'{x_axis}:Q', title=x_axis.replace('_', ' ').title()),
305
  y=alt.Y('score:Q', title='Score'),
306
  tooltip=['title', x_axis, 'score']
307
- ).properties(height=300).configure_view(
308
- strokeWidth=2,
309
- stroke='#E3F2FD'
310
  )
311
 
312
- st.markdown('<div class="chart-wrapper">', unsafe_allow_html=True)
313
  st.altair_chart(scatter_chart, use_container_width=True)
314
  st.markdown('</div>', unsafe_allow_html=True)
315
- st.markdown('</div>', unsafe_allow_html=True)
316
 
317
- # Plot 3: Word Cloud
318
  with row2_col1:
319
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
320
- st.markdown('<p class="plot-title">Word Cloud by Sentiment</p>', unsafe_allow_html=True)
321
-
322
  analyzer = SentimentIntensityAnalyzer()
323
  df['sentiment'] = df['title'].apply(lambda x: analyzer.polarity_scores(x)['compound'])
324
  df['sentiment_category'] = df['sentiment'].apply(
325
  lambda score: 'positive' if score > 0.05 else 'negative' if score < -0.05 else 'neutral'
326
  )
327
-
328
  selected_sentiment = st.selectbox("Select Sentiment", ['positive', 'negative', 'neutral'])
329
  filtered_titles = " ".join(df[df['sentiment_category'] == selected_sentiment]['title'])
330
-
331
- wordcloud = WordCloud(
332
- width=800,
333
- height=400,
334
- background_color='white',
335
- colormap='Blues'
336
- ).generate(filtered_titles)
337
-
338
  fig, ax = plt.subplots(figsize=(10, 6))
339
  ax.imshow(wordcloud, interpolation='bilinear')
340
  ax.axis('off')
341
- ax.set_facecolor('white')
342
- fig.patch.set_facecolor('white')
343
-
344
- st.markdown('<div class="chart-wrapper">', unsafe_allow_html=True)
345
  st.pyplot(fig)
346
  st.markdown('</div>', unsafe_allow_html=True)
347
- st.markdown('</div>', unsafe_allow_html=True)
348
 
349
- # Plot 4: Bubble Chart
350
  with row2_col2:
351
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
352
- st.markdown('<p class="plot-title">Author Performance Analysis</p>', unsafe_allow_html=True)
353
-
354
  bubble_chart = alt.Chart(df).mark_circle().encode(
355
  x=alt.X('num_comments:Q', title='Number of Comments'),
356
  y=alt.Y('score:Q', title='Score'),
357
- size=alt.Size('upvote_ratio:Q', scale=alt.Scale(range=[20, 200])),
358
- color=alt.Color('upvote_ratio:Q', scale=alt.Scale(scheme='blues')),
359
  tooltip=['author', 'num_comments', 'score', 'upvote_ratio']
360
  ).properties(
361
- width=400,
362
- height=300
363
- ).configure_view(
364
- strokeWidth=2,
365
- stroke='#E3F2FD'
366
  )
367
-
368
- st.markdown('<div class="chart-wrapper">', unsafe_allow_html=True)
369
- st.altair_chart(bubble_chart, use_container_width=True)
370
- st.markdown('</div>', unsafe_allow_html=True)
371
  st.markdown('</div>', unsafe_allow_html=True)
372
 
373
  # Display raw data
374
  if st.checkbox("Show Raw Data"):
375
  st.write(df)
376
 
377
- # Summary section
378
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
379
- st.markdown('<p class="plot-title">Project Summary</p>', unsafe_allow_html=True)
380
- st.markdown("""
381
- This project used dataset from the r/AskReddit subreddit. It includes various fields such as post scores,
382
- the number of comments, upvote ratios, and timestamps, providing a comprehensive view of post performance
383
- and user engagement. The dataset was explored through several interactive visualizations designed to reveal
384
- key insights effectively.
385
-
386
- A line graph was used to display the number of posts by the hour for each day of the week, with a dropdown
387
- menu allowing users to select specific days, making it easy to identify and compare posting patterns such
388
- as peak activity hours.
389
-
390
- A scatter plot illustrated the correlation between post scores and other engagement metrics like the number
391
- of comments and crossposts, with interactive buttons enabling users to switch the x-axis variable and examine
392
- relationships influencing post success.
393
-
394
- A word cloud incorporating sentiment analysis enriched the exploration by categorizing post titles as positive,
395
- negative, or neutral using VADER sentiment analysis. Words were color-coded based on their sentiment, and users
396
- could toggle between categories and explore different color palettes.
397
-
398
- Lastly, a bubble chart provided a unique perspective on individual author performance, with bubbles representing
399
- authors, their size reflecting the upvote ratio, and their position showing the number of comments and post scores,
400
- offering an engaging way to analyze user contributions.
401
- """)
402
- st.markdown('</div>', unsafe_allow_html=True)
 
155
  # Set page config as the first Streamlit command
156
  st.set_page_config(layout="wide", page_title="AskReddit Data Explorer")
157
 
158
+ import os
159
  import pandas as pd
160
  import matplotlib.pyplot as plt
161
  from nltk.sentiment.vader import SentimentIntensityAnalyzer
 
165
  from datetime import datetime
166
  import praw
167
 
168
+ # Custom CSS to beautify the app
169
  st.markdown("""
170
  <style>
171
+ .reportview-container {
172
+ background: #f0f2f6
 
 
 
 
 
 
173
  }
174
+ .main {
175
+ background: #ffffff;
 
 
176
  padding: 20px;
177
  border-radius: 10px;
178
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
 
 
179
  }
180
+ h1 {
181
+ color: #1E88E5;
182
+ text-align: center;
183
+ padding-bottom: 20px;
184
+ border-bottom: 2px solid #1E88E5;
185
+ }
186
+ h2 {
187
+ color: #333;
188
+ border-bottom: 1px solid #ddd;
189
  padding-bottom: 10px;
 
190
  }
191
+ .stButton>button {
192
+ background-color: #1E88E5;
 
 
193
  color: white;
 
 
 
 
194
  }
195
+ .plot-container {
196
+ background: #ffffff;
197
+ padding: 20px;
 
 
 
 
 
 
 
 
 
 
 
198
  border-radius: 5px;
199
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
200
+ margin-bottom: 20px;
201
  }
202
  </style>
203
  """, unsafe_allow_html=True)
204
 
205
+ # Streamlit App Title
206
+ st.title("AskReddit Dashboard")
207
 
208
+ # Download NLTK resources
209
+ nltk.download('vader_lexicon', quiet=True)
210
+ nltk.download('stopwords', quiet=True)
211
+
212
+ # Initialize the Reddit API client
213
  reddit = praw.Reddit(
214
  client_id="dFhdA1_6NWavjmAEt83r2A",
215
  client_secret="l4UsnweG9HdHLffJ9ZbAow0-HRocWA",
 
224
  posts.append({
225
  'title': post.title,
226
  'score': post.score,
227
+ 'id': post.id,
228
+ 'url': post.url,
229
  'num_comments': post.num_comments,
230
  'num_crossposts': post.num_crossposts,
231
  'created_utc': datetime.fromtimestamp(post.created_utc),
 
236
  })
237
  return pd.DataFrame(posts)
238
 
239
+ # Fetch data
240
  df = fetch_askreddit_posts()
241
 
242
+ # Create two rows with two columns each
243
  row1_col1, row1_col2 = st.columns(2)
244
  row2_col1, row2_col2 = st.columns(2)
245
 
246
+ # Plot 1: Number of Posts vs Time of Day
247
  with row1_col1:
248
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
249
+ st.subheader("Number of Posts vs Time of Day")
250
  days_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
251
  selected_day = st.selectbox('Select Day of the Week:', days_order)
 
252
  plot_data = df[df['day_of_week'] == selected_day].groupby('hour').size().reset_index(name='count')
253
+
254
+ chart = alt.Chart(plot_data).mark_line(point=True).encode(
 
 
255
  x=alt.X('hour:Q', title='Hour of Day'),
256
  y=alt.Y('count:Q', title='Number of Posts'),
257
  tooltip=['hour', 'count']
258
+ ).properties(title=f'Posts on {selected_day}', height=300)
259
+
 
 
 
260
  st.altair_chart(chart, use_container_width=True)
261
  st.markdown('</div>', unsafe_allow_html=True)
 
262
 
263
+ # Plot 2: Scatter Plot: Comments vs Score
264
  with row1_col2:
265
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
266
+ st.subheader("Scatter Plot of Comments or Crossposts vs Score")
267
 
268
  col1, col2 = st.columns(2)
269
  with col1:
 
273
 
274
  x_axis = 'num_crossposts' if crossposts_button else 'num_comments'
275
 
276
+ scatter_chart = alt.Chart(df).mark_circle().encode(
 
 
277
  x=alt.X(f'{x_axis}:Q', title=x_axis.replace('_', ' ').title()),
278
  y=alt.Y('score:Q', title='Score'),
279
  tooltip=['title', x_axis, 'score']
280
+ ).properties(
281
+ height=300,
282
+ title=f'Correlation between {x_axis.replace("_", " ").title()} and Score'
283
  )
284
 
 
285
  st.altair_chart(scatter_chart, use_container_width=True)
286
  st.markdown('</div>', unsafe_allow_html=True)
 
287
 
288
+ # Plot 3: Word Cloud by Sentiment
289
  with row2_col1:
290
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
291
+ st.subheader("Word Cloud by Sentiment")
 
292
  analyzer = SentimentIntensityAnalyzer()
293
  df['sentiment'] = df['title'].apply(lambda x: analyzer.polarity_scores(x)['compound'])
294
  df['sentiment_category'] = df['sentiment'].apply(
295
  lambda score: 'positive' if score > 0.05 else 'negative' if score < -0.05 else 'neutral'
296
  )
 
297
  selected_sentiment = st.selectbox("Select Sentiment", ['positive', 'negative', 'neutral'])
298
  filtered_titles = " ".join(df[df['sentiment_category'] == selected_sentiment]['title'])
299
+ wordcloud = WordCloud(width=400, height=150, background_color='white').generate(filtered_titles)
 
 
 
 
 
 
 
300
  fig, ax = plt.subplots(figsize=(10, 6))
301
  ax.imshow(wordcloud, interpolation='bilinear')
302
  ax.axis('off')
 
 
 
 
303
  st.pyplot(fig)
304
  st.markdown('</div>', unsafe_allow_html=True)
 
305
 
306
+ # Plot 4: Author Performance Bubble Chart
307
  with row2_col2:
308
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
309
+ st.subheader("Author Performance Bubble Chart")
 
310
  bubble_chart = alt.Chart(df).mark_circle().encode(
311
  x=alt.X('num_comments:Q', title='Number of Comments'),
312
  y=alt.Y('score:Q', title='Score'),
313
+ size=alt.Size('upvote_ratio:Q', title='Upvote Ratio', scale=alt.Scale(range=[20, 200])),
314
+ color=alt.Color('author:N', legend=None),
315
  tooltip=['author', 'num_comments', 'score', 'upvote_ratio']
316
  ).properties(
317
+ width=600,
318
+ height=350
 
 
 
319
  )
320
+ st.altair_chart(bubble_chart, use_container_width=False)
 
 
 
321
  st.markdown('</div>', unsafe_allow_html=True)
322
 
323
  # Display raw data
324
  if st.checkbox("Show Raw Data"):
325
  st.write(df)
326
 
327
+ # Summary
328
  st.markdown('<div class="plot-container">', unsafe_allow_html=True)
329
+ st.markdown("### Project Summary")
330
+ st.markdown("This project used dataset from the r/AskReddit subreddit. It includes various fields such as post scores, the number of comments, upvote ratios, and timestamps, providing a comprehensive view of post performance and user engagement. The dataset was explored through several interactive visualizations designed to reveal key insights effectively. A line graph was used to display the number of posts by the hour for each day of the week, with a dropdown menu allowing users to select specific days, making it easy to identify and compare posting patterns such as peak activity hours. A scatter plot illustrated the correlation between post scores and other engagement metrics like the number of comments and crossposts, with interactive radio buttons enabling users to switch the x-axis variable and examine relationships influencing post success. A word cloud incorporating sentiment analysis enriched the exploration by categorizing post titles as positive, negative, or neutral using VADER sentiment analysis. Words were color-coded based on their sentiment, and users could toggle between categories and explore different color palettes, making the visualization both informative and visually appealing. Lastly, a bubble chart provided a unique perspective on individual author performance, with bubbles representing authors, their size reflecting the upvote ratio, and their position showing the number of comments and post scores, offering an engaging way to analyze user contributions.")
331
+ st.markdown('</div>', unsafe_allow_html=True)