numpyMenengah6 mnt baca

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.

python
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:

python
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.

python
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:

python
# 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.

python
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 cepat

Catatan teknis: Slicing NumPy menghasilkan view, bukan salinan. Mengubah slice mengubah array asli. Pakai .copy() untuk array independen.

Jebakan Umum

SALAH, mengira slice adalah salinan:

python
# 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:

python
# 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 utuh

SALAH, broadcasting yang bentuknya tidak cocok:

python
# 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 broadcast

BENAR, samakan bentuk dulu dengan newaxis:

python
# 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.