NumPy Lanjutan: Indexing 2D & Broadcasting
Kuasai array 2 dimensi: slicing baris-kolom, broadcasting, dan operasi vektor yang jauh lebih cepat dari loop.
Spreadsheet dalam Bentuk Array
Kalau array 1D itu satu baris angka, array 2D itu tabel mini: baris dan kolom. Data tabular (penjualan per cabang per bulan, misalnya) secara alami berbentuk 2D, jadi mengirisnya adalah skill inti.
Kenapa penting untuk data analyst? NumPy adalah mesin di bawah pandas. Paham indexing 2D dan broadcasting berarti paham kenapa operasi pandas terasa instan, dan kapan turun langsung ke NumPy untuk komputasi berat.
Indexing dan Slicing Array 2D
Indeks ditulis [baris, kolom], dipisah koma.
import numpy as np
# 3 cabang x 4 bulan penjualan (dalam juta)
data = np.array([
[120, 150, 130, 170], # cabang A
[90, 110, 100, 120], # cabang B
[200, 210, 190, 230], # cabang C
])
print(data[0, 1]) # 150 (baris 0, kolom 1)
print(data[2, :]) # [200 210 190 230] (seluruh baris 2)
print(data[:, 0]) # [120 90 200] (seluruh kolom 0)
print(data[0:2, 1:3])
# [[150 130]
# [110 100]]Boolean indexing, memilih elemen yang memenuhi syarat:
mask = data[0] > 140
print(data[0][mask]) # [150 170]
# Ganti semua nilai di bawah 100 jadi 100 (flooring)
data[data < 100] = 100
print(data[1]) # [100 110 100 120]Broadcasting: Operasi Beda Bentuk
Broadcasting adalah cara NumPy "meregangkan" array kecil agar pas dengan array besar, tanpa menduplikasi data di memori.
harga = np.array([18000, 12000, 20000])
# Tambah array 1D (3,) ke tiap baris array 2D (2, 3)
diskon = np.array([
[2000, 1000, 3000],
[1500, 500, 2500],
])
print(diskon + harga)
# [[20000 13000 23000]
# [19500 12500 22500]]Contoh realistis, normalisasi nilai ujian ke skala 0-100 per mata pelajaran:
# 4 siswa x 3 mapel, tiap mapel punya nilai maksimum berbeda
nilai = np.array([
[80, 45, 28],
[90, 40, 25],
[70, 48, 30],
[85, 42, 27],
])
maks = np.array([100, 50, 30]) # nilai maksimum tiap mapel
skor = nilai / maks * 100 # broadcasting: (4,3) / (3,)
print(skor.round(1))
# [[ 80. 90. 93.3]
# [ 90. 80. 83.3]
# [ 70. 96. 100. ]
# [ 85. 84. 90. ]]Operasi Vektor vs Loop: Bedanya Jauh
Ini alasan utama NumPy ada: operasi vektor jalan di kode C yang teroptimasi, bukan loop Python yang lambat.
import time
besar = np.arange(1_000_000)
mulai = time.time()
hasil_vektor = besar * 2 + 10
print(f"Vektor: {time.time() - mulai:.4f} detik")
mulai = time.time()
hasil_loop = np.array([x * 2 + 10 for x in besar])
print(f"Loop: {time.time() - mulai:.4f} detik")
# Vektor biasanya 50-100x lebih cepatCatatan teknis: Slicing NumPy menghasilkan view, bukan salinan. Mengubah slice mengubah array asli. Pakai
.copy()untuk array independen.
Jebakan Umum
SALAH, mengira slice adalah salinan:
# Jangan: berharap data asli aman
a = np.array([1, 2, 3, 4])
b = a[1:3]
b[0] = 99
print(a) # [ 1 99 3 4] <- array asli ikut berubah!BENAR, pakai .copy() untuk salinan mandiri:
# Do: salinan independen
a = np.array([1, 2, 3, 4])
b = a[1:3].copy()
b[0] = 99
print(a) # [1 2 3 4] <- asli tetap utuhSALAH, broadcasting yang bentuknya tidak cocok:
# Jangan: (3,) tidak bisa broadcast dengan (2,) secara langsung
x = np.array([1, 2, 3])
y = np.array([10, 20])
print(x + y) # ValueError: operands could not be broadcastBENAR, samakan bentuk dulu dengan newaxis:
# Lakukan: ubah y jadi kolom (2, 1) agar broadcast dengan (3,) jadi (2, 3)
x = np.array([1, 2, 3])
y = np.array([10, 20])
print(x + y[:, np.newaxis])
# [[11 12 13]
# [21 22 23]]Tantangan
Normalisasi nilai dengan broadcasting
Diberi array 2D nilai 3 siswa x 4 kuis: [[80, 70, 90, 60], [50, 85, 75, 95], [90, 90, 80, 70]] dan array bobot [0.1, 0.2, 0.3, 0.4]. Hitung nilai akhir tiap siswa (rata-rata berbobot) HANYA dengan operasi vektor dan broadcasting, tanpa loop Python.