Idenya
Sebagian besar pertanyaan dimulai dengan "hanya": hanya order yang selesai, hanya Jakarta, hanya order di atas Rp 300.000 di bulan Maret. Di spreadsheet kamu akan mengklik filter. Di pandas kamu membangun boolean mask: Series berisi True/False, satu per baris, lalu memakainya untuk menyimpan baris yang True.
import pandas as pd
sales = pd.read_csv("sales.csv")
is_completed = sales["status"] == "completed"
print(is_completed.head())
print(is_completed.sum())
0 True
1 True
2 True
3 True
4 True
Name: status, dtype: bool
45
Perbandingan ini berjalan pada setiap baris sekaligus (tervektorisasi, seperti perhitungan di modul 1) dan mengembalikan Series boolean. Karena True dihitung sebagai 1, .sum() menghitung baris yang cocok: 45 baris berstatus completed.
Menerapkan mask
Letakkan mask di dalam kurung siku, dan pandas menyimpan baris yang bernilai True:
completed = sales[is_completed]
print(completed.shape)
print(completed["revenue"].sum())
(45, 12)
11836000
Bandingkan dengan total tanpa filter sebesar 17.502.000 dari modul sebelumnya: memasukkan order yang dibatalkan dan pending akan membuat revenue terlihat hampir 50% lebih besar. Filter yang benar adalah pembeda antara laporan yang benar dan yang salah.
Biasanya kamu akan menulis kondisinya langsung di dalam kurung:
big = sales[sales["revenue"] >= 500000]
print(big[["customer", "product", "revenue"]])
customer product revenue
2 Eka Putri Pour-over Kettle 960000
6 Kartika Rahma USB-C Hub 578000
9 Lukman Hakim USB-C Hub 578000
22 Hendra Wijaya Pour-over Kettle 640000
26 Hendra Wijaya Desk Lamp LED 735000
31 Kartika Rahma USB-C Hub 578000
36 Hendra Wijaya USB-C Hub 867000
40 Kartika Rahma USB-C Hub 867000
54 Ayu Lestari French Press 600ml 555000
57 Kartika Rahma Desk Lamp LED 735000
67 Citra Dewi Pour-over Kettle 640000
Baca sales[sales["revenue"] >= 500000] sebagai "sales, di mana revenue minimal 500.000".
Menggabungkan kondisi: & | ~
Gabungkan mask dengan & (dan), | (atau) dan ~ (bukan). Setiap kondisi butuh tanda kurungnya sendiri:
jakarta_completed = sales[(sales["city"] == "Jakarta") & (sales["status"] == "completed")]
coffee_or_kitchen = sales[(sales["category"] == "Coffee") | (sales["category"] == "Kitchen")]
not_cancelled = sales[~(sales["status"] == "cancelled")]
print(len(jakarta_completed), len(coffee_or_kitchen), len(not_cancelled))
13 29 52
and, or dan not milik Python tidak bekerja pada Series; kamu akan mendapat ValueError: The truth value of a Series is ambiguous. Pakai &, |, ~. Dan karena & mengikat lebih kuat daripada ==, lupa memberi tanda kurung di setiap perbandingan juga menghasilkan error yang membingungkan. Tanda kurung di setiap kondisi, selalu.
Untuk filter yang lebih panjang, beri nama pada mask-nya. Hasilnya terbaca seperti aturan bisnis:
is_completed = sales["status"] == "completed"
is_business = sales["segment"] == "Business"
is_big = sales["revenue"] >= 300000
target = sales[is_completed & is_business & is_big]
print(target[["customer", "product", "revenue"]])
customer product revenue
22 Hendra Wijaya Pour-over Kettle 640000
26 Hendra Wijaya Desk Lamp LED 735000
31 Kartika Rahma USB-C Hub 578000
36 Hendra Wijaya USB-C Hub 867000
40 Kartika Rahma USB-C Hub 867000
41 Kartika Rahma Pour-over Kettle 320000
57 Kartika Rahma Desk Lamp LED 735000
Keanggotaan: isin
Misalkan kamu hanya ingin order dari kota-kota di Pulau Jawa. Kamu bisa menulis (city == "Jakarta") | (city == "Bandung") | ... untuk kelima kota, tapi itu panjang dan mudah salah. .isin() mengecek apakah setiap nilai ada di list yang kamu berikan:
java = ["Jakarta", "Bandung", "Semarang", "Surabaya", "Yogyakarta"]
in_java = sales[sales["city"].isin(java)]
print(len(in_java))
print(sales.loc[~sales["city"].isin(java), "city"].unique())
49
['Makassar' nan 'Medan']
Perhatikan NaN (kota yang hilang) tidak ada di list, jadi ~isin menyimpan baris-baris itu. Nilai yang hilang tidak pernah sama dengan apa pun, dan ini perlu diingat setiap kali kamu memfilter. .isna() dan .notna() memberi mask untuk nilai yang hilang dan yang ada.
Rentang: between
Untuk "harga dari 50 sampai 120 ribu", biasanya kamu butuh dua kondisi yang digabung dengan &. .between() melakukannya dalam satu langkah, yang terbaca jauh lebih mirip dengan pertanyaan yang sedang kamu ajukan:
mid_range = sales[sales["unit_price"].between(50000, 120000)]
print(mid_range["unit_price"].min(), mid_range["unit_price"].max())
55000 120000
between(low, high) secara default mengikutsertakan kedua ujungnya.
Kondisi teks: .str
Untuk kolom teks, method .str juga menghasilkan mask (modul 11 membahas .str secara mendalam):
coffee_items = sales[sales["product"].str.contains("250g")]
print(coffee_items["product"].unique())
['Arabica Gayo 250g' 'Toraja Kalosi 250g' 'Robusta Lampung 250g']
query: filter sebagai kalimat
.query() memungkinkanmu menulis kondisi sebagai string, yang sering kali lebih mudah dibaca untuk filter yang panjang:
min_revenue = 300000
result = sales.query("status == 'completed' and segment == 'Business' and revenue >= @min_revenue")
print(len(result))
7
Di dalam query, kamu bisa memakai and/or/not, dan @name merujuk ke variabel Python. Ini soal selera; kedua gaya umum dipakai di kode profesional. Pakai mana pun yang membuat aturannya paling jelas.
Menghitung dan merangkum dengan mask
Mask bukan hanya untuk memfilter. Karena nilainya 0/1, mask langsung menjawab "berapa banyak" dan "berapa porsinya":
print((sales["status"] == "cancelled").sum())
print(round((sales["status"] == "cancelled").mean(), 3))
19
0.268
.mean() dari Series boolean adalah proporsi yang bernilai True: di sini 26,8% baris dibatalkan.
Mengapa ini penting untuk kode yang bersih
Sebuah filter adalah aturan bisnis ("penjualan dihitung kalau statusnya completed dan bukan order percobaan"). Beri nama pada mask yang penting, definisikan sekali, lalu pakai ulang, supaya aturannya tinggal di satu tempat. Saat tim finance mengubah definisi penjualan yang valid, kamu cukup mengubah satu baris alih-alih mencarinya di setiap analisis.
Rangkuman
df["col"] == valuememberi boolean mask;df[mask]menyimpan baris yangTrue.mask.sum()menghitung yang cocok;mask.mean()memberi porsinya.- Gabungkan dengan
&,|,~, dan beri tanda kurung di setiap kondisi. isin([...])untuk keanggotaan,between(a, b)untuk rentang,.str.contains()untuk teks,.isna()/.notna()untuk nilai yang hilang.- Nilai yang hilang tidak pernah cocok dengan
==atauisin. df.query("...")menulis filter sebagai string yang mudah dibaca;@varmemakai variabel Python.