Idenya
Slicing memilih nilai berdasarkan posisi. Pertanyaan sungguhan memilih nilai berdasarkan kondisi: hari dengan lebih dari 60 order, produk di bawah Rp 100.000, bacaan sensor yang jelas-jelas rusak. NumPy melakukan ini dengan boolean mask: array berisi True/False, satu per elemen, yang menyatakan mana yang disimpan.
Begitulah persisnya filter pandas bekerja di balik layar: df[df["price"] > 100] juga sebuah boolean mask.
Mask
Misalkan kafe ingin tahu hari mana saja yang ramai, artinya lebih dari 60 order. Bandingkan seluruh array dengan 60, dan alih-alih satu True atau False, kamu mendapat satu jawaban per hari. Array berisi jawaban-jawaban itu disebut mask:
import numpy as np
daily_orders = np.array([42, 38, 51, 47, 60, 75, 68, 44, 39, 55, 49, 63, 80, 71])
busy = daily_orders > 60
print(busy)
[False False False False False True True False False False False True
True True]
Taruh mask di dalam kurung siku, dan kamu hanya mendapat nilai yang mask-nya True:
print(daily_orders[busy])
print(daily_orders[daily_orders < 40])
[75 68 63 80 71]
[38 39]
Biasanya kamu menulis kondisinya langsung di dalam kurung, seperti di baris kedua. Bacalah sebagai "daily orders di mana daily orders di bawah 40".
Menghitung dan mengukur dengan mask
True dihitung 1 dan False 0, jadi sum menghitung yang cocok dan mean memberi porsinya:
print((daily_orders > 60).sum()) # berapa hari yang ramai
print((daily_orders > 60).mean()) # porsi hari yang ramai
5
0.35714285714285715
.any() dan .all() menjawab pertanyaan ya/tidak: "apakah ada hari mana pun di bawah 40?", "apakah semua hari di atas 30?"
print((daily_orders < 40).any(), (daily_orders > 30).all())
True True
Menggabungkan kondisi: & | ~
Untuk menggabungkan mask, pakai & (dan), | (atau) dan ~ (bukan), dan bungkus setiap kondisi dengan tanda kurung:
weekend = np.array([False, False, False, False, False, True, True] * 2)
print(daily_orders[(daily_orders > 60) & ~weekend]) # hari kerja yang ramai
[63]
Kenapa bukan and / or? and milik Python bertanya "apakah keseluruhannya benar?", dan array berisi banyak boolean tidak punya satu jawaban:
print(daily_orders > 40 and daily_orders < 60)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Dan kenapa tanda kurung? & mengikat lebih kuat daripada >, jadi daily_orders > 40 & daily_orders < 60 dibaca sebagai daily_orders > (40 & daily_orders) < 60, yang tidak masuk akal. Tanda kurung di setiap kondisi menghindari masalah ini sepenuhnya.
Di NumPy (dan pandas), gabungkan kondisi dengan &, |, ~, dan taruh setiap kondisi di dalam tanda kurung: (a > 40) & (a < 60).
Mengubah nilai yang cocok
Mask di sisi kiri = hanya mengubah elemen yang cocok. Data bergaya sensor sering punya bacaan yang mustahil; di sini dua hari tercatat negatif karena proses import yang rusak:
orders = np.array([42, -1, 51, 47, -1, 75, 68])
orders[orders < 0] = 0
print(orders)
[42 0 51 47 0 75 68]
np.where: memilih per elemen
np.where(condition, if_true, if_false) membangun array baru, memilih dari salah satu dari dua nilai (atau array) elemen demi elemen. Ini versi tervektorisasi dari if/else:
labels = np.where(daily_orders > 60, "busy", "normal")
print(labels)
bonus = np.where(daily_orders > 60, daily_orders * 0.1, 0)
print(bonus)
['normal' 'normal' 'normal' 'normal' 'normal' 'busy' 'busy' 'normal'
'normal' 'normal' 'normal' 'busy' 'busy' 'busy']
[0. 0. 0. 0. 0. 7.5 6.8 0. 0. 0. 0. 6.3 8. 7.1]
Fancy indexing: list posisi
Berikan list (atau array) index untuk mengambil beberapa elemen sekaligus, dalam urutan apa pun:
products = np.array(["French Press", "Kettle", "Arabica", "Robusta", "Toraja"])
prices = np.array([185, 320, 95, 65, 110])
print(prices[[1, 4]])
print(products[[4, 0, 2]])
[320 110]
['Toraja' 'French Press' 'Arabica']
Ini paling bersinar bersama np.argsort, yang mengembalikan posisi yang akan mengurutkan sebuah array. Pakai posisi itu untuk mengurutkan satu array berdasarkan array lain:
order = np.argsort(prices)[::-1] # posisi dari yang termahal ke termurah
print(order)
print(products[order])
print(prices[order])
[1 0 4 2 3]
['Kettle' 'French Press' 'Toraja' 'Arabica' 'Robusta']
[320 185 110 95 65]
Karena products dan prices di-index dengan posisi yang sama, nama-nama tetap cocok dengan harganya. Itulah trik "urutkan berdasarkan kolom lain", gaya NumPy.
Mask di 2-D
Mask juga bekerja pada grid. Hasilnya diratakan menjadi 1-D, karena nilai-nilai yang cocok tidak membentuk persegi panjang:
units = np.array([
[120, 135, 150, 160],
[ 80, 85, 90, 88],
[ 95, 92, 105, 110],
])
print(units[units > 100])
[120 135 150 160 105 110]
Untuk menyimpan baris utuh, bangun mask 1-D atas baris-barisnya, misalnya toko yang bulan pertamanya di atas 90:
print(units[units[:, 0] > 90])
[[120 135 150 160]
[ 95 92 105 110]]
Mengapa ini penting untuk kode yang bersih
Mask yang diberi nama mengubah filter menjadi kalimat: busy = daily_orders > 60, lalu daily_orders[busy & ~weekend]. Lebih cepat daripada loop dengan if, dan terbaca seperti pertanyaan bisnisnya. Kebiasaan yang sama langsung terbawa ke pandas, di mana hampir setiap filter adalah mask.
Rangkuman
- Perbandingan menghasilkan boolean mask;
a[mask]menyimpan posisi yangTrue. mask.sum()menghitung yang cocok,mask.mean()memberi porsinya;.any()/.all()menjawab ya/tidak.- Gabungkan dengan
&,|,~dan tanda kurung di setiap kondisi; jangan pernahand/or. a[mask] = valuemengubah elemen yang cocok;np.where(cond, x, y)memilih per elemen.- Fancy indexing:
a[[i, j, k]];np.argsortmemberi posisi yang mengurutkan sebuah array.