import streamlit as st import pandas as pd import numpy as np import re import json import joblib from sklearn.feature_extraction.text import TfidfVectorizer # Impor library tambahan import matplotlib.pyplot as plt import seaborn as sns from wordcloud import WordCloud # Fungsi untuk membersihkan teks dengan ekspresi reguler @st.cache_data def clean_text(text): # Tahap-1: Menghapus karakter non-ASCII text = re.sub(r'[^\x00-\x7F]+', '', text) # Tahap-2: Menghapus URL text = re.sub(r'http[s]?://.[a-zA-Z0-9./_?=%&#+!]+', '', text) text = re.sub(r'pic.twitter.com?.[a-zA-Z0-9./_?=%&#+!]+', '', text) # Tahap-3: Menghapus mentions text = re.sub(r'@[\w]+', '', text) # Tahap-4: Menghapus hashtag text = re.sub(r'#([\w]+)', '', text) # Tahap-5 Menghapus 'amp' yang menempel pada '&' dan 'gt' yang menempel pada '&' text = re.sub(r'&|>', '', text) # Tahap-6: Menghapus karakter khusus (simbol) text = re.sub(r'[!$%^&*@#()_+|~=`{}\[\]%\-:";\'<>?,./]', '', text) # Tahap-7: Menghapus angka text = re.sub(r'[0-9]+', '', text) # Tahap-8: Menggabungkan spasi ganda menjadi satu spasi text = re.sub(' +', ' ', text) # Tahap-9: Menghapus spasi di awal dan akhir kalimat text = text.strip() # Tahap-10: Konversi teks ke huruf kecil text = text.lower() # Tahap-11: koreksi duplikasi tiga karakter beruntun atau lebih (contoh. yukkk) # text = re.sub(r'([a-zA-Z])\1\1', '\\1', text) #text = re.sub(r'(.)(\1{2,})', r'\1\1', text) text = re.sub(r'(\w)\1{2,}', r'\1', text) return text # Membaca kamus kata gaul Salsabila kamus_path = '_json_colloquial-indonesian-lexicon (1).txt' # Ganti dengan path yang benar with open(kamus_path) as f: data = f.read() lookp_dict = json.loads(data) # Dict kata gaul saya sendiri yang tidak masuk di dict Salsabila kamus_sendiri_path = 'kamus_gaul_custom.txt' with open(kamus_sendiri_path) as f: kamus_sendiri = f.read() kamus_gaul_baru = json.loads(kamus_sendiri) # Menambahkan dict kata gaul baru ke kamus yang sudah ada lookp_dict.update(kamus_gaul_baru) # Fungsi untuk normalisasi kata gaul @st.cache_data def normalize_slang(text, slang_dict): words = text.split() normalized_words = [slang_dict.get(word, word) for word in words] return ' '.join(normalized_words) # Fungsi untuk ekstraksi fitur TF-IDF @st.cache_data def extract_tfidf_features(texts, tfidf_vectorizer): tfidf_matrix = tfidf_vectorizer.transform(texts) return tfidf_matrix # Memuat model TF-IDF dengan joblib (pastikan path-nya benar) tfidf_model_path = 'X_tfidf_model.joblib' tfidf_vectorizer = joblib.load(tfidf_model_path) # Tambahkan widget untuk memilih model selected_model = st.selectbox("Pilih Model Sentimen:", ("Ensemble", "Naive Bayes", "Logistic Regression")) # Fungsi untuk memilih model berdasarkan pilihan pengguna def select_sentiment_model(selected_model): if selected_model == "Ensemble": model_path = 'ensemble_clf_soft_smote.joblib' elif selected_model == "Random Forest": model_path = 'best_rf_model_smote.joblib' elif selected_model == "Naive Bayes": model_path = 'naive_bayes_model_smote.joblib' elif selected_model == "Logistic Regression": model_path = 'logreg_model_smote.joblib' else: # Fallback ke model default jika pilihan tidak valid model_path = 'ensemble_clf_soft_smote.joblib' model = joblib.load(model_path) return model # Memilih model sentimen berdasarkan pilihan pengguna sentiment_model = select_sentiment_model(selected_model) # Fungsi untuk prediksi sentimen @st.cache_data def predict_sentiment(text, model, tfidf_vectorizer, slang_dict): # Tahap-1: Membersihkan dan normalisasi teks cleaned_text = clean_text(text) norm_slang_text = normalize_slang(cleaned_text, slang_dict) # Tahap-2: Ekstraksi fitur TF-IDF tfidf_matrix = tfidf_vectorizer.transform([norm_slang_text]) # Tahap-3: Lakukan prediksi sentimen sentiment = model.predict(tfidf_matrix) # Tahap-4: Menggantikan indeks dengan label sentimen labels = {0: "Negatif", 1: "Netral", 2: "Positif"} sentiment_label = labels[int(sentiment)] return sentiment_label def get_emoticon(sentiment): if sentiment == "Positif": emoticon = "😄" # Emotikon untuk sentimen positif elif sentiment == "Negatif": emoticon = "😞" # Emotikon untuk sentimen negatif else: emoticon = "😐" # Emotikon untuk sentimen netral return emoticon # Fungsi untuk membuat tautan unduhan def get_table_download_link(df, download_format): if download_format == "XLSX": df.to_excel("hasil_sentimen.xlsx", index=False) return f'Unduh File XLSX' else: csv = df.to_csv(index=False) return f'Unduh File CSV' # Set judul situs web st.set_page_config(page_title="naufalnashif-ML") # Judul st.title("Analisis Sentimen Based on Tweets Biskita Transpakuan") # Pilihan input teks manual atau berkas XLSX input_option = st.radio("Pilih metode input:", ("Teks Manual", "Unggah Berkas XLSX")) if input_option == "Teks Manual": # Input teks dari pengguna user_input = st.text_area("Masukkan teks:", "") else: # Input berkas XLSX uploaded_file = st.file_uploader("Unggah berkas XLSX", type=["xlsx"]) st.write("**Pastikan berkas XLSX Anda memiliki kolom yang bernama 'Text'.**") if uploaded_file is not None: df = pd.read_excel(uploaded_file) if 'Text' not in df.columns: st.warning("Berkas XLSX harus memiliki kolom bernama 'Text' untuk analisis sentimen.") else: texts = df['Text'] # Sesuaikan dengan nama kolom di berkas XLSX Anda # Analisis sentimen results = [] analisis = False if input_option == "Teks Manual" and user_input: # Pisahkan teks yang dimasukkan pengguna menjadi baris-baris terpisah user_texts = user_input.split('\n') for text in user_texts: sentiment_label = predict_sentiment(text, sentiment_model, tfidf_vectorizer, lookp_dict) emoticon = get_emoticon(sentiment_label) cleaned_text = clean_text(text) norm_slang_text = normalize_slang(cleaned_text, lookp_dict) results.append((text, cleaned_text, norm_slang_text, sentiment_label, emoticon)) analisis = True elif input_option == "Unggah Berkas XLSX" and uploaded_file is not None: if 'Text' in df.columns: for text in texts: sentiment_label = predict_sentiment(text, sentiment_model, tfidf_vectorizer, lookp_dict) emoticon = get_emoticon(sentiment_label) cleaned_text = clean_text(text) norm_slang_text = normalize_slang(cleaned_text, lookp_dict) results.append((text, cleaned_text, norm_slang_text, sentiment_label, emoticon)) analisis = True else: st.warning("Berkas XLSX harus memiliki kolom bernama 'Text' untuk analisis sentimen.") st.info('Tekan "Analysis" kemabli jika tampilan menghilang', icon = 'ⓘ') if results and analisis == True and st.button("Analysis"): # Membagi tampilan menjadi dua kolom columns = st.columns(2) # Kolom pertama untuk Word Cloud with columns[0]: if results: all_texts = [result[2] for result in results if result[2] is not None and not pd.isna(result[2])] all_texts = " ".join(all_texts) st.subheader("Word Cloud") if all_texts: wordcloud = WordCloud(width=800, height=660, background_color='white', colormap='Purples', # Warna huruf contour_color='black', # Warna kontur contour_width=2, # Lebar kontur mask=None, # Gunakan mask untuk bentuk kustom ).generate(all_texts) st.image(wordcloud.to_array()) else: st.write("Tidak ada data untuk ditampilkan dalam Word Cloud.") # Kolom kedua untuk Bar Chart with columns[1]: st.subheader("Chart") if results: df_results = pd.DataFrame(results, columns=["Teks", "Cleaned Text", "Norm Text", "Hasil Analisis Sentimen", "Emotikon"]) sns.set_style("whitegrid") # Menyiapkan label kelas class_labels = ["Negatif", "Netral", "Positif"] # Menghitung nilai hitungan per label value_counts = df_results["Hasil Analisis Sentimen"].value_counts() # Mengurutkan nilai hitungan berdasarkan label value_counts = value_counts.reindex(class_labels) fig, ax = plt.subplots() # Buat objek Figure sns.barplot(x=value_counts.index, y=value_counts.values, ax=ax) # Gunakan ax= untuk plot plt.xticks(rotation=45) st.pyplot(fig) # Tampilkan plot menggunakan st.pyplot(fig) # Menampilkan hasil analisis sentimen dalam kotak yang dapat diperluas with st.expander("Hasil Analisis Sentimen"): # Tampilkan tabel hasil analisis sentimen st.write(pd.DataFrame(results, columns=["Teks", "Cleaned Text", "Norm Text", "Hasil Analisis Sentimen", "Emotikon"])) # Tautan untuk mengunduh hasil dalam format XLSX atau CSV st.subheader("Unduh Hasil") download_format = st.selectbox("Pilih format unduhan:", ["XLSX", "CSV"]) if results: if download_format == "XLSX": # Simpan DataFrame ke dalam file XLSX df = pd.DataFrame(results, columns=["Teks", "Cleaned Text", "Norm Text", "Hasil Analisis Sentimen", "Emotikon"]) df.to_excel("hasil_sentimen.xlsx", index=False) # Tampilkan tombol unduh XLSX st.download_button(label="Unduh XLSX", data=open("hasil_sentimen.xlsx", "rb").read(), key="xlsx_download", file_name="hasil_sentimen.xlsx") else: # Jika CSV # Simpan DataFrame ke dalam file CSV df = pd.DataFrame(results, columns=["Teks", "Cleaned Text", "Norm Text", "Hasil Analisis Sentimen", "Emotikon"]) csv = df.to_csv(index=False) # Tampilkan tombol unduh CSV st.download_button(label="Unduh CSV", data=csv, key="csv_download", file_name="hasil_sentimen.csv") else: st.write("Tidak ada data untuk diunduh.") # Garis pemisah st.divider() # Tautan ke GitHub github_link = "https://github.com/naufalnashif/" st.markdown(f"GitHub: [{github_link}]({github_link})") # Tautan ke Instagram instagram_link = "https://www.instagram.com/naufal.nashif/" st.markdown(f"Instagram: [{instagram_link}]({instagram_link})") # Pesan penutup st.write('Thank you for trying the demo!') st.write('Best regards, Naufal Nashif')