Update src/streamlit_app.py
Browse files- src/streamlit_app.py +53 -24
src/streamlit_app.py
CHANGED
|
@@ -446,7 +446,7 @@ def clean_text(text):
|
|
| 446 |
text = text.lower()
|
| 447 |
text = re.sub(r"http\S+", "", text)
|
| 448 |
text = re.sub(r"@\w+", "", text)
|
| 449 |
-
text = re.sub(r"#\w+", "", text)
|
| 450 |
text = re.sub(r"[^\w\s]", "", text)
|
| 451 |
text = re.sub(r"\b\d+\b", "", text)
|
| 452 |
text = re.sub(r"\s+", " ", text).strip()
|
|
@@ -466,6 +466,7 @@ def classify_tb(score):
|
|
| 466 |
if score < -TEXTBLOB_THRESHOLD: return 'negative'
|
| 467 |
return 'neutral'
|
| 468 |
|
|
|
|
| 469 |
def map_roberta(label):
|
| 470 |
return {"LABEL_0": "negative", "LABEL_1": "neutral", "LABEL_2": "positive"}.get(label, "neutral")
|
| 471 |
|
|
@@ -606,27 +607,48 @@ if st.session_state.page == "uji_kalimat":
|
|
| 606 |
|
| 607 |
for i, (method, label, detail) in enumerate(results):
|
| 608 |
col = col_a if i % 2 == 0 else col_b
|
|
|
|
| 609 |
bg = badge_color(label)
|
| 610 |
tc = badge_text_color(label)
|
| 611 |
icon = "↗" if label == "positive" else ("↘" if label == "negative" else "→")
|
| 612 |
-
detail_html =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 613 |
with col:
|
| 614 |
-
st.markdown(
|
| 615 |
-
<div style="background:#FAFAFA;border:1px solid #e2e8f0;border-left:4px solid {'#10b981' if label=='positive' else ('#f43f5e' if label=='negative' else '#cbd5e1')};
|
| 616 |
-
border-radius:12px;padding:1rem 1.2rem;margin-bottom:1rem;
|
| 617 |
-
display:flex;align-items:center;justify-content:space-between;
|
| 618 |
-
box-shadow:0 2px 4px rgba(0,0,0,0.02);">
|
| 619 |
-
<div>
|
| 620 |
-
<div style="font-weight:600;font-size:0.75rem;color:#64748b;margin-bottom:4px;">{method}</div>
|
| 621 |
-
<div style="font-weight:800;font-size:1.05rem;color:#0f172a;">{label.capitalize()}</div>
|
| 622 |
-
{detail_html}
|
| 623 |
-
</div>
|
| 624 |
-
<div style="background:{bg};color:{tc};font-size:0.75rem;font-weight:700;
|
| 625 |
-
padding:6px 12px;border-radius:50px;">
|
| 626 |
-
{icon} {label.upper()}
|
| 627 |
-
</div>
|
| 628 |
-
</div>
|
| 629 |
-
""", unsafe_allow_html=True)
|
| 630 |
|
| 631 |
with st.expander("🔍 Lihat teks setelah preprocessing"):
|
| 632 |
st.code(text if text.strip() else "(kosong setelah dibersihkan)", language=None)
|
|
@@ -849,15 +871,20 @@ elif st.session_state.page == "analisis_batch":
|
|
| 849 |
raw_display_cols = ["date","raw_tweet","vader","textblob","bertweet","roberta","roberta_large","vader_score","tb_score"]
|
| 850 |
st.dataframe(df[raw_display_cols], use_container_width=True, hide_index=True)
|
| 851 |
|
|
|
|
|
|
|
|
|
|
| 852 |
sentiment_map = {"positive": 1, "neutral": 0, "negative": -1}
|
| 853 |
df_score = df.copy()
|
| 854 |
models_cat = ["vader","textblob","bertweet","roberta","roberta_large"]
|
| 855 |
for col in models_cat:
|
| 856 |
df_score[col] = df_score[col].map(sentiment_map)
|
| 857 |
|
|
|
|
| 858 |
df_sentiment_daily = df_score.groupby("date")[models_cat].mean().reset_index()
|
| 859 |
df_sentiment_daily["date"] = pd.to_datetime(df_sentiment_daily["date"]).dt.date
|
| 860 |
|
|
|
|
| 861 |
df_numeric_daily = df.groupby("date")[["vader_score","tb_score"]].mean().reset_index()
|
| 862 |
df_numeric_daily["date"] = pd.to_datetime(df_numeric_daily["date"]).dt.date
|
| 863 |
|
|
@@ -897,7 +924,7 @@ elif st.session_state.page == "analisis_batch":
|
|
| 897 |
st.markdown("<hr class='vbc-divider'>", unsafe_allow_html=True)
|
| 898 |
|
| 899 |
# ==================================================
|
| 900 |
-
# UJI KORELASI PEARSON
|
| 901 |
# ==================================================
|
| 902 |
st.subheader("🔬 Uji Korelasi Pearson")
|
| 903 |
st.caption(
|
|
@@ -908,11 +935,11 @@ elif st.session_state.page == "analisis_batch":
|
|
| 908 |
)
|
| 909 |
|
| 910 |
corr_columns = {
|
| 911 |
-
"VADER":"vader_score",
|
| 912 |
-
"TextBlob":
|
| 913 |
-
"BERTweet":"bertweet",
|
| 914 |
-
"RoBERTa": "roberta",
|
| 915 |
-
"RoBERTa Large":
|
| 916 |
}
|
| 917 |
|
| 918 |
corr_rows = []
|
|
@@ -1002,6 +1029,7 @@ elif st.session_state.page == "analisis_batch":
|
|
| 1002 |
scatter_kws={"s": 40, "color": "#10b981", "alpha": 0.5},
|
| 1003 |
line_kws={"color": "#0f172a", "linewidth": 2}
|
| 1004 |
)
|
|
|
|
| 1005 |
try:
|
| 1006 |
r_val, p_val = pearsonr(df_merged[col_key], df_merged["log_return"])
|
| 1007 |
ax_s.set_title(f"{disp_name.split(' ')[0]}\nr={r_val:.3f}, p={p_val:.3f}", fontweight='bold', fontsize=9)
|
|
@@ -1019,6 +1047,7 @@ elif st.session_state.page == "analisis_batch":
|
|
| 1019 |
df_merged["date"], df_merged["log_return"],
|
| 1020 |
label="BTC Log Return", color="#f7931a", linewidth=3
|
| 1021 |
)
|
|
|
|
| 1022 |
colors_line = ["#3B82F6","#10B981","#EC4899","#14B8A6","#6366F1"]
|
| 1023 |
for i, (disp_name, col_key) in enumerate(corr_columns.items()):
|
| 1024 |
ax_line.plot(
|
|
|
|
| 446 |
text = text.lower()
|
| 447 |
text = re.sub(r"http\S+", "", text)
|
| 448 |
text = re.sub(r"@\w+", "", text)
|
| 449 |
+
text = re.sub(r"#\w+", "", text)
|
| 450 |
text = re.sub(r"[^\w\s]", "", text)
|
| 451 |
text = re.sub(r"\b\d+\b", "", text)
|
| 452 |
text = re.sub(r"\s+", " ", text).strip()
|
|
|
|
| 466 |
if score < -TEXTBLOB_THRESHOLD: return 'negative'
|
| 467 |
return 'neutral'
|
| 468 |
|
| 469 |
+
|
| 470 |
def map_roberta(label):
|
| 471 |
return {"LABEL_0": "negative", "LABEL_1": "neutral", "LABEL_2": "positive"}.get(label, "neutral")
|
| 472 |
|
|
|
|
| 607 |
|
| 608 |
for i, (method, label, detail) in enumerate(results):
|
| 609 |
col = col_a if i % 2 == 0 else col_b
|
| 610 |
+
|
| 611 |
bg = badge_color(label)
|
| 612 |
tc = badge_text_color(label)
|
| 613 |
icon = "↗" if label == "positive" else ("↘" if label == "negative" else "→")
|
| 614 |
+
detail_html = (
|
| 615 |
+
'<div style="font-size:0.7rem;color:#94a3b8;margin-top:2px;">'
|
| 616 |
+
+ detail +
|
| 617 |
+
'</div>'
|
| 618 |
+
) if detail else ''
|
| 619 |
+
|
| 620 |
+
if label == 'positive':
|
| 621 |
+
border_color = '#10b981'
|
| 622 |
+
elif label == 'negative':
|
| 623 |
+
border_color = '#f43f5e'
|
| 624 |
+
else:
|
| 625 |
+
border_color = '#cbd5e1'
|
| 626 |
+
|
| 627 |
+
html_card = (
|
| 628 |
+
'<div style="background:#FAFAFA;border:1px solid #e2e8f0;'
|
| 629 |
+
'border-left:4px solid ' + border_color + ';'
|
| 630 |
+
'border-radius:12px;padding:1rem 1.2rem;margin-bottom:1rem;'
|
| 631 |
+
'display:flex;align-items:center;justify-content:space-between;'
|
| 632 |
+
'box-shadow:0 2px 4px rgba(0,0,0,0.02);">'
|
| 633 |
+
'<div>'
|
| 634 |
+
'<div style="font-weight:600;font-size:0.75rem;color:#64748b;margin-bottom:4px;">'
|
| 635 |
+
+ method +
|
| 636 |
+
'</div>'
|
| 637 |
+
'<div style="font-weight:800;font-size:1.05rem;color:#0f172a;">'
|
| 638 |
+
+ label.capitalize() +
|
| 639 |
+
'</div>'
|
| 640 |
+
+ detail_html +
|
| 641 |
+
'</div>'
|
| 642 |
+
'<div style="background:' + bg + ';color:' + tc + ';'
|
| 643 |
+
'font-size:0.75rem;font-weight:700;'
|
| 644 |
+
'padding:6px 12px;border-radius:50px;">'
|
| 645 |
+
+ icon + ' ' + label.upper() +
|
| 646 |
+
'</div>'
|
| 647 |
+
'</div>'
|
| 648 |
+
)
|
| 649 |
+
|
| 650 |
with col:
|
| 651 |
+
st.markdown(html_card, unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 652 |
|
| 653 |
with st.expander("🔍 Lihat teks setelah preprocessing"):
|
| 654 |
st.code(text if text.strip() else "(kosong setelah dibersihkan)", language=None)
|
|
|
|
| 871 |
raw_display_cols = ["date","raw_tweet","vader","textblob","bertweet","roberta","roberta_large","vader_score","tb_score"]
|
| 872 |
st.dataframe(df[raw_display_cols], use_container_width=True, hide_index=True)
|
| 873 |
|
| 874 |
+
# ==================================================
|
| 875 |
+
# AGREGASI HARIAN DUAL-MODE
|
| 876 |
+
# ==================================================
|
| 877 |
sentiment_map = {"positive": 1, "neutral": 0, "negative": -1}
|
| 878 |
df_score = df.copy()
|
| 879 |
models_cat = ["vader","textblob","bertweet","roberta","roberta_large"]
|
| 880 |
for col in models_cat:
|
| 881 |
df_score[col] = df_score[col].map(sentiment_map)
|
| 882 |
|
| 883 |
+
# Agregasi kategorik (−1/0/1 mean)
|
| 884 |
df_sentiment_daily = df_score.groupby("date")[models_cat].mean().reset_index()
|
| 885 |
df_sentiment_daily["date"] = pd.to_datetime(df_sentiment_daily["date"]).dt.date
|
| 886 |
|
| 887 |
+
# Agregasi numerik VADER compound & TextBlob polarity
|
| 888 |
df_numeric_daily = df.groupby("date")[["vader_score","tb_score"]].mean().reset_index()
|
| 889 |
df_numeric_daily["date"] = pd.to_datetime(df_numeric_daily["date"]).dt.date
|
| 890 |
|
|
|
|
| 924 |
st.markdown("<hr class='vbc-divider'>", unsafe_allow_html=True)
|
| 925 |
|
| 926 |
# ==================================================
|
| 927 |
+
# UJI KORELASI PEARSON DENGAN LAG
|
| 928 |
# ==================================================
|
| 929 |
st.subheader("🔬 Uji Korelasi Pearson")
|
| 930 |
st.caption(
|
|
|
|
| 935 |
)
|
| 936 |
|
| 937 |
corr_columns = {
|
| 938 |
+
"VADER (numerik)": "vader_score",
|
| 939 |
+
"TextBlob (numerik)": "tb_score",
|
| 940 |
+
"BERTweet (kategorik)": "bertweet",
|
| 941 |
+
"RoBERTa Base (kategorik)": "roberta",
|
| 942 |
+
"RoBERTa Large (kategorik)":"roberta_large",
|
| 943 |
}
|
| 944 |
|
| 945 |
corr_rows = []
|
|
|
|
| 1029 |
scatter_kws={"s": 40, "color": "#10b981", "alpha": 0.5},
|
| 1030 |
line_kws={"color": "#0f172a", "linewidth": 2}
|
| 1031 |
)
|
| 1032 |
+
|
| 1033 |
try:
|
| 1034 |
r_val, p_val = pearsonr(df_merged[col_key], df_merged["log_return"])
|
| 1035 |
ax_s.set_title(f"{disp_name.split(' ')[0]}\nr={r_val:.3f}, p={p_val:.3f}", fontweight='bold', fontsize=9)
|
|
|
|
| 1047 |
df_merged["date"], df_merged["log_return"],
|
| 1048 |
label="BTC Log Return", color="#f7931a", linewidth=3
|
| 1049 |
)
|
| 1050 |
+
|
| 1051 |
colors_line = ["#3B82F6","#10B981","#EC4899","#14B8A6","#6366F1"]
|
| 1052 |
for i, (disp_name, col_key) in enumerate(corr_columns.items()):
|
| 1053 |
ax_line.plot(
|