Setiap perhitungan yang kita lakukan pada dua chapter sebelumnya — dot product, gradien, mean, simpangan baku — dieksekusi oleh satu pustaka: NumPy. scikit-learn menerima datanya dalam bentuk ndarray, pandas menyimpan kolomnya di atas ndarray, dan jaringan saraf pada dasarnya hanya rangkaian operasi array berlapis. Penguasaan NumPy karena itu bukan pelengkap, melainkan prasyarat teknis untuk membaca, menulis, dan men-debug kode machine learning. Chapter ini melatih enam keterampilan inti: membuat array, mengirisnya, menyaring dengan mask, menyiarkan operasi (broadcasting), merangkum per sumbu (axis), dan menggantikan loop dengan vektorisasi.
ndarray: Wadah Data Machine Learning #
ndarray adalah kumpulan angka berdimensi bebas yang selalu homogen: satu array hanya menyimpan satu jenis data (dtype), dan bentuknya (shape) menjelaskan susunan baris-kolomnya. Array dua dimensi berbentuk (4, 3) adalah cara NumPy merepresentasikan dataset 4 sampel × 3 fitur — konvensi yang sama dengan scikit-learn. dtype juga menentukan presisi dan memori: int64 untuk bilangan bulat, float64 untuk desimal yang dipakai hampir semua model.
File array-dasar.py memperkenalkan pembuatan array dari list maupun pola (zeros, ones, arange, linspace), membaca atribut bentuk, lalu mengiris array dengan notasi [baris, kolom].
# ndarray: pembuatan, bentuk, dtype, dan potongan (slicing).
import numpy as np
np.set_printoptions(precision=2, suppress=True)
# Matriks 2 baris x 3 kolom: cara paling langsung membuat ndarray
a = np.array([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]])
print("Array a:")
print(a)
print("shape :", a.shape)
print("ndim :", a.ndim)
print("dtype :", a.dtype)
# Pembuatan dengan pola: nol, satu, rentang, dan pembagian rata
print("\nzeros(2, 3):\n", np.zeros((2, 3)))
print("ones(3) :", np.ones(3))
print("arange(0, 10, 2):", np.arange(0, 10, 2))
print("linspace(0, 1, 5):", np.linspace(0, 1, 5))
# dtype menentukan jenis dan presisi data yang disimpan
bil_bulat = np.array([1, 2, 3])
bil_desimal = np.array([1, 2, 3], dtype=np.float64)
print("\ndtype bawaan :", bil_bulat.dtype)
print("dtype float64:", bil_desimal.dtype)
# Slicing: notasi [baris, kolom] dengan pola start:stop
print("\nBaris ke-0 :", a[0, :])
print("Kolom ke-2 :", a[:, 2])
print("Blok a[0:2, 1:3]:\n", a[0:2, 1:3])
print("Elemen terakhir:", a[-1, -1])
# Vektorisasi: operasi berlaku ke seluruh elemen tanpa loop
print("\na * 2 + 1:\n", a * 2 + 1)Output yang Dihasilkan:
Array a:
[[1. 2. 3.]
[4. 5. 6.]]
shape : (2, 3)
ndim : 2
dtype : float64
zeros(2, 3):
[[0. 0. 0.]
[0. 0. 0.]]
ones(3) : [1. 1. 1.]
arange(0, 10, 2): [0 2 4 6 8]
linspace(0, 1, 5): [0. 0.25 0.5 0.75 1. ]
dtype bawaan : int64
dtype float64: float64
Baris ke-0 : [1. 2. 3.]
Kolom ke-2 : [3. 6.]
Blok a[0:2, 1:3]:
[[2. 3.]
[5. 6.]]
Elemen terakhir: 6.0
a * 2 + 1:
[[ 3. 5. 7.]
[ 9. 11. 13.]]Mari kita telaah baris-baris kode yang berperan penting:
- Baris 8–14 (
adan atributnya): array dibuat dari list bersarang;shape(2, 3) danndim2 menggambarkan susunannya,dtypefloat64 karena semua elemen desimal. - Baris 17–20 (pola pembuatan):
zerosdanonesmenyiapkan wadah kosong (matriks bobot awal, misalnya),arangemembuat rentang berlangkah,linspacemembagi rentang menjadi jumlah titik yang sama rata. - Baris 23–26 (dtype): list
[1, 2, 3]otomatis menjadiint64; parameterdtype=np.float64memaksanya menjadi desimal — penting sebelum dipakai pembagian atau masuk ke estimator. - Baris 29–32 (slicing):
a[0, :]mengambil satu baris (satu sampel),a[:, 2]mengambil satu kolom (satu fitur untuk seluruh sampel) — pola yang dipakai terus-menerus saat menyiapkan data. - Baris 35 (vektorisasi):
a * 2 + 1mengubah keenam elemen sekaligus tanpa loop; kode yang sama pada list Python memicu galat.
Boolean Mask: Menyaring Data #
Pekerjaan sehari-hari machine learning penuh pertanyaan penyaringan: baris mana yang pencilan, sampel mana yang salah prediksi, transaksi mana di atas ambang. NumPy menjawabnya dengan mask — array boolean yang dihasilkan langsung dari perbandingan, lalu dipasang sebagai indeks untuk memilih baris yang True. Operator & (dan), | (atau), dan ~ (tidak) menggabungkan beberapa kondisi; keduanya harus ditulis dengan tanda kurung karena operator pembanding lebih rapat daripada &.
File masking-data.py menyaring data pelanggan sintetis: siapa yang berusia di bawah 35, siapa yang berpengeluaran di atas 2,0 juta rupiah, dan siapa yang memenuhi keduanya.
# Boolean mask: menyaring baris data dengan kondisi logika.
import numpy as np
np.set_printoptions(precision=2, suppress=True)
rng = np.random.default_rng(42)
# Data 8 pelanggan: kolom [usia, pengeluaran bulanan (juta rupiah)]
usia = rng.integers(20, 55, size=8)
pengeluaran = rng.uniform(0.5, 5.0, size=8).round(2)
data = np.column_stack([usia, pengeluaran])
print("Data pelanggan:\n", data)
# Mask: array boolean sepanjang jumlah baris
mask_muda = usia < 35
mask_royal = pengeluaran > 2.0
print("\nMask usia < 35 :", mask_muda)
print("Mask pengeluaran > 2.0:", mask_royal)
print("Kombinasi keduanya (&):", mask_muda & mask_royal)
# Seleksi baris: cukup pasang mask sebagai indeks
print("\nPelanggan usia < 35:\n", data[mask_muda])
print("Pelanggan usia < 35 dan pengeluaran > 2.0:\n", data[mask_muda & mask_royal])
print("Jumlah pelanggan royal:", int(mask_royal.sum()))
# np.where: ubah kondisi menjadi label kategori
label = np.where(pengeluaran > 2.0, "royal", "reguler")
print("\nLabel pelanggan:", label)Output yang Dihasilkan:
Data pelanggan:
[[23. 0.92]
[47. 4.89]
[42. 3.93]
[35. 4.04]
[35. 1.08]
[50. 2.53]
[23. 2.17]
[44. 4.67]]
Mask usia < 35 : [ True False False False False False True False]
Mask pengeluaran > 2.0: [False True True True False True True True]
Kombinasi keduanya (&): [False False False False False False True False]
Pelanggan usia < 35:
[[23. 0.92]
[23. 2.17]]
Pelanggan usia < 35 dan pengeluaran > 2.0:
[[23. 2.17]]
Jumlah pelanggan royal: 6
Label pelanggan: ['reguler' 'royal' 'royal' 'royal' 'reguler' 'royal' 'royal' 'royal']Bedah kode di atas baris demi baris:
- Baris 10–13 (persiapan data): usia dan pengeluaran dibuat generator berseed, lalu
np.column_stackmenumpuk keduanya sebagai kolom menjadi matriks (8, 2). - Baris 16–20 (pembuatan mask): perbandingan
usia < 35menghasilkan array boolean delapan elemen;mask_muda & mask_royalbernilaiTruehanya pada baris yang memenuhi keduanya — perhatikan hanya pelanggan berusia 23 dengan pengeluaran 2,17 yang lolos. - Baris 23–25 (seleksi baris):
data[mask_muda]mempertahankan baris yangTruedan membuang sisanya; hasil kombinasi tinggal satu baris[[23. 2.17]].mask_royal.sum()menghitung enam nilaiTrue— jumlah pelanggan royal tanpa loop. - Baris 28–29 (
np.where): kondisi diubah menjadi label teks per elemen, pola yang berguna untuk membuat kolom kategori dari ambang numerik.
Broadcasting dan Agregasi per Sumbu #
Dua mekanisme membuat NumPy ringkas sekaligus cepat. Pertama, agregasi axis: X.mean(axis=0) merangkum ke bawah sehingga hasilnya satu angka per kolom (statistik tiap fitur), sedangkan axis=1 merangkum ke samping sehingga hasilnya satu angka per baris (ringkasan tiap sampel). Kedua, broadcasting: operasi antara bentuk yang berbeda tetap berjalan selama dimensi yang tidak sama bernilai 1 — vektor (3,) otomatis diperluas ke setiap baris matriks (4, 3) tanpa disalin.
Kombinasi keduanya memungkinkan normalisasi seluruh dataset dalam satu baris. File broadcasting-dan-agregasi.py mendemonstrasikannya: Z = (X - mu) / sigma mengurangi setiap kolom dengan mean-nya sendiri dan membaginya dengan simpangan baku kolom itu — persis definisi StandardScaler.
# Broadcasting dan agregasi per sumbu: normalisasi fitur ala StandardScaler.
import numpy as np
np.set_printoptions(precision=2, suppress=True)
# Data 4 rumah x 3 fitur: [luas (m2), kamar, jarak stasiun (km)]
X = np.array([
[70.0, 3.0, 1.5],
[120.0, 4.0, 0.8],
[85.0, 3.0, 2.0],
[150.0, 5.0, 1.0],
])
# Agregasi: axis menentukan sumbu yang diringkas
print("Mean seluruh elemen :", f"{X.mean():.3f}")
print("Mean per fitur (axis=0):", X.mean(axis=0))
print("Mean per baris (axis=1):", X.mean(axis=1))
print("Sum per fitur (axis=0) :", X.sum(axis=0))
print("Max per baris (axis=1) :", X.max(axis=1))
# Broadcasting: vektor (3,) dikalikan ke setiap baris matriks (4, 3)
skala = np.array([0.01, 1.0, 10.0])
X_skala = X * skala
print("\nX * skala (broadcasting kolom):\n", X_skala)
# Normalisasi fitur ala StandardScaler: z = (x - mean) / std per kolom
mu = X.mean(axis=0)
sigma = X.std(axis=0)
Z = (X - mu) / sigma
print("\nMean tiap fitur:", mu)
print("Std tiap fitur :", sigma)
print("Z = (X - mu) / sigma:\n", Z)
print("Cek: mean Z per fitur:", Z.mean(axis=0))
print("Cek: std Z per fitur :", Z.std(axis=0))Output yang Dihasilkan:
Mean seluruh elemen : 37.108
Mean per fitur (axis=0): [106.25 3.75 1.32]
Mean per baris (axis=1): [24.83 41.6 30. 52. ]
Sum per fitur (axis=0) : [425. 15. 5.3]
Max per baris (axis=1) : [ 70. 120. 85. 150.]
X * skala (broadcasting kolom):
[[ 0.7 3. 15. ]
[ 1.2 4. 8. ]
[ 0.85 3. 20. ]
[ 1.5 5. 10. ]]
Mean tiap fitur: [106.25 3.75 1.32]
Std tiap fitur : [31.1 0.83 0.47]
Z = (X - mu) / sigma:
[[-1.17 -0.9 0.38]
[ 0.44 0.3 -1.13]
[-0.68 -0.9 1.45]
[ 1.41 1.51 -0.7 ]]
Cek: mean Z per fitur: [ 0. -0. 0.]
Cek: std Z per fitur : [1. 1. 1.]Bedah kode di atas baris demi baris:
- Baris 16–20 (agregasi):
axis=0meruntuhkan baris dan meninggalkan tiga angka — mean tiap fitur[106.25, 3.75, 1.32];axis=1meruntuhkan kolom dan meninggalkan empat angka — mean tiap rumah. - Baris 23–25 (broadcasting): vektor
skalaberbentuk(3,)dikalikan ke matriks(4, 3); NumPy memperlakukan vektor itu seolah-olah disalin ke setiap baris, mengubah satuan tiap kolom tanpa loop. - Baris 28–30 (normalisasi):
mudansigmaberbentuk(3,); pengurangan(4, 3) − (3,)menyebar ke setiap baris lewat broadcasting sehingga setiap kolom ternormalisasi dengan statistiknya sendiri. - Baris 34–35 (verifikasi): mean Z per fitur bernilai nol dan simpangan baku bernilai satu — angka
-0.pada keluaran adalah nol negatif, artefak pembulatan floating point yang nilainya tetap nol.
Vektorisasi Menggantikan Loop #
Aturan praktis menulis kode NumPy: jika kita menulis for yang mengisi array elemen per elemen, hampir selalu ada versi bervektor yang lebih pendek dan puluhan kali lebih cepat, karena loop Python berjalan di interpreter sedangkan operasi array berjalan di kode C yang terkompilasi. a * 2 + 1, X @ w, np.abs(sampel) <= 1 pada chapter-chapter sebelumnya adalah contoh vektorisasi: satu ekspresi untuk seluruh array. Kebiasaan ini nanti menentukan kualitas feature engineering kita — transformasi yang ditulis vektorisasi tetap cepat saat dataset membesar dari puluhan baris menjadi jutaan.
| Operasi | Contoh | Peran di machine learning |
|---|---|---|
| Pembuatan array | np.zeros, np.arange, np.array | Menyiapkan matriks fitur dan bobot |
| Slicing | X[:, 0], X[0:2, 1:3] | Memilih fitur, membagi data |
| Boolean mask | X[X[:, 0] > 70] | Menyaring baris, deteksi pencilan |
| Agregasi axis | X.mean(axis=0) | Statistik per fitur untuk scaling |
| Broadcasting | (X - mu) / sigma | Normalisasi seluruh dataset sekaligus |
| Vektorisasi | X @ w | Prediksi satu batch tanpa loop |
Array NumPy menjadi mesin hitung kita, tetapi data nyata datang dalam bentuk tabel berlabel kolom, baris bernama, dan tipe campuran — dan di situlah pandas bekerja. Kita akan berkenalan dengan DataFrame pada Chapter Pandas untuk Machine Learning.
Referensi Resmi #
- NumPy: the absolute basics for beginners — pengenalan resmi ndarray, slicing, dan mask.
- Broadcasting — NumPy User Guide — aturan resmi broadcasting.
- NumPy: the fundamental package — quickstart — tur berbagai operasi inti NumPy.