import io import pandas as pd import streamlit as st import plotly.express as px from utils.model_loader import predict_batch, model_is_available st.markdown("## 📊 Analisis Batch") st.caption("Unggah file CSV berisi ulasan produk untuk memproses sentimen secara massal.") with st.container(border=True): uploaded = st.file_uploader( "Seret & letakkan file CSV di sini, atau klik untuk memilih file dari komputer", type=["csv"], help="Format: CSV · Maks: 20MB · Kolom teks harus ada", ) st.caption("📄 Format: CSV · Maks: 20MB · Kolom teks (Butuh 1 kolom berisi teks ulasan)") st.caption("Belum ada file? [unduh contoh data](https://example.com) — atau gunakan `sample_data/contoh_ulasan.csv` di repo ini.") if uploaded is not None: try: df = pd.read_csv(uploaded) except Exception as e: st.error(f"Gagal membaca CSV: {e}") st.stop() if df.empty: st.warning("File CSV kosong.") st.stop() st.write("") text_col = st.selectbox( "Pilih kolom yang berisi teks ulasan:", options=list(df.columns), index=0, ) max_rows = st.slider("Jumlah baris yang diproses (batasi untuk demo cepat)", 1, len(df), min(len(df), 200)) run = st.button("🚀 Jalankan Analisis Batch", type="primary") if run: subset = df.head(max_rows).copy() texts = subset[text_col].astype(str).tolist() progress_bar = st.progress(0.0, text="Memproses ulasan...") def _cb(frac): progress_bar.progress(frac, text=f"Memproses ulasan... {int(frac*100)}%") results = predict_batch(texts, progress_callback=_cb) progress_bar.empty() subset["Sentimen"] = [r[0] for r in results] subset["Confidence"] = [round(r[1] * 100, 2) for r in results] st.success(f"Selesai! {len(subset)} ulasan berhasil dianalisis.") st.markdown("#### Hasil Analisis") st.dataframe(subset, use_container_width=True, height=320) st.write("") col_pie, col_bar = st.columns(2) sent_counts = subset["Sentimen"].value_counts().reindex(["Positive", "Negative"]).fillna(0) with col_pie: fig_pie = px.pie( names=sent_counts.index, values=sent_counts.values, color=sent_counts.index, color_discrete_map={"Positive": "#2ecc71", "Negative": "#e74c3c"}, title="Proporsi Sentimen", hole=0.35, ) st.plotly_chart(fig_pie, use_container_width=True) with col_bar: fig_bar = px.bar( x=sent_counts.index, y=sent_counts.values, color=sent_counts.index, color_discrete_map={"Positive": "#2ecc71", "Negative": "#e74c3c"}, labels={"x": "Sentimen", "y": "Jumlah Ulasan"}, title="Distribusi Sentimen", ) fig_bar.update_layout(showlegend=False) st.plotly_chart(fig_bar, use_container_width=True) csv_buffer = io.StringIO() subset.to_csv(csv_buffer, index=False) st.download_button( "⬇️ Unduh Hasil Analisis (CSV)", data=csv_buffer.getvalue(), file_name="hasil_analisis_sentimen.csv", mime="text/csv", ) else: st.info("Unggah file CSV untuk mulai menganalisis banyak ulasan sekaligus.") if not model_is_available(): st.caption( "⚠️ Model IndoBERT belum ditemukan — hasil batch di atas menggunakan mode demo " "(heuristik kata kunci). Lihat README.md untuk cara memasang model asli." )