Varriety commited on
Commit
bf1409f
Β·
verified Β·
1 Parent(s): 340f967

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +167 -167
src/streamlit_app.py CHANGED
@@ -28,115 +28,142 @@ img_hero = os.path.join(BASE_DIR, "crypto-currency-concept-830px.png")
28
  img_batch = os.path.join(BASE_DIR, "slice3-1-1536x830.png")
29
 
30
  # ==============================
31
- # KONFIGURASI HALAMAN & TEMA
32
  # ==============================
33
  st.set_page_config(page_title="SKRIPSI - BTS", page_icon="β‚Ώ", layout="wide")
34
 
35
- st.markdown("""
36
- <style>
37
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
38
-
39
- :root {
40
- --bitcoin-orange: #F7931A;
41
- --bitcoin-orange-hover: #E8830C;
42
- --bg-main: #0B0E14;
43
- --bg-secondary: #151A22;
44
- --text-dark: #FFFFFF;
45
- --text-muted: #9CA3AF;
46
- --border-color: #2D3748;
47
- }
48
 
49
- .stApp {
50
- background-color: var(--bg-main);
51
- color: var(--text-dark);
52
- font-family: 'Inter', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
53
- }
54
-
55
- h1, h2, h3, h4, h5, h6 {
56
- color: var(--text-dark) !important;
57
- font-family: 'Inter', sans-serif !important;
58
- font-weight: 700 !important;
59
- letter-spacing: -0.5px;
60
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- .btc-highlight {
63
- color: var(--bitcoin-orange);
64
- }
65
-
66
- div.stButton > button:first-child {
67
- background-color: var(--bitcoin-orange);
68
- color: #ffffff;
69
- border: none;
70
- border-radius: 4px;
71
- padding: 0.6rem 1.5rem;
72
- font-weight: 600;
73
- font-family: 'Inter', sans-serif;
74
- letter-spacing: 0.5px;
75
- text-transform: uppercase;
76
- transition: all 0.2s ease-in-out;
77
- box-shadow: 0 2px 4px rgba(247, 147, 26, 0.2);
78
- }
79
- div.stButton > button:first-child:hover {
80
- background-color: var(--bitcoin-orange-hover);
81
- box-shadow: 0 4px 8px rgba(247, 147, 26, 0.4);
82
- transform: translateY(-1px);
83
- color: #ffffff;
84
- }
85
-
86
- .stTabs [data-baseweb="tab-list"] {
87
- gap: 30px;
88
- border-bottom: 2px solid var(--border-color);
89
- }
90
- .stTabs [data-baseweb="tab"] {
91
- height: 55px;
92
- white-space: pre-wrap;
93
- background-color: transparent;
94
- border-radius: 0px;
95
- color: var(--text-muted);
96
- font-weight: 500;
97
- font-size: 1.05rem;
98
- padding-top: 10px;
99
- padding-bottom: 10px;
100
- }
101
- .stTabs [aria-selected="true"] {
102
- border-bottom: 3px solid var(--bitcoin-orange) !important;
103
- color: var(--text-dark) !important;
104
- font-weight: 700;
105
- }
106
 
107
- .streamlit-expanderHeader {
108
- font-weight: 600;
109
- color: var(--text-dark);
110
- background-color: var(--bg-secondary);
111
- border-radius: 6px;
112
- }
113
-
114
- [data-testid="stMetricValue"] {
115
- color: var(--bitcoin-orange);
116
- font-weight: 700;
117
  }
118
- [data-testid="stMetricLabel"] {
119
- color: var(--text-muted);
120
- font-weight: 600;
 
 
 
121
  }
122
-
123
- hr {
124
- border-color: var(--border-color);
125
- margin: 2rem 0;
126
  }
127
  </style>
128
  """, unsafe_allow_html=True)
129
 
130
- plt.style.use('dark_background')
131
- sns.set_theme(style="darkgrid", rc={
132
- "axes.facecolor": "#151A22",
133
- "figure.facecolor": "#0B0E14",
134
- "axes.edgecolor": "#2D3748",
135
- "text.color": "#FFFFFF",
136
- "xtick.color": "#9CA3AF",
137
- "ytick.color": "#9CA3AF",
138
- "grid.color": "#2D3748"
139
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
  # ==============================
142
  # DOWNLOAD REQUIRED RESOURCES
@@ -194,28 +221,18 @@ def get_daily_label(score):
194
  elif score < -0.05: return 'Negative'
195
  else: return 'Neutral'
196
 
197
- # ==============================
198
- # UI UTAMA STREAMLIT
199
- # ==============================
200
- st.markdown("<br>", unsafe_allow_html=True)
201
-
202
- tab1, tab2 = st.tabs(["πŸ§ͺ Uji Kalimat", "πŸ“Š Analisis Batch Data"])
203
-
204
- # ==============================
205
- # TAB 1: UJI KALIMAT (Layout Web Hero)
206
- # ==============================
207
- with tab1:
208
- st.markdown("<br>", unsafe_allow_html=True)
209
-
210
- # Kolom teks & input di Kiri, Gambar di Kanan
211
  col_text, col_img = st.columns([1.2, 1], gap="large")
212
 
213
  with col_text:
214
  st.markdown("""
215
  <div style="padding-top: 0.5rem;">
216
- <h1 style="font-size: 2.8rem; line-height: 1.2; margin-bottom: 1rem;"><span class="btc-highlight">β‚Ώ</span>itcoin Volatility vs Public Sentiment</h1>
217
- <p style='color: #9CA3AF; font-size: 1.15rem; font-weight: 500;'>Analisis Volatilitas Harga Bitcoin Terhadap Sentimen Publik Pada Platform X Berbasis Python</p>
218
- <p style='color: #6B7280; font-size: 0.95rem; margin-top: 1.5rem; margin-bottom: 2rem; border-left: 4px solid var(--bitcoin-orange); padding-left: 10px;'>
219
  <b>Peneliti:</b> Arya Galuh Saputra (H1D022022)
220
  </p>
221
  </div>
@@ -223,12 +240,9 @@ with tab1:
223
 
224
  user_input = st.text_area("Masukkan Tweet (Bahasa Inggris):", "Great, Bitcoin just crashed another 10% today.", height=130)
225
 
226
- col_btn, col_info = st.columns([1, 1.2])
227
- with col_btn:
228
- analyze_btn = st.button("πŸš€ Analisis Sentimen", use_container_width=True)
229
- with col_info:
230
- with st.expander("ℹ️ Info Model NLP"):
231
- st.caption("5 Metode: VADER, TextBlob, BERTweet, RoBERTa, dan RoBERTa Large.")
232
 
233
  with col_img:
234
  try:
@@ -236,7 +250,6 @@ with tab1:
236
  except Exception as e:
237
  st.error(f"⏳ Menunggu gambar diunggah... (Pastikan file {img_hero} tersedia)")
238
 
239
- # Menampilkan hasil di bawahnya secara full width jika tombol ditekan
240
  if analyze_btn:
241
  st.markdown("<hr>", unsafe_allow_html=True)
242
  st.subheader("πŸ“‹ Hasil Deteksi Sentimen")
@@ -277,13 +290,23 @@ with tab1:
277
  }
278
  st.dataframe(pd.DataFrame(data_test), use_container_width=True, hide_index=True)
279
 
280
- # ==============================
281
- # TAB 2: ANALISIS BATCH (Layout Web Informasi)
282
- # ==============================
283
- with tab2:
284
- st.markdown("<br>", unsafe_allow_html=True)
285
-
286
- # Gambar Monitor di Kiri, Form Upload di Kanan
 
 
 
 
 
 
 
 
 
 
287
  col_img_batch, col_upload = st.columns([1, 1.5], gap="large")
288
 
289
  with col_img_batch:
@@ -295,8 +318,8 @@ with tab2:
295
  with col_upload:
296
  st.markdown("""
297
  <div style="padding-top: 1.5rem;">
298
- <h2 style="font-size: 2.3rem; margin-bottom: 0.5rem; color: #FFFFFF;">πŸ“‚ Analisis Batch Data</h2>
299
- <p style='color: #9CA3AF; font-size: 1.1rem; margin-bottom: 1.5rem;'>Unggah file ekstensi .txt yang berisi history tweet untuk dianalisis secara masal.</p>
300
  </div>
301
  """, unsafe_allow_html=True)
302
 
@@ -305,9 +328,10 @@ with tab2:
305
  with st.expander("πŸ“Œ Lihat Format TXT yang Benar"):
306
  st.code("username | 2024-03-01 14:00:00\nIsi tweet baris pertama di sini\n\nusername2 | 2024-03-01 15:30:00\nIsi tweet baris kedua di sini", language="text")
307
 
 
308
  analyze_batch_btn = st.button("βš™οΈ Eksekusi Analisis", key="batch_btn")
 
309
 
310
- # Bagian pemrosesan data, dieksekusi secara full-width di bawah layout gambar & form
311
  if tweet_files and analyze_batch_btn:
312
  st.markdown("---")
313
  tweet_files = sorted(tweet_files, key=lambda x: x.name)
@@ -325,12 +349,10 @@ with tab2:
325
 
326
  for tweet in tweets:
327
  parts = tweet.strip().split("\n", 1)
328
- if len(parts) != 2:
329
- continue
330
 
331
  meta, text_raw = parts
332
 
333
- # FILTER BAHASA INGGRIS
334
  try:
335
  DetectorFactory.seed = 0
336
  lang = detect(text_raw)
@@ -392,11 +414,7 @@ with tab2:
392
  st.info("πŸ“‘ Mengambil data harga Bitcoin dari CoinGecko API...")
393
 
394
  url = "https://api.coingecko.com/api/v3/coins/bitcoin/market_chart/range"
395
- params = {
396
- "vs_currency": "usd",
397
- "from": start_unix,
398
- "to": end_unix
399
- }
400
  headers = {"accept": "application/json", "User-Agent": "Mozilla/5.0"}
401
 
402
  try:
@@ -427,13 +445,11 @@ with tab2:
427
  st.markdown("---")
428
  st.header("πŸ“‹ Ringkasan Data")
429
 
430
- # TABEL 1: SENTIMEN MENTAH
431
  st.markdown("#### πŸ—£οΈ Data Sentimen Mentah")
432
  st.caption("Tweet asli, hasil preprocessing, dan label prediksi dari 5 model sebelum konversi ke numerik.")
433
  raw_display_cols = ["date", "raw_tweet", "cleaned_tweet", "vader", "textblob", "bertweet", "roberta", "roberta_large"]
434
  st.dataframe(df[raw_display_cols], use_container_width=True, hide_index=True)
435
 
436
- # KONVERSI LABEL SENTIMEN KE ANGKA
437
  sentiment_map = {"positive": 1, "neutral": 0, "negative": -1}
438
  df_score = df.copy()
439
  for col in ["vader", "textblob", "bertweet", "roberta", "roberta_large"]:
@@ -450,19 +466,16 @@ with tab2:
450
  for col in models:
451
  daily_display_cols.extend([col, f"{col}_label"])
452
 
453
- # TABEL 2: SKOR SENTIMEN HARIAN
454
  st.markdown("<br>", unsafe_allow_html=True)
455
  st.markdown("#### πŸ”’ Skor Sentimen Harian")
456
  st.caption("Rata-rata skor sentimen harian yang dikonversi ke representasi metrik.")
457
  st.dataframe(df_sentiment_daily[daily_display_cols], use_container_width=True, hide_index=True)
458
 
459
- # TABEL 3: HARGA BITCOIN
460
  st.markdown("<br>", unsafe_allow_html=True)
461
  st.markdown("#### β‚Ώ Historis Harga & Volatilitas Bitcoin")
462
  st.caption("Data pergerakan rata-rata harga, persentase perubahan, dan Log Return (CoinGecko API).")
463
  st.dataframe(df_price[["date", "price", "pct_change", "log_return"]], use_container_width=True, hide_index=True)
464
 
465
- # MERGE KEDUA TABEL
466
  df_merged = pd.merge(df_price, df_sentiment_daily, on="date", how="inner")
467
 
468
  st.markdown("---")
@@ -470,7 +483,6 @@ with tab2:
470
  final_display_cols = ["date", "price", "pct_change", "log_return"] + [c for c in daily_display_cols if c != "date"]
471
  st.dataframe(df_merged[final_display_cols], use_container_width=True, hide_index=True)
472
 
473
- # Export Buttons
474
  col_dl1, col_dl2, _ = st.columns([1, 1, 2])
475
  csv_data = df_merged.to_csv(index=False).encode('utf-8')
476
  col_dl1.download_button("πŸ“₯ Download CSV", data=csv_data, file_name="sentiment_volatility.csv", mime="text/csv")
@@ -482,9 +494,7 @@ with tab2:
482
 
483
  st.markdown("---")
484
 
485
- # ==============================
486
  # UJI KORELASI PEARSON
487
- # ==============================
488
  st.subheader("πŸ”¬ Uji Korelasi Pearson (Sentiment vs Log Return)")
489
 
490
  with st.expander("πŸ’‘ Dasar Pengambilan Keputusan"):
@@ -507,17 +517,11 @@ with tab2:
507
  "Status": signifikansi
508
  })
509
 
510
- raw_corr_results.append({
511
- "metode": method.upper(),
512
- "r": corr,
513
- "p": pval
514
- })
515
 
516
  st.table(pd.DataFrame(corr_data))
517
 
518
- # ==============================
519
  # LINE CHART
520
- # ==============================
521
  st.markdown("---")
522
  st.subheader("πŸ“ˆ Trend Analisis: Sentiment vs BTC Volatility")
523
 
@@ -529,16 +533,14 @@ with tab2:
529
  for idx, method in enumerate(["vader", "textblob", "roberta", "roberta_large", "bertweet"]):
530
  ax_line.plot(df_merged["date"], df_merged[method], label=f"Sentiment: {method.upper()}", color=colors[idx], linewidth=1.5, linestyle="--", alpha=0.8)
531
 
532
- ax_line.set_title("Pergerakan Rata-Rata Sentimen Harian Terhadap Volatilitas Harga Bitcoin", color="#FFFFFF", fontsize=14, pad=15, fontweight='bold')
533
- ax_line.set_xlabel("Tanggal", color="#9CA3AF", fontsize=11)
534
- ax_line.set_ylabel("Nilai (Value)", color="#9CA3AF", fontsize=11)
535
- ax_line.legend(loc='upper left', bbox_to_anchor=(1, 1), frameon=True, facecolor='#151A22', edgecolor='#2D3748')
536
  plt.tight_layout()
537
  st.pyplot(fig_line)
538
 
539
- # ==============================
540
- # SCATTER PLOT + TRENDLINE
541
- # ==============================
542
  st.markdown("---")
543
  st.subheader("πŸ”΅ Pola Distribusi (Scatter Plot & Trendline)")
544
 
@@ -551,9 +553,9 @@ with tab2:
551
  sns.regplot(data=df_merged, x=method, y="log_return", ax=ax_scatter,
552
  scatter_kws={"s": 50, "color": "#4F46E5", "alpha": 0.6},
553
  line_kws={"color": "#F7931A", "linewidth": 2.5})
554
- ax_scatter.set_title(f"{method.upper()} vs Log Return", color="#FFFFFF", fontweight='bold')
555
- ax_scatter.set_xlabel("Skor Sentimen", color="#9CA3AF")
556
- ax_scatter.set_ylabel("Log Return", color="#9CA3AF")
557
  plt.tight_layout()
558
  st.pyplot(fig_scatter)
559
 
@@ -561,9 +563,7 @@ with tab2:
561
  st.write("- **Garis Orange (Trendline):** Menunjukkan arah korelasi (Naik = Positif, Turun = Negatif).")
562
  st.write("- **Titik Ungu:** Sebaran data, semakin merapat ke garis orange berarti korelasi semakin kuat.")
563
 
564
- # ==============================
565
- # KESIMPULAN & PEMBAHASAN AKHIR
566
- # ==============================
567
  st.markdown("---")
568
  st.header("πŸ“ Ekstraksi Kesimpulan")
569
 
 
28
  img_batch = os.path.join(BASE_DIR, "slice3-1-1536x830.png")
29
 
30
  # ==============================
31
+ # KONFIGURASI HALAMAN & STATE NAVIGASI
32
  # ==============================
33
  st.set_page_config(page_title="SKRIPSI - BTS", page_icon="β‚Ώ", layout="wide")
34
 
35
+ if 'page' not in st.session_state:
36
+ st.session_state.page = "uji_kalimat"
 
 
 
 
 
 
 
 
 
 
 
37
 
38
+ # ==============================
39
+ # INJEKSI CSS DINAMIS (TEMA HALAMAN)
40
+ # ==============================
41
+ if st.session_state.page == "uji_kalimat":
42
+ # TEMA ORANYE (Mirip Vancouver Bitcoin Landing Page)
43
+ st.markdown("""
44
+ <style>
45
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
46
+
47
+ .stApp {
48
+ background-color: #F38220; /* Oranye */
49
+ color: #FFFFFF;
50
+ font-family: 'Inter', sans-serif;
51
+ }
52
+
53
+ h1, h2, h3, h4, h5, h6, p, span, label {
54
+ color: #FFFFFF !important;
55
+ }
56
+
57
+ /* Styling Text Area & Inputs agar kontras di latar oranye */
58
+ .stTextArea textarea {
59
+ background-color: #FFFFFF;
60
+ color: #111827 !important;
61
+ border-radius: 8px;
62
+ }
63
+
64
+ /* Tombol Aksi */
65
+ .action-btn > button {
66
+ background-color: #1E1E24;
67
+ color: #FFFFFF !important;
68
+ border: none;
69
+ border-radius: 25px;
70
+ padding: 0.6rem 2rem;
71
+ font-weight: 600;
72
+ }
73
+ .action-btn > button:hover {
74
+ background-color: #33333C;
75
+ }
76
+ </style>
77
+ """, unsafe_allow_html=True)
78
 
79
+ else:
80
+ # TEMA PUTIH/KREM (Mirip Vancouver Bitcoin Info Page)
81
+ st.markdown("""
82
+ <style>
83
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
84
+
85
+ .stApp {
86
+ background-color: #FFFDF5; /* Krem / Putih Gading */
87
+ color: #111827;
88
+ font-family: 'Inter', sans-serif;
89
+ }
90
+
91
+ h1, h2, h3, h4, h5, h6, label {
92
+ color: #111827 !important;
93
+ }
94
+
95
+ p, span {
96
+ color: #4B5563 !important;
97
+ }
98
+
99
+ hr { border-color: #E5E7EB; }
100
+
101
+ /* Tombol Aksi */
102
+ .action-btn > button {
103
+ background-color: #312E81;
104
+ color: #FFFFFF !important;
105
+ border: none;
106
+ border-radius: 25px;
107
+ padding: 0.6rem 2rem;
108
+ font-weight: 600;
109
+ }
110
+ .action-btn > button:hover {
111
+ background-color: #4338CA;
112
+ }
113
+
114
+ /* Expander Header Text */
115
+ .streamlit-expanderHeader { color: #111827 !important; }
116
+ </style>
117
+ """, unsafe_allow_html=True)
 
 
 
 
 
118
 
119
+ # CSS Global untuk Navigasi (Tetap sama di kedua tema)
120
+ st.markdown("""
121
+ <style>
122
+ .nav-container {
123
+ padding: 10px 0;
124
+ border-bottom: 2px solid rgba(255, 255, 255, 0.2);
125
+ margin-bottom: 20px;
 
 
 
126
  }
127
+ .nav-btn > button {
128
+ background-color: transparent !important;
129
+ border: 2px solid currentColor !important;
130
+ border-radius: 20px !important;
131
+ font-weight: 600 !important;
132
+ transition: all 0.3s ease;
133
  }
134
+ .nav-btn > button:hover {
135
+ opacity: 0.8;
136
+ transform: translateY(-2px);
 
137
  }
138
  </style>
139
  """, unsafe_allow_html=True)
140
 
141
+ # ==============================
142
+ # FUNGSI NAVIGASI
143
+ # ==============================
144
+ def set_page(page_name):
145
+ st.session_state.page = page_name
146
+
147
+ # Header Navigasi (Sistem Berpindah Halaman)
148
+ col_logo, col_nav1, col_nav2, col_space = st.columns([2, 1.5, 1.5, 4])
149
+ with col_logo:
150
+ st.markdown("<h3 style='margin:0; padding:0;'>β‚Ώitcoin Sentimen</h3>", unsafe_allow_html=True)
151
+
152
+ with col_nav1:
153
+ st.markdown('<div class="nav-btn">', unsafe_allow_html=True)
154
+ if st.button("Uji Kalimat πŸ“", use_container_width=True):
155
+ set_page("uji_kalimat")
156
+ st.rerun()
157
+ st.markdown('</div>', unsafe_allow_html=True)
158
+
159
+ with col_nav2:
160
+ st.markdown('<div class="nav-btn">', unsafe_allow_html=True)
161
+ if st.button("Analisis Batch πŸ“Š", use_container_width=True):
162
+ set_page("analisis_batch")
163
+ st.rerun()
164
+ st.markdown('</div>', unsafe_allow_html=True)
165
+
166
+ st.markdown("<div style='margin-bottom: 40px;'></div>", unsafe_allow_html=True)
167
 
168
  # ==============================
169
  # DOWNLOAD REQUIRED RESOURCES
 
221
  elif score < -0.05: return 'Negative'
222
  else: return 'Neutral'
223
 
224
+ # ==============================================================================
225
+ # HALAMAN 1: UJI KALIMAT (BACKGROUND ORANYE)
226
+ # ==============================================================================
227
+ if st.session_state.page == "uji_kalimat":
 
 
 
 
 
 
 
 
 
 
228
  col_text, col_img = st.columns([1.2, 1], gap="large")
229
 
230
  with col_text:
231
  st.markdown("""
232
  <div style="padding-top: 0.5rem;">
233
+ <h1 style="font-size: 3rem; line-height: 1.2; margin-bottom: 1rem; color: white;">Bitcoin Volatility vs Public Sentiment</h1>
234
+ <p style='font-size: 1.15rem; font-weight: 500;'>Analisis Volatilitas Harga Bitcoin Terhadap Sentimen Publik Pada Platform X Berbasis Python</p>
235
+ <p style='font-size: 0.95rem; margin-top: 1.5rem; margin-bottom: 2rem; border-left: 4px solid white; padding-left: 10px;'>
236
  <b>Peneliti:</b> Arya Galuh Saputra (H1D022022)
237
  </p>
238
  </div>
 
240
 
241
  user_input = st.text_area("Masukkan Tweet (Bahasa Inggris):", "Great, Bitcoin just crashed another 10% today.", height=130)
242
 
243
+ st.markdown('<div class="action-btn">', unsafe_allow_html=True)
244
+ analyze_btn = st.button("πŸš€ Analisis Sentimen", use_container_width=True)
245
+ st.markdown('</div>', unsafe_allow_html=True)
 
 
 
246
 
247
  with col_img:
248
  try:
 
250
  except Exception as e:
251
  st.error(f"⏳ Menunggu gambar diunggah... (Pastikan file {img_hero} tersedia)")
252
 
 
253
  if analyze_btn:
254
  st.markdown("<hr>", unsafe_allow_html=True)
255
  st.subheader("πŸ“‹ Hasil Deteksi Sentimen")
 
290
  }
291
  st.dataframe(pd.DataFrame(data_test), use_container_width=True, hide_index=True)
292
 
293
+
294
+ # ==============================================================================
295
+ # HALAMAN 2: ANALISIS BATCH DATA (BACKGROUND PUTIH/KREM)
296
+ # ==============================================================================
297
+ elif st.session_state.page == "analisis_batch":
298
+ # Ubah tema grafik matplotlib agar sesuai dengan background terang
299
+ plt.style.use('default')
300
+ sns.set_theme(style="whitegrid", rc={
301
+ "axes.facecolor": "#FFFFFF",
302
+ "figure.facecolor": "#FFFDF5",
303
+ "axes.edgecolor": "#E5E7EB",
304
+ "text.color": "#111827",
305
+ "xtick.color": "#4B5563",
306
+ "ytick.color": "#4B5563",
307
+ "grid.color": "#F3F4F6"
308
+ })
309
+
310
  col_img_batch, col_upload = st.columns([1, 1.5], gap="large")
311
 
312
  with col_img_batch:
 
318
  with col_upload:
319
  st.markdown("""
320
  <div style="padding-top: 1.5rem;">
321
+ <h2 style="font-size: 2.5rem; margin-bottom: 0.5rem; color: #111827;">πŸ“‚ Analisis Batch Data</h2>
322
+ <p style='font-size: 1.1rem; margin-bottom: 1.5rem;'>Unggah file ekstensi .txt yang berisi history tweet untuk dianalisis secara masal.</p>
323
  </div>
324
  """, unsafe_allow_html=True)
325
 
 
328
  with st.expander("πŸ“Œ Lihat Format TXT yang Benar"):
329
  st.code("username | 2024-03-01 14:00:00\nIsi tweet baris pertama di sini\n\nusername2 | 2024-03-01 15:30:00\nIsi tweet baris kedua di sini", language="text")
330
 
331
+ st.markdown('<div class="action-btn">', unsafe_allow_html=True)
332
  analyze_batch_btn = st.button("βš™οΈ Eksekusi Analisis", key="batch_btn")
333
+ st.markdown('</div>', unsafe_allow_html=True)
334
 
 
335
  if tweet_files and analyze_batch_btn:
336
  st.markdown("---")
337
  tweet_files = sorted(tweet_files, key=lambda x: x.name)
 
349
 
350
  for tweet in tweets:
351
  parts = tweet.strip().split("\n", 1)
352
+ if len(parts) != 2: continue
 
353
 
354
  meta, text_raw = parts
355
 
 
356
  try:
357
  DetectorFactory.seed = 0
358
  lang = detect(text_raw)
 
414
  st.info("πŸ“‘ Mengambil data harga Bitcoin dari CoinGecko API...")
415
 
416
  url = "https://api.coingecko.com/api/v3/coins/bitcoin/market_chart/range"
417
+ params = {"vs_currency": "usd", "from": start_unix, "to": end_unix}
 
 
 
 
418
  headers = {"accept": "application/json", "User-Agent": "Mozilla/5.0"}
419
 
420
  try:
 
445
  st.markdown("---")
446
  st.header("πŸ“‹ Ringkasan Data")
447
 
 
448
  st.markdown("#### πŸ—£οΈ Data Sentimen Mentah")
449
  st.caption("Tweet asli, hasil preprocessing, dan label prediksi dari 5 model sebelum konversi ke numerik.")
450
  raw_display_cols = ["date", "raw_tweet", "cleaned_tweet", "vader", "textblob", "bertweet", "roberta", "roberta_large"]
451
  st.dataframe(df[raw_display_cols], use_container_width=True, hide_index=True)
452
 
 
453
  sentiment_map = {"positive": 1, "neutral": 0, "negative": -1}
454
  df_score = df.copy()
455
  for col in ["vader", "textblob", "bertweet", "roberta", "roberta_large"]:
 
466
  for col in models:
467
  daily_display_cols.extend([col, f"{col}_label"])
468
 
 
469
  st.markdown("<br>", unsafe_allow_html=True)
470
  st.markdown("#### πŸ”’ Skor Sentimen Harian")
471
  st.caption("Rata-rata skor sentimen harian yang dikonversi ke representasi metrik.")
472
  st.dataframe(df_sentiment_daily[daily_display_cols], use_container_width=True, hide_index=True)
473
 
 
474
  st.markdown("<br>", unsafe_allow_html=True)
475
  st.markdown("#### β‚Ώ Historis Harga & Volatilitas Bitcoin")
476
  st.caption("Data pergerakan rata-rata harga, persentase perubahan, dan Log Return (CoinGecko API).")
477
  st.dataframe(df_price[["date", "price", "pct_change", "log_return"]], use_container_width=True, hide_index=True)
478
 
 
479
  df_merged = pd.merge(df_price, df_sentiment_daily, on="date", how="inner")
480
 
481
  st.markdown("---")
 
483
  final_display_cols = ["date", "price", "pct_change", "log_return"] + [c for c in daily_display_cols if c != "date"]
484
  st.dataframe(df_merged[final_display_cols], use_container_width=True, hide_index=True)
485
 
 
486
  col_dl1, col_dl2, _ = st.columns([1, 1, 2])
487
  csv_data = df_merged.to_csv(index=False).encode('utf-8')
488
  col_dl1.download_button("πŸ“₯ Download CSV", data=csv_data, file_name="sentiment_volatility.csv", mime="text/csv")
 
494
 
495
  st.markdown("---")
496
 
 
497
  # UJI KORELASI PEARSON
 
498
  st.subheader("πŸ”¬ Uji Korelasi Pearson (Sentiment vs Log Return)")
499
 
500
  with st.expander("πŸ’‘ Dasar Pengambilan Keputusan"):
 
517
  "Status": signifikansi
518
  })
519
 
520
+ raw_corr_results.append({"metode": method.upper(), "r": corr, "p": pval})
 
 
 
 
521
 
522
  st.table(pd.DataFrame(corr_data))
523
 
 
524
  # LINE CHART
 
525
  st.markdown("---")
526
  st.subheader("πŸ“ˆ Trend Analisis: Sentiment vs BTC Volatility")
527
 
 
533
  for idx, method in enumerate(["vader", "textblob", "roberta", "roberta_large", "bertweet"]):
534
  ax_line.plot(df_merged["date"], df_merged[method], label=f"Sentiment: {method.upper()}", color=colors[idx], linewidth=1.5, linestyle="--", alpha=0.8)
535
 
536
+ ax_line.set_title("Pergerakan Rata-Rata Sentimen Harian Terhadap Volatilitas Harga Bitcoin", color="#111827", fontsize=14, pad=15, fontweight='bold')
537
+ ax_line.set_xlabel("Tanggal", color="#4B5563", fontsize=11)
538
+ ax_line.set_ylabel("Nilai (Value)", color="#4B5563", fontsize=11)
539
+ ax_line.legend(loc='upper left', bbox_to_anchor=(1, 1), frameon=True, facecolor='#FFFFFF', edgecolor='#E5E7EB')
540
  plt.tight_layout()
541
  st.pyplot(fig_line)
542
 
543
+ # SCATTER PLOT
 
 
544
  st.markdown("---")
545
  st.subheader("πŸ”΅ Pola Distribusi (Scatter Plot & Trendline)")
546
 
 
553
  sns.regplot(data=df_merged, x=method, y="log_return", ax=ax_scatter,
554
  scatter_kws={"s": 50, "color": "#4F46E5", "alpha": 0.6},
555
  line_kws={"color": "#F7931A", "linewidth": 2.5})
556
+ ax_scatter.set_title(f"{method.upper()} vs Log Return", color="#111827", fontweight='bold')
557
+ ax_scatter.set_xlabel("Skor Sentimen", color="#4B5563")
558
+ ax_scatter.set_ylabel("Log Return", color="#4B5563")
559
  plt.tight_layout()
560
  st.pyplot(fig_scatter)
561
 
 
563
  st.write("- **Garis Orange (Trendline):** Menunjukkan arah korelasi (Naik = Positif, Turun = Negatif).")
564
  st.write("- **Titik Ungu:** Sebaran data, semakin merapat ke garis orange berarti korelasi semakin kuat.")
565
 
566
+ # KESIMPULAN
 
 
567
  st.markdown("---")
568
  st.header("πŸ“ Ekstraksi Kesimpulan")
569