Kolom teks adalah tempat favorit masalah data bersembunyi. Begitu isian diketik manusia — pemesan menulis nama menu sendiri di formulir daring — spasi ekstra, kapital yang tak seragam, dan karakter pengganti mulai bermunculan. Dua nilai yang sama maknanya terhitung berbeda oleh komputer, dan laporan penjualan per menu jadi terpecah. Pada chapter ini kita merapikan kolom teks dengan keluarga metode .str milik pandas: strip untuk tepi, lower untuk kapital, replace untuk karakter, split untuk memecah nilai gabungan, dan extract untuk mengambil pola dari teks.
Formulir Bebas dan Penyamaran Teks #
Formulir pemesanan daring Toko Kopi Kita membiarkan pelanggan menulis nama menu secara bebas. Delapan pesanan masuk, dan sekilas semuanya memesan salah satu dari lima menu. Sekilas itu keliru — mari kita hitung dengan nunique() dan value_counts().
import pandas as pd
pesanan = pd.DataFrame({
"menu": [" Kopi Susu", "kopi susu", "Matcha_Latte", "TEH TAWAR",
"teh tawar ", "Es Kopi Susu", "matcha latte", "AMERICANO"],
})
print(pesanan)
print()
print("Jumlah nilai unik:", pesanan["menu"].nunique())
print()
print(pesanan["menu"].value_counts())Output yang Dihasilkan:
menu
0 Kopi Susu
1 kopi susu
2 Matcha_Latte
3 TEH TAWAR
4 teh tawar
5 Es Kopi Susu
6 matcha latte
7 AMERICANO
Jumlah nilai unik: 8
menu
Kopi Susu 1
kopi susu 1
Matcha_Latte 1
TEH TAWAR 1
teh tawar 1
Es Kopi Susu 1
matcha latte 1
AMERICANO 1
Name: count, dtype: int64Hasilnya janggal: nunique() melaporkan delapan nilai unik dari delapan pesanan — seolah-olah toko menjual delapan menu berbeda. Padahal Kopi Susu dan kopi susu hanyalah kopi susu yang ditulis dua gaya, dan TEH TAWAR dengan teh tawar adalah menu yang sama. Laporan penjualan per menu akan memuat delapan baris yang sebagian besar berisi satu pesanan.
Merapikan Tepi dan Kapital dengan strip dan lower #
Dua masalah paling umum diselesaikan dua metode pertama. str.strip() membuang spasi di awal dan akhir teks; str.lower() menyeragamkan seluruh huruf menjadi kecil — bentuk yang paling cocok untuk analisis karena menghapus perbedaan kapital. Keduanya dirantai langsung pada kolom.
import pandas as pd
menu = [" Kopi Susu", "kopi susu", "Matcha_Latte", "TEH TAWAR",
"teh tawar ", "Es Kopi Susu", "matcha latte", "AMERICANO"]
rapikan = pd.Series(menu).str.strip().str.lower()
print(rapikan.tolist())
print()
print(rapikan.value_counts())
print()
print("Untuk tampilan:", rapikan.str.title().tolist())Output yang Dihasilkan:
['kopi susu', 'kopi susu', 'matcha_latte', 'teh tawar', 'teh tawar', 'es kopi susu', 'matcha latte', 'americano']
teh tawar 2
kopi susu 1
kopi susu 1
matcha_latte 1
es kopi susu 1
matcha latte 1
americano 1
Name: count, dtype: int64
Untuk tampilan: ['Kopi Susu', 'Kopi Susu', 'Matcha_Latte', 'Teh Tawar', 'Teh Tawar', 'Es Kopi Susu', 'Matcha Latte', 'Americano']Mari kita telaah kemajuannya:
- Baris 6 (rantai
.str.strip().str.lower()): daftar hasil memperlihatkan spasi tepi hilang dan seluruh huruf kecil.TEH TAWARdanteh tawarkini sama-sama menjaditeh tawar— dua pesanan itu akhirnya terhitung satu menu. value_counts(): jumlah nilai unik turun dari delapan menjadi tujuh. Kemajuan nyata, tetapikopi susu(spasi ganda) danmatcha_latte(garis bawah) masih menyamar sebagai menu tersendiri.- Baris 11 (
str.title()): kapital di tiap kata dipulihkan menjadiKopi Susu— tetapi hanya untuk tampilan laporan. Untuk analisis, tetap pakai bentuk kecil yang seragam.
Karakter Pengganti dan Spasi Ganda #
Sisa dua penyamar — garis bawah dan spasi ganda — butuh str.replace. Pola spasi berulang ditangkap regex \s+ yang berarti “satu atau lebih karakter spasi”; menggantinya dengan satu spasi meratakan semua spasi ganda, tiga, dan seterusnya. Rantai lengkapnya menjadi satu alur pembersihan yang bisa dibaca dari atas ke bawah.
import pandas as pd
menu = [" Kopi Susu", "kopi susu", "Matcha_Latte", "TEH TAWAR",
"teh tawar ", "Es Kopi Susu", "matcha latte", "AMERICANO"]
bersih = (pd.Series(menu)
.str.strip()
.str.lower()
.str.replace("_", " ", regex=False)
.str.replace(r"\s+", " ", regex=True))
perbandingan = pd.DataFrame({"asli": menu, "bersih": bersih})
print(perbandingan)
print()
print(bersih.value_counts())
print()
print("Menu yang memuat kata kopi:")
print(bersih[bersih.str.contains("kopi")].tolist())Output yang Dihasilkan:
asli bersih
0 Kopi Susu kopi susu
1 kopi susu kopi susu
2 Matcha_Latte matcha latte
3 TEH TAWAR teh tawar
4 teh tawar teh tawar
5 Es Kopi Susu es kopi susu
6 matcha latte matcha latte
7 AMERICANO americano
kopi susu 2
matcha latte 2
teh tawar 2
es kopi susu 1
americano 1
Name: count, dtype: int64
Menu yang memuat kata kopi:
['kopi susu', 'kopi susu', 'es kopi susu']Mari kita telaah rantai pembersihannya:
- Baris 9 (
.str.replace("_", " ", regex=False)): garis bawah dianggap pembatas kata, diganti spasi —matcha_lattemenjadimatcha latte. - Baris 10 (
.str.replace(r"\s+", " ", regex=True)): dengan mode regex,\s+mencocokkan deretan spasi sepanjang apa pun dan menggantinya dengan tepat satu spasi.kopi susuakhirnya menyatu dengankopi susu. - Hasil akhir: dari delapan nilai unik menjadi lima menu sesungguhnya —
kopi susu2,matcha latte2,teh tawar2,es kopi susu1,americano1. Urutan operasi juga penting:striplebih dulu, lalu penggantian, agar spasi hasil ganti garis bawah ikut dirapikan. - Baris 17-18 (
str.contains("kopi")): saringan teks — mengambil hanya menu yang memuat katakopi, berguna untuk analisis per lini produk.
Memecah Nilai Gabungan dengan split #
Satu sel kadang memuat dua informasi sekaligus: nama lengkap Budi Santoso sebenarnya adalah nama depan dan nama belakang yang direkatkan spasi. str.split memecahnya; dengan expand=True hasil pecahannya langsung menjadi kolom-kolom DataFrame baru.
import pandas as pd
pesanan = pd.DataFrame({
"pemesan": ["Budi Santoso", "Siti Rahayu", "Dewi Lestari",
"Rani Wijaya", "Agus Pratama"],
"menu": ["kopi susu", "teh tawar", "matcha latte",
"americano", "es kopi susu"],
})
bagian = pesanan["pemesan"].str.split(r"\s+", regex=True, expand=True)
print(bagian)
print()
pesanan["nama_depan"] = bagian[0]
pesanan["nama_belakang"] = bagian[1]
print(pesanan)Output yang Dihasilkan:
0 1
0 Budi Santoso
1 Siti Rahayu
2 Dewi Lestari
3 Rani Wijaya
4 Agus Pratama
pemesan menu nama_depan nama_belakang
0 Budi Santoso kopi susu Budi Santoso
1 Siti Rahayu teh tawar Siti Rahayu
2 Dewi Lestari matcha latte Dewi Lestari
3 Rani Wijaya americano Rani Wijaya
4 Agus Pratama es kopi susu Agus PratamaMari kita telaah hasilnya:
- Baris 10 (
str.split(r"\s+", regex=True, expand=True)): pola pemisah adalah deretan spasi, sehingga nama dengan spasi ganda pun terpecah benar.expand=Truemenghasilkan DataFrame dengan kolom bernomor0,1, dan seterusnya — satu kolom per pecahan. - Baris 13-14 (
bagian[0],bagian[1]): kolom pecahan dipindahkan kepesanandengan nama yang bermakna. Bila hanya butuh satu bagian,pesanan["pemesan"].str.split().str.get(0)memberi nama depan tanpa membuat kolom tambahan. - Kolom ketiga bila ada: nama dengan tiga kata membuat
expand=Truemenghasilkan kolom2yang berisiNaNuntuk baris lainnya — pecahan yang tidak seragam tetap terlihat, bukan tersembunyi.
Mengambil Angka dari Teks dengan extract #
Kebalikan dari split: kadang kita hanya ingin mengambil sebagian teks yang mengikuti pola tertentu. Kolom harga hasil tempelan manusia — Rp24.000, Rp 28.000 — menyimpan angka di tengah teks. str.extract dengan regex menangkapnya lewat capture group.
import pandas as pd
harga_teks = pd.Series(["Rp24.000", "Rp 28.000", "Rp20.000", "Rp 22.000"])
print(harga_teks.tolist())
print()
tangkap = harga_teks.str.extract(r"Rp\s*([\d\.]+)")
print(tangkap)
print()
harga = pd.to_numeric(tangkap[0].str.replace(".", "", regex=False))
print(harga)Output yang Dihasilkan:
['Rp24.000', 'Rp 28.000', 'Rp20.000', 'Rp 22.000']
0
0 24.000
1 28.000
2 20.000
3 22.000
0 24000
1 28000
2 20000
3 22000
Name: 0, dtype: int64Mari kita bedah pola regex r"Rp\s*([\d\.]+)" itu:
Rp: dicocokkan apa adanya — setiap nilai memuat teksRpdi depan.\s*: nol atau lebih spasi, menanganiRp24.000danRp 28.000sekaligus.([\d\.]+): tanda kurung adalah capture group — bagian yang diambil. Isinya, satu atau lebih karakter angka atau titik, menangkap24.000lengkap.- Baris 11 (
pd.to_numeric): hasil tangkapan berupa kolom teks24.000, lalu langkah lama kita mengambil alih: buang titik ribuan denganstr.replace, konversi denganpd.to_numeric— pola yang sudah kita kuasai pada chapter Memperbaiki Tipe Data . Bila pola memuat lebih dari satu capture group,extractmengembalikan satu kolom per group.
Rangkuman #
nunique()danvalue_counts()membongkar nilai yang menyamar; delapan “menu” ternyata lima menu dengan gaya penulisan berbeda.str.strip()danstr.lower()merapikan tepi dan kapital untuk analisis;str.title()hanya untuk tampilan.str.replace()menangani karakter pengganti dan — dengan regex\s+— meratakan spasi ganda; susun operasi sebagai rantai yang terbaca.str.split(..., expand=True)memecah nilai gabungan menjadi kolom;str.extract()mengambil bagian teks yang cocok pola regex.str.contains()menyaring baris berdasar teks; seluruh metode.strmenurunkanNaNtetap sebagaiNaN.
Normalisasi teks menyelesaikan masalah dalam satu kolom. Tetapi ketika dua tabel harus digabungkan — transaksi di satu sisi, daftar harga di sisi lain — perbedaan penulisan antar tabel menghasilkan kecocokan yang gagal tanpa galat. Kita tangani itu pada chapter berikutnya, Menangani Data Tidak Konsisten .