Spaces:
Paused
Paused
Download services/preprocessing.py from noranisa/Sentimen-Analysis: direct link, hf CLI and curl.
- Browser
- Download file 13.3 kB
-
https://huggingface.co/spaces/noranisa/Sentimen-Analysis/resolve/main/services/preprocessing.py
- Command line
-
hf download hf://spaces/noranisa/Sentimen-Analysis/services/preprocessing.py
-
curl -L -o preprocessing.py https://huggingface.co/spaces/noranisa/Sentimen-Analysis/resolve/main/services/preprocessing.py
13.3 kB
| """ | |
| services/preprocessing_id.py | |
| Preprocessing Bahasa Indonesia yang komprehensif untuk media sosial. | |
| Mencakup: | |
| 1. Normalisasi slang & singkatan (~200 kata) | |
| 2. Handling emoji โ sentimen kata | |
| 3. Code-switching ID-EN | |
| 4. Deteksi & normalisasi kata berulang | |
| 5. Stopwords gabungan ID+EN | |
| """ | |
| import re | |
| from typing import Optional | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 1. STOPWORDS GABUNGAN ID + EN | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| STOPWORDS = { | |
| # Bahasa Indonesia - fungsi | |
| 'yang','dan','di','ke','dari','ini','itu','dengan','untuk','adalah','ada', | |
| 'pada','juga','tidak','bisa','sudah','saya','kamu','kami','mereka','kita', | |
| 'nya','pun','aja','ya','yah','mau','jadi','buat','kalau','banget','sangat', | |
| 'lebih','nih','sih','dong','lah','lagi','terus','sama','atau','karena', | |
| 'oleh','akan','dapat','harus','boleh','perlu','agar','supaya','namun', | |
| 'tetapi','tapi','sedang','sedangkan','setelah','sebelum','ketika','saat', | |
| 'antara','dalam','luar','atas','bawah','depan','belakang','kiri','kanan', | |
| 'sekitar','tentang','terhadap','selama','karena','sebab','hingga','sampai', | |
| 'bahwa','agar','jika','bila','apabila','maka','sehingga','walaupun','meski', | |
| 'justru','malah','bahkan','hanya','saja','paling','makin','semakin', | |
| 'begitu','seperti','misalnya','contoh','yaitu','yakni','antara','lain', | |
| 'hal','cara','jenis','macam','bagian','sistem','proses', | |
| # Bahasa gaul/informal | |
| 'tuh','tuu','gitu','gini','gituu','ginii','doang','woi','hoi','hai','hey', | |
| 'nah','neh','deh','kok','sih','kan','kaan','si','bu','pak','mas','mbak', | |
| # Bahasa Inggris - fungsi | |
| 'the','is','in','of','to','a','an','and','it','for','that','this', | |
| 'was','are','be','has','have','had','do','does','did','will','would', | |
| 'could','should','may','might','shall','must','can','need','dare', | |
| 'with','at','by','from','up','about','into','through','during', | |
| 'before','after','above','below','between','out','off','over','under', | |
| 'again','further','then','once','here','there','when','where','why', | |
| 'how','all','both','each','few','more','most','other','some','such', | |
| 'no','not','only','own','same','so','than','too','very','just','but', | |
| 'if','or','because','as','until','while','although','though','since', | |
| 'i','you','he','she','we','they','me','him','her','us','them', | |
| 'my','your','his','its','our','their', | |
| } | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 2. SLANG & SINGKATAN (~200 entri) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| SLANG_MAP = { | |
| # Negasi | |
| 'gak':'tidak','ga':'tidak','nggak':'tidak','ngga':'tidak','enggak':'tidak', | |
| 'gk':'tidak','ndk':'tidak','ndak':'tidak','kagak':'tidak','kaga':'tidak', | |
| 'bkn':'bukan','bukan':'bukan', | |
| # Kata ganti | |
| 'gue':'saya','gw':'saya','w':'saya','aq':'saya','ak':'saya', | |
| 'lo':'kamu','lu':'kamu','elo':'kamu','elu':'kamu','loe':'kamu', | |
| 'dy':'dia','doi':'dia','dya':'dia', | |
| 'kt':'kita','qta':'kita','qt':'kita', | |
| 'mrk':'mereka','mrek':'mereka', | |
| # Konjungsi & partikel | |
| 'tp':'tapi','tpi':'tapi','ttpi':'tetapi', | |
| 'jg':'juga','jga':'juga','jg':'juga', | |
| 'krn':'karena','karna':'karena','kren':'karena', | |
| 'dgn':'dengan','dg':'dengan','d':'dengan', | |
| 'yg':'yang','y':'yang', | |
| 'utk':'untuk','buat':'untuk','bt':'buat', | |
| 'dr':'dari','dri':'dari', | |
| 'sm':'sama','ama':'sama','bareng':'bersama', | |
| 'kl':'kalau','kalo':'kalau','klw':'kalau', | |
| 'spy':'supaya','biar':'supaya', | |
| 'sdgkan':'sedangkan','pdhl':'padahal','pdhal':'padahal', | |
| 'mk':'maka','jd':'jadi','jdi':'jadi', | |
| # Aspek waktu | |
| 'udah':'sudah','udh':'sudah','dah':'sudah','sdh':'sudah','uda':'sudah', | |
| 'blm':'belum','blum':'belum','belom':'belum', | |
| 'msh':'masih','masi':'masih','msih':'masih', | |
| 'lg':'lagi','lgi':'lagi','lg':'lagi', | |
| 'skrg':'sekarang','skrng':'sekarang','skg':'sekarang', | |
| 'stlh':'setelah','sblm':'sebelum', | |
| 'trs':'terus','trus':'terus','teros':'terus', | |
| # Kemampuan & keharusan | |
| 'bs':'bisa','bsa':'bisa','biz':'bisa', | |
| 'hrs':'harus','hrus':'harus', | |
| 'prlu':'perlu','prl':'perlu', | |
| 'mau':'ingin','mw':'ingin','mo':'ingin', | |
| # Kuantitas & kualitas | |
| 'bgt':'banget','bngt':'banget','bngt':'banget', | |
| 'sgt':'sangat','bgt':'banget', | |
| 'krg':'kurang','krang':'kurang', | |
| 'lbh':'lebih','lbih':'lebih', | |
| 'plg':'paling','paling':'paling', | |
| 'byk':'banyak','bnyk':'banyak', | |
| 'dkt':'dekat','dkt':'dekat', | |
| 'cpt':'cepat','cpet':'cepat', | |
| 'lmbt':'lambat','lmbat':'lambat', | |
| # Afirmatif & konfirmasi | |
| 'iya':'ya','iyes':'ya','iyess':'ya', | |
| 'ok':'oke','okay':'oke','oke':'oke','okey':'oke', | |
| 'sip':'bagus','siip':'bagus', | |
| 'bnr':'benar','bner':'benar','bnar':'benar', | |
| 'emg':'memang','emang':'memang','mmg':'memang','memg':'memang', | |
| 'kyk':'kayak','kek':'seperti','kyak':'kayak', | |
| # Ekspresi umum medsos | |
| 'wkwk':'haha','wkwkwk':'haha','wkwkwkwk':'haha', | |
| 'hehe':'haha','hihi':'haha','huhu':'sedih','hiks':'sedih', | |
| 'lol':'tertawa','lmao':'tertawa','rofl':'tertawa', | |
| 'omg':'terkejut','omgg':'terkejut', | |
| 'btw':'ngomong-ngomong','fyi':'informasi', | |
| 'asap':'segera','imo':'menurutku','imho':'menurutku', | |
| 'tbh':'jujurnya','ngl':'jujur', | |
| # Informasi & pertanyaan | |
| 'info':'informasi','inpo':'informasi', | |
| 'kmn':'kemana','kmana':'kemana', | |
| 'dmn':'dimana','dmana':'dimana', | |
| 'gmn':'bagaimana','gmana':'bagaimana','bgmn':'bagaimana', | |
| 'knp':'kenapa','ngp':'kenapa','napa':'kenapa', | |
| 'brp':'berapa','brapa':'berapa', | |
| 'ttg':'tentang','mnrt':'menurut', | |
| 'mksud':'maksud','mksd':'maksud', | |
| # Platform-spesifik | |
| 'fyp':'for you page','foryou':'untuk kamu', | |
| 'collab':'kolaborasi','ft':'bersama', | |
| 'dm':'pesan langsung','pm':'pesan pribadi', | |
| 'fr':'sungguh','frfr':'sungguh-sungguh', | |
| 'ngl':'jujur','idk':'tidak tahu','idk':'tidak tahu', | |
| 'ootd':'outfit hari ini','grwm':'ikut siap-siapku', | |
| # Sapaan & gelar | |
| 'kk':'kakak','kak':'kakak','bro':'saudara','sis':'saudari', | |
| 'bang':'abang','bg':'abang','abg':'abang', | |
| 'om':'paman','tante':'bibi','bund':'bunda', | |
| # Kata-kata umum yang sering disingkat | |
| 'prt':'pemerintah','pmrth':'pemerintah', | |
| 'jkt':'jakarta','sby':'surabaya','bdg':'bandung', | |
| 'indo':'indonesia','id':'indonesia', | |
| 'pns':'pegawai negeri','asn':'aparatur sipil negara', | |
| 'umkm':'usaha mikro kecil menengah', | |
| 'pdp':'pendapatan','gdp':'produk domestik bruto', | |
| 'bbm':'bahan bakar minyak','lpg':'gas elpiji', | |
| 'rs':'rumah sakit','puskesmas':'pusat kesehatan masyarakat', | |
| 'sd':'sekolah dasar','smp':'sekolah menengah pertama', | |
| 'sma':'sekolah menengah atas','pt':'perguruan tinggi', | |
| } | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 3. EMOJI โ SENTIMEN KATA | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| EMOJI_MAP = { | |
| # Positif | |
| '๐':'senang','๐':'senang','๐':'senang','๐':'senang', | |
| '๐':'senang','๐ฅฐ':'cinta','๐':'cinta','โค':'cinta', | |
| 'โค๏ธ':'cinta','๐':'sayang','๐':'sayang','๐':'sayang', | |
| '๐':'bagus','๐':'oke','โ ':'benar','๐ฏ':'sempurna', | |
| '๐ฅ':'keren','โญ':'bagus','๐':'luar biasa','โจ':'indah', | |
| '๐':'selamat','๐':'perayaan','๐':'menang','๐ฅ':'juara', | |
| '๐':'lucu','๐คฃ':'lucu','๐':'lucu', | |
| '๐':'terima kasih','๐':'tepuk tangan','๐ช':'kuat','๐':'maju', | |
| '๐':'keren','๐คฉ':'kagum','๐':'baik','๐ค':'pelukan', | |
| # Negatif | |
| '๐ข':'sedih','๐ญ':'menangis','๐':'kecewa','๐':'kecewa', | |
| '๐ก':'marah','๐ ':'marah','๐คฌ':'sangat marah','๐ข':'marah', | |
| '๐':'jelek','โ':'salah','๐ซ':'dilarang','โ':'berhenti', | |
| '๐ฑ':'terkejut','๐จ':'takut','๐ฐ':'khawatir','๐':'khawatir', | |
| '๐คฎ':'jijik','๐คข':'mual','๐':'patah hati','๐ค':'kesal', | |
| '๐ฃ':'susah','๐':'frustrasi','๐ฉ':'lelah','๐ซ':'capek', | |
| # Netral/Informasi | |
| '๐ค':'berpikir','๐ง':'mengamati','๐ญ':'berpikir', | |
| '๐ข':'pengumuman','๐ฃ':'pengumuman','โน':'informasi', | |
| 'โ ':'peringatan','โ':'pertanyaan','โ':'penting', | |
| '๐':'data','๐':'naik','๐':'turun','๐ฐ':'uang', | |
| '๐ฅ':'rumah sakit','๐ซ':'sekolah','๐':'pemerintah', | |
| } | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 4. PREPROCESSING PIPELINE | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def normalize_repeated_chars(text: str) -> str: | |
| """ | |
| 'baguuuus' โ 'bagus', 'hahahaha' โ 'haha' | |
| Pertahankan max 2 karakter berulang. | |
| """ | |
| return re.sub(r'(.)\1{2,}', r'\1\1', text) | |
| def extract_emoji_sentiment(text: str) -> tuple[str, list[str]]: | |
| """ | |
| Ekstrak emoji dan ganti dengan kata sentimen. | |
| Return: (text_tanpa_emoji, list_kata_sentimen) | |
| """ | |
| sentiment_words = [] | |
| result = text | |
| for emoji, word in EMOJI_MAP.items(): | |
| if emoji in result: | |
| count = result.count(emoji) | |
| sentiment_words.extend([word] * min(count, 2)) | |
| result = result.replace(emoji, f' {word} ') | |
| # Hapus emoji yang tidak ada di map menggunakan regex | |
| result = re.sub( | |
| r'[\U00010000-\U0010ffff' | |
| r'\U0001F600-\U0001F64F' | |
| r'\U0001F300-\U0001F5FF' | |
| r'\U0001F680-\U0001F6FF' | |
| r'\U0001F1E0-\U0001F1FF' | |
| r'\u2600-\u26FF\u2700-\u27BF]', | |
| ' ', result | |
| ) | |
| return result, sentiment_words | |
| def normalize_slang(tokens: list[str]) -> list[str]: | |
| """Normalisasi slang per token.""" | |
| return [SLANG_MAP.get(t, t) for t in tokens] | |
| def clean_text_deep(text: str, keep_emoji_words: bool = True) -> str: | |
| """ | |
| Full preprocessing pipeline: | |
| 1. Lowercase | |
| 2. Hapus URL, mention, hashtag (simpan kata hashtag) | |
| 3. Ekstrak emoji โ kata sentimen | |
| 4. Normalisasi karakter berulang | |
| 5. Normalisasi slang | |
| 6. Bersihkan karakter non-alfanumerik | |
| 7. Filter stopwords & token pendek | |
| """ | |
| if not text or not isinstance(text, str): | |
| return "" | |
| # 1. Lowercase | |
| text = text.lower().strip() | |
| # 2. Hapus URL | |
| text = re.sub(r'https?://\S+|www\.\S+', '', text) | |
| # 3. Hapus mention (@user) | |
| text = re.sub(r'@\w+', '', text) | |
| # 4. Hashtag โ simpan kata | |
| text = re.sub(r'#(\w+)', r' \1 ', text) | |
| # 5. Emoji โ kata sentimen | |
| text, emoji_words = extract_emoji_sentiment(text) | |
| # 6. Normalisasi karakter berulang | |
| text = normalize_repeated_chars(text) | |
| # 7. Hapus karakter non-alfanumerik (simpan spasi) | |
| text = re.sub(r'[^a-z0-9\s]', ' ', text) | |
| # 8. Tokenisasi | |
| tokens = text.split() | |
| # 9. Normalisasi slang | |
| tokens = normalize_slang(tokens) | |
| # 10. Filter: hapus stopwords dan token terlalu pendek | |
| tokens = [t for t in tokens if len(t) > 2 and t not in STOPWORDS] | |
| # 11. Tambahkan kata dari emoji | |
| if keep_emoji_words and emoji_words: | |
| tokens.extend(emoji_words) | |
| return ' '.join(tokens) | |
| def is_valid(text: str, min_words: int = 3) -> bool: | |
| """Cek apakah teks valid untuk dianalisis.""" | |
| if not text or not isinstance(text, str): | |
| return False | |
| cleaned = clean_text_deep(text) | |
| return len(cleaned.split()) >= min_words | |
| def clean_text(text: str) -> str: | |
| """ | |
| Alias untuk kompatibilitas dengan kode lama. | |
| Memanggil clean_text_deep. | |
| """ | |
| return clean_text_deep(text) | |
| def batch_clean(texts: list[str]) -> list[str]: | |
| """Batch preprocessing untuk list teks.""" | |
| return [clean_text_deep(t) for t in texts] | |
| def get_text_stats(text: str) -> dict: | |
| """ | |
| Statistik linguistik untuk satu teks. | |
| Berguna untuk feature engineering. | |
| """ | |
| original_tokens = text.lower().split() | |
| cleaned = clean_text_deep(text) | |
| cleaned_tokens = cleaned.split() | |
| # Deteksi code-switching (campuran ID-EN) | |
| en_words = {'the','is','in','of','a','an','and','it','for','that', | |
| 'this','good','bad','great','nice','love','hate','really', | |
| 'very','just','like','get','can','go','make','know','think', | |
| 'want','need','people','time','work','life','day','way'} | |
| en_count = sum(1 for t in original_tokens if t in en_words) | |
| cs_ratio = en_count / max(len(original_tokens), 1) | |
| # Deteksi emoji | |
| emoji_count = sum(1 for c in text if c in EMOJI_MAP) | |
| # Deteksi pengulangan kata | |
| repeat_count = len(re.findall(r'(.)\1{2,}', text.lower())) | |
| return { | |
| 'original_len': len(original_tokens), | |
| 'cleaned_len': len(cleaned_tokens), | |
| 'emoji_count': emoji_count, | |
| 'cs_ratio': round(cs_ratio, 3), | |
| 'repeat_count': repeat_count, | |
| 'is_codeswitched': cs_ratio > 0.2, | |
| } |