DataLens

Filter dengan boolean mask

Simpan hanya baris yang memenuhi kondisi, gabungkan kondisi dengan & | ~, dan pakai isin, between serta query untuk filter yang mudah dibaca.

Level: Dasar · 9 menit baca

Latihan modul ini

Soal-soal untuk pelajaran ini berjalan di browser-mu dan langsung mengecek jawabanmu.

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
Awas

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

Latihan modul ini

Soal-soal untuk pelajaran ini berjalan di browser-mu dan langsung mengecek jawabanmu.

Berikutnya: loc vs iloc →