Chapter sebelumnya kita berhenti pada struktur: matriks menyimpan data dengan baris sebagai observasi dan kolom sebagai fitur. Struktur saja belum cukup, karena data pada akhirnya harus dihitung. Toko Kopi Kita, misalnya, perlu menjumlahkan penjualan dua pekan, menghitung pendapatan dari penjualan kali harga, hingga menghasilkan prediksi model untuk seluruh pelanggan sekaligus. Semua pekerjaan itu dikerjakan empat operasi matriks: penjumlahan, perkalian skalar, perkalian matriks, dan transpos. Pada chapter ini kita kerjakan keempatnya dari mekanisme paling dasar — loop manual pada list of lists — lalu membandingkannya dengan versi NumPy.
Penjumlahan Matriks dan Perkalian Skalar #
Penjumlahan matriks adalah operasi paling sederhana: dua matriks dijumlahkan elemen demi elemen pada posisi yang sama. Operasi ini hanya sah bila kedua matriks berbentuk identik — sama banyak baris dan sama banyak kolom — karena setiap sel harus punya pasangan yang jelas. Perkalian skalar berbeda: satu bilangan tunggal mengalikan seluruh elemen matriks, dan bentuk matriks tidak berubah. Kedua aturan tersebut dirangkum berikut:
A + B : jumlahkan elemen yang seposisi (syarat: bentuk A = bentuk B)
c * A : kalikan setiap elemen dengan c (bentuk hasil = bentuk A)Kita kerjakan keduanya dengan loop manual pada list of lists, memakai data penjualan dua pekan (satuan gelas):
def jumlah_matriks(A, B):
"""Penjumlahan dua matriks: jumlahkan elemen yang seposisi."""
n_baris = len(A)
n_kolom = len(A[0])
hasil = [[0] * n_kolom for _ in range(n_baris)]
for i in range(n_baris):
for j in range(n_kolom):
hasil[i][j] = A[i][j] + B[i][j]
return hasil
def kali_skalar(c, A):
"""Perkalian skalar: setiap elemen matriks dikali bilangan c."""
hasil = []
for baris in A:
baris_baru = []
for nilai in baris:
baris_baru.append(c * nilai)
hasil.append(baris_baru)
return hasil
# Penjualan dua pekan (gelas): baris = hari, kolom = [kopi susu, matcha]
pekan1 = [
[12, 8], # Senin
[15, 10], # Selasa
]
pekan2 = [
[14, 9], # Senin
[17, 13], # Selasa
]
total = jumlah_matriks(pekan1, pekan2)
print("Total dua pekan :", total)
target = kali_skalar(2, total)
print("Target 2x lipat :", target)Output yang Dihasilkan:
Total dua pekan : [[26, 17], [32, 23]]
Target 2x lipat : [[52, 34], [64, 46]]Mari kita telaah baris-baris kode di atas:
- Fungsi
jumlah_matriks: matriks hasil disiapkan lebih dulu sebagai wadah berisi nol ([[0] * n_kolom ...]), lalu loop dua tingkati(baris) danj(kolom) mengisi setiap sel denganA[i][j] + B[i][j]. Hanya seposisi itulah penjumlahan terjadi. - Fungsi
kali_skalar: loop luar menyusuri baris, loop dalam mengalikan setiap nilai denganc, danappendmerangkai baris baru. Tidak ada syarat bentuk karena hanya satu matriks yang terlibat. - Baris penerapan:
totalmenjumlahkan penjualan pekan pertama dan kedua — sel[0][0]berisi 12 + 14 = 26 gelas kopi susu hari Senin.targetmenggandakan total tersebut sebagai target kampanye promosi dua minggu ke depan.
Kedua fungsi memakai pola loop bersarang i-j yang sama — persis pola penjumlahan berulang pada chapter Vektor dan Matriks
.
Perkalian Matriks: Baris Dikalikan dengan Kolom #
Perkalian matriks adalah operasi terpenting sekaligus paling sering disalahpahami. Berbeda dengan penjumlahan, operasi ini tidak bekerja elemen demi elemen. Aturan bentuknya: matriks A berukuran m x n hanya bisa dikalikan dengan matriks B berukuran n x p, dan hasilnya berukuran m x p. Angka n menjadi syarat sekaligus jembatan — banyaknya kolom A wajib sama dengan banyaknya baris B.
Mekanismenya: baris dikalikan kolom. Elemen hasil pada baris i kolom j adalah dot product baris i dari A dengan kolom j dari B — pola hasil kali berpasangan yang sudah kita kenal dari chapter sebelumnya. Maknanya pada contoh bisnis kita: baris A memuat penjualan semua minuman pada satu hari, kolom B memuat satu skema harga untuk semua minuman, dan dot product keduanya menjawab “berapa total pendapatan hari itu pada skema harga tersebut”.
penjualan (2x3) @ harga (3x2) -> hasil (2x2)
hasil[Senin][normal] = 10*24 + 6*28 + 4*20 = 240 + 168 + 80 = 488Matriks penjualan berukuran 2x3 (dua hari, tiga minuman) dan harga berukuran 3x2 (tiga minuman, dua skema harga): kolom penjualan (3) cocok dengan baris harga (3), jadi perkaliannya sah. Loop manualnya sebagai berikut:
def kali_matriks(A, B):
"""Perkalian matriks: elemen (i, j) = dot product baris i A dan kolom j B."""
n_baris = len(A) # jumlah baris A -> jumlah baris hasil
n_kolom = len(B[0]) # jumlah kolom B -> jumlah kolom hasil
n_sama = len(B) # kolom A (wajib sama dengan baris B)
hasil = [[0] * n_kolom for _ in range(n_baris)]
for i in range(n_baris):
for j in range(n_kolom):
jumlah = 0
for k in range(n_sama):
jumlah += A[i][k] * B[k][j]
hasil[i][j] = jumlah
return hasil
# Penjualan (gelas): baris = hari, kolom = [kopi susu, matcha, teh tawar]
penjualan = [
[10, 6, 4], # Senin
[12, 5, 8], # Selasa
]
# Harga (ribu rupiah): baris = minuman, kolom = [harga normal, harga promo]
harga = [
[24, 19], # kopi susu
[28, 22], # matcha
[20, 15], # teh tawar
]
hasil = kali_matriks(penjualan, harga)
print("Penjualan @ harga (ribu rupiah) :")
for baris in hasil:
print(baris)Output yang Dihasilkan:
Penjualan @ harga (ribu rupiah) :
[488, 382]
[588, 458]Mari kita telaah loop tiga tingkat di dalam kali_matriks:
- Loop
i: memilih satu barisA— satu hari yang sedang dihitung. - Loop
j: memilih satu kolomB— satu skema harga yang sedang dihitung. Perpaduanidanjmenunjuk satu sel hasil. - Loop
k: menjalankan dot product — mengalikan penjualan minuman ke-kdengan harga minuman ke-k, lalu menjumlahkannya. Untuk sel[0][0]: 10*24 + 6*28 + 4*20 = 488, persis perhitungan manual di bloktextdi atas.
Hasil 488 (ribu rupiah) adalah pendapatan Senin dengan harga normal, 382 pendapatan Senin saat promo, dan baris kedua memuat angka untuk Selasa. Satu operasi menghitung empat kombinasi hari-skema sekaligus — itulah kekuatan perkalian matriks dibanding empat perkalian yang ditulis terpisah.
Transpos dan Matriks Identitas #
Transpos membalik orientasi matriks: baris menjadi kolom dan kolom menjadi baris. Matriks penjualan 2x3 yang disusun per hari berubah menjadi matriks 3x2 yang disusun per minuman — trik yang sering dipakai agar bentuk data cocok dengan operasi yang dibutuhkan:
# Penjualan (gelas): baris = hari, kolom = [kopi susu, matcha, teh tawar]
penjualan = [
[10, 6, 4], # Senin
[12, 5, 8], # Selasa
]
def transpos(A):
"""Transpos: baris menjadi kolom, kolom menjadi baris."""
n_baris = len(A)
n_kolom = len(A[0])
hasil = [[0] * n_baris for _ in range(n_kolom)]
for i in range(n_baris):
for j in range(n_kolom):
hasil[j][i] = A[i][j]
return hasil
per_minuman = transpos(penjualan)
print("Bentuk awal : 2 baris x 3 kolom (penjualan per hari)")
print("Bentuk hasil : 3 baris x 2 kolom (penjualan per minuman)")
print(per_minuman)Output yang Dihasilkan:
Bentuk awal : 2 baris x 3 kolom (penjualan per hari)
Bentuk hasil : 3 baris x 2 kolom (penjualan per minuman)
[[10, 12], [6, 5], [4, 8]]Baris pertama hasil [10, 12] kini berarti “kopi susu terjual 10 gelas Senin dan 12 gelas Selasa”. Kuncinya ada pada satu baris dalam fungsi: hasil[j][i] = A[i][j] — indeks baris dan kolom bertukar peran, dan wadah hasil disiapkan dengan ukuran yang sudah terbalik.
Satu matriks istimewa melengkapi materi ini: matriks identitas I, yaitu matriks persegi yang diagonalnya berisi 1 dan sel lainnya 0. Perannya seperti angka 1 pada perkalian bilangan biasa — mengalikan matriks apa pun dengan I mengembalikan matriks itu sendiri (A @ I = A). Matriks ini akan sering muncul sebagai alat perhitungan pada bab-bab machine learning.
I (3x3) = | 1 0 0 |
| 0 1 0 |
| 0 0 1 |Sebelum lanjut, mari kita rangkum syarat dan bentuk hasil keempat operasi:
| Operasi | Syarat | Bentuk hasil |
|---|---|---|
Penjumlahan A + B | Bentuk A = bentuk B | Sama dengan bentuk A |
Perkalian skalar c * A | Tanpa syarat | Sama dengan bentuk A |
Perkalian matriks A @ B | Kolom A = baris B | (m x n) @ (n x p) = m x p |
Transpos A.T | Tanpa syarat | Baris dan kolom bertukar |
Versi NumPy: Kode Ringkas, Hasil yang Sama #
Loop manual membuat mekanisme terlihat jelas, tetapi pada data nyata berukuran ribuan baris kita menyerahkan perhitungan ke NumPy. Array NumPy menyediakan operator + untuk penjumlahan, @ untuk perkalian matriks, dan atribut .T untuk transpos. Kode berikut menghitung ulang seluruh contoh di atas dan menempatkan versi manual di samping versi NumPy agar hasilnya bisa dibandingkan langsung:
import numpy as np
# Data yang sama dengan versi loop manual
pekan1 = [[12, 8], [15, 10]]
pekan2 = [[14, 9], [17, 13]]
penjualan = [[10, 6, 4], [12, 5, 8]]
harga = [[24, 19], [28, 22], [20, 15]]
A = np.array(pekan1)
B = np.array(pekan2)
P = np.array(penjualan)
H = np.array(harga)
# Penjumlahan: manual vs NumPy
total_manual = [[pekan1[i][j] + pekan2[i][j] for j in range(2)] for i in range(2)]
print("Penjumlahan manual :", total_manual)
print("Penjumlahan NumPy :", (A + B).tolist())
print()
# Perkalian matriks: manual vs NumPy
kali_manual = [
[sum(penjualan[i][k] * harga[k][j] for k in range(3)) for j in range(2)]
for i in range(2)
]
print("Perkalian manual :", kali_manual)
print("Perkalian NumPy :", (P @ H).tolist())
print()
# Transpos: manual vs NumPy
transpos_manual = [[penjualan[j][i] for j in range(2)] for i in range(3)]
print("Transpos manual :", transpos_manual)
print("Transpos NumPy :", P.T.tolist())
print()
# Matriks identitas: I @ H = H
I = np.eye(3, dtype=int)
print("Identitas 3x3 :")
print(I)
print("I @ H :")
print(I @ H)Output yang Dihasilkan:
Penjumlahan manual : [[26, 17], [32, 23]]
Penjumlahan NumPy : [[26, 17], [32, 23]]
Perkalian manual : [[488, 382], [588, 458]]
Perkalian NumPy : [[488, 382], [588, 458]]
Transpos manual : [[10, 12], [6, 5], [4, 8]]
Transpos NumPy : [[10, 12], [6, 5], [4, 8]]
Identitas 3x3 :
[[1 0 0]
[0 1 0]
[0 0 1]]
I @ H :
[[24 19]
[28 22]
[20 15]]Ketiga pasangan hasil tersebut identik. Fungsi np.array() mengubah list of lists menjadi array, operator + dan @ mengerjakan seluruh sel sekaligus, .T membalik orientasi, dan np.eye(3) membangun matriks identitas — terlihat pada bagian akhir bahwa I @ H mengembalikan H apa adanya. Baris-baris berlabel “manual” hanyalah versi ringkas dari loop-loop di awal chapter, ditulis sebagai list comprehension, dan menjadi bukti bahwa kedua versi sejalan.
Prediksi Model Linear sebagai X @ w #
Inilah alasan perkalian matriks begitu penting bagi data science. Model linear — dasar dari semua model regresi — memprediksi target sebagai kombinasi berbobot fitur: setiap fitur dikalikan bobotnya lalu dijumlahkan, dan kalimat itu persis definisi dot product. Karena setiap baris matriks data X adalah satu observasi, prediksi satu observasi adalah dot product baris tersebut dengan vektor bobot w — dan prediksi seluruh observasi sekaligus adalah X @ w.
Kita lihat wujudnya pada kasus sederhana: memprediksi penjualan kopi susu harian dari jumlah pengunjung kafe. Model (anggap sudah terlatih) memakai dua bobot — intersep 10 gelas sebagai nilai dasar, dan 0.5 gelas tambahan per pengunjung:
import numpy as np
# X: baris = hari (observasi), kolom = fitur [intersep, jumlah pengunjung]
X = np.array([
[1, 120], # Senin
[1, 150], # Selasa
[1, 90], # Rabu
[1, 200], # Kamis
])
# w: bobot model linear hasil pelatihan (intersep, koefisien pengunjung)
w = np.array([10, 0.5])
# Prediksi manual untuk Senin: dot product baris pertama X dengan w
baris_senin = [1, 120]
bobot = [10, 0.5]
manual_senin = sum(b * x for b, x in zip(bobot, baris_senin))
print("Prediksi Senin (manual) :", manual_senin)
prediksi = X @ w
print("Bentuk X :", X.shape, "(4 observasi, 2 fitur)")
print("Bentuk X @ w :", prediksi.shape, "(4 prediksi)")
print("Prediksi (X @ w) :", prediksi)
print("Rata-rata prediksi :", np.mean(prediksi))Output yang Dihasilkan:
Prediksi Senin (manual) : 70.0
Bentuk X : (4, 2) (4 observasi, 2 fitur)
Bentuk X @ w : (4,) (4 prediksi)
Prediksi (X @ w) : [ 70. 85. 55. 110.]
Rata-rata prediksi : 80.0Mari kita baca hasilnya:
- Prediksi manual Senin:
1*10 + 120*0.5 = 70gelas — dot product baris pertamaXdenganw, dikerjakan tanpa NumPy agar mekanismenya terlihat. X @ w: mengerjakan dot product yang sama untuk keempat baris sekaligus. Bentuk hasil(4,)menegaskan empat prediksi keluar dari satu operasi; nilai pertamanya persis 70 seperti hitungan manual.np.mean(prediksi): merata-ratakan keempat prediksi menjadi 80 gelas per hari.
Saat model linear dilatih, komputer mencari vektor bobot w yang membuat X @ w sedekat mungkin dengan nilai target sesungguhnya; algoritme pencarinya kita bahas pada chapter Regresi Linear
. Operasi NumPy yang dipakai di sepanjang jalan itu dibedah tuntas pada chapter Dasar NumPy
.
Rangkuman #
- Penjumlahan menjumlahkan elemen seposisi dan menuntut kedua matriks berbentuk sama; perkalian skalar mengalikan seluruh elemen dengan satu bilangan.
- Perkalian matriks mengikuti aturan
(m x n) @ (n x p) = (m x p); setiap sel hasil adalah dot product barisAdengan kolomB. - Transpos menukar baris dan kolom; matriks identitas berperan seperti angka 1 sehingga
A @ I = A. - NumPy menyediakan
+,@,.T, dannp.eye()dengan hasil identik versi loop manual — ingat bahwa*adalah perkalian per elemen, bukan perkalian matriks. - Prediksi model linear untuk seluruh observasi adalah satu perkalian matriks:
X @ w.
Mesin hitungnya kini lengkap: kita bisa menyusun data dalam matriks dan mengolahnya dengan empat operasi inti. Tersisa satu bahasa matematika yang perlu dikenali — kalkulus — yang muncul setiap kali kita membicarakan cara model “belajar” dari kesalahan prediksinya. Kabar baiknya, untuk kerja sehari-hari kita cukup memegang intuisinya tanpa rumus berat. Kita tutup Section ini pada chapter Konsep Dasar Kalkulus .