Satu tanda kali, dua makna yang sama sekali berbeda. Sejak Chapter Operasi Aritmetika, kita memakai operator * yang mengalikan elemen pada posisi yang sama. Aljabar linear mengenal operasi lain untuk matriks: perkalian matriks, yang menjumlahkan hasil kali baris dengan kolom. NumPy memisahkan keduanya ke dua operator berbeda: * untuk elemen demi elemen dan @ untuk perkalian matriks.

Masalahnya, ketika bentuk kedua array cocok, kedua operator berjalan tanpa satu pun pesan error — tetapi hasilnya berbeda total. Salah memilih operator berarti mendapat angka yang salah secara senyap. Untuk melihat kontrasnya, kita kalikan dua matriks yang sama dengan kedua cara.

Perkalian elemen demi elemen dengan operator bintang #

python
import numpy as np

A = np.array([[1, 2],
              [3, 4]])
B = np.array([[5, 6],
              [7, 8]])
print(A * B)

Output yang Dihasilkan:

text
[[ 5 12]
 [21 32]]

Operator * memasangkan elemen pada posisi yang sama: 1*5, 2*6, 3*7, 4*8. Bentuk hasil tetap (2, 2), dan setiap sel hasil hanya melibatkan satu pasang elemen. Pola ini disebut element-wise.

Perkalian matriks dengan operator at #

python
import numpy as np

A = np.array([[1, 2],
              [3, 4]])
B = np.array([[5, 6],
              [7, 8]])
print(A @ B)

Output yang Dihasilkan:

text
[[19 22]
 [43 50]]

Kali ini hasilnya berbeda. Aturan perkalian matriks: entri baris ke-i kolom ke-j pada hasil adalah jumlah hasil kali antara baris ke-i dari A dan kolom ke-j dari B. Contoh untuk sel kiri atas: 1*5 + 2*7 = 19 — dua pasangan elemen ikut serta, bukan satu.

Tabel berikut menempatkan kedua hasil berdampingan:

PosisiA * B (elemen)A @ B (matriks)
(0, 0)1*5 = 51*5 + 2*7 = 19
(0, 1)2*6 = 121*6 + 2*8 = 22
(1, 0)3*7 = 213*5 + 4*7 = 43
(1, 1)4*8 = 323*6 + 4*8 = 50

Kedua operator mengerjakan dua hal yang berbeda pada dua matriks yang persis sama — inilah pembedaan terpenting di section ini.

Aturan kesesuaian ukuran untuk operator at #

Perkalian matriks punya aturan bentuk yang tegas: (m, n) @ (n, p) menghasilkan (m, p). Angka tengah — kolom operan kiri dan baris operan kanan — harus sama; hasilnya memakai baris operan kiri dan kolom operan kanan.

python
import numpy as np

P = np.ones((2, 3))
Q = np.ones((3, 4))
print((P @ Q).shape)
R = np.ones((2, 2))
try:
    print(P @ R)
except ValueError as e:
    print("ValueError:", e)

Output yang Dihasilkan:

text
(2, 4)
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)

Mari kita bedah:

  1. Baris 3-5: P berbentuk (2, 3) dan Q berbentuk (3, 4). Kolom P (3) sama dengan baris Q (3), sehingga perkalian sah dan hasilnya (2, 4) — baris dari P, kolom dari Q.
  2. Baris 6-10: R berbentuk (2, 2). Kolom P (3) tidak sama dengan baris R (2), sehingga NumPy memunculkan ValueError. Blok try/except menangkapnya dan mencetak pesan errornya agar blok tetap bisa dijalankan utuh.
  3. Pesan error: bagian penentu ada di ujung — size 2 is different from 3. NumPy membandingkan baris operan kedua (2) dengan kolom operan pertama (3).

Contoh: total belanja tiga pelanggan #

Aturan bentuk di atas juga berlaku ketika operan kanan berupa vektor 1-D: (m, n) @ (n,) menghasilkan (m,). Ini pola yang paling sering dipakai — menjumlahkan total dari banyak komponen sekaligus:

python
import numpy as np

belanja = np.array([[2, 1],
                    [0, 3],
                    [1, 1]])
harga = np.array([15000, 8000])
print(belanja @ harga)

Output yang Dihasilkan:

text
[38000 24000 23000]

Matriks belanja berordo 3×2 mencatat banyaknya unit yang dibeli tiga pelanggan untuk dua produk, dan harga berisi harga satuan. Hasilnya adalah total belanja per pelanggan:

  1. Pelanggan pertama: 2*15000 + 1*8000 = 38000.
  2. Pelanggan kedua: 0*15000 + 3*8000 = 24000.
  3. Pelanggan ketiga: 1*15000 + 1*8000 = 23000.

Satu operator @ menggantikan tiga kali perhitungan manual, dan pola ini akan terus kita pakai sampai akhir section. Mekanisme langkah demi langkah perkalian matriks dibahas pada Chapter Matrix Multiplication.