Error shape mismatch adalah tamu paling sering di program NumPy: data dari dua sumber berbeda jarang memiliki ukuran yang persis sama. Kabar baiknya, hampir semua kasusnya dapat didiagnosis dengan pola yang sama — cetak shape, bandingkan dimensi dari kanan, lalu putuskan apakah solusinya reshape, newaxis, atau memang data yang salah.

Bab ini mempraktikkan diagnosis tersebut pada dua error klasik, memperkenalkan np.broadcast_shapes sebagai alat simulasi, dan menutup dengan jebakan yang lebih licin: operasi yang berhasil tanpa error tetapi secara semantik salah. Prasyaratnya hanya aturan broadcasting dasar yang dibahas pada Chapter Aturan Broadcasting.

Langkah Pertama: Cetak Shape #

Sebelum menebak-nebak, biasakan mencetak atribut shape kedua array yang bermasalah. Kebiasaan kecil ini menjawab separuh pertanyaan diagnosis dalam hitungan detik:

python
import numpy as np

tinggi = np.array([1.62, 1.71, 1.55])
berat = np.array([55.0, 68.0, 49.0, 60.0])
print("shape tinggi :", tinggi.shape)
print("shape berat  :", berat.shape)
try:
    bmi_mentah = tinggi / berat
except ValueError as e:
    print("ValueError:", e)

Output yang Dihasilkan:

text
shape tinggi : (3,)
shape berat  : (4,)
ValueError: operands could not be broadcast together with shapes (3,) (4,) 

Mari kita bedah:

  1. Baris 3-4: data tinggi berisi tiga pengukuran dan data berat berisi empat; dua array ini sengaja dibuat tidak sejajar untuk mereproduksi error yang umum dijumpai saat menggabungkan data dari dua file berbeda.
  2. Baris 5-6: mencetak shape langsung mengonfirmasi ketidakcocokan: (3,) melawan (4,). Angka di pesan error dan angka di layar kita identik — diagnosis tinggal memastikan data mana yang benar.
  3. Baris 7-10: operasi tinggi / berat memicu ValueError yang dibungkus try/except agar pesan errornya menjadi output, bukan penghentian program.

Pesan error operands could not be broadcast together with shapes (3,) (4,) selalu menyebut kedua shape persis seperti yang dicetak. Kesesuaian ini yang membuat pola “cetak shape dulu” begitu efektif.

Membedah Error pada Array Dua Dimensi #

Kasus yang sedikit lebih rumit muncul saat salah satu array sudah dua dimensi. Kita ingin menambahkan bonus per baris ke matriks penjualan, tetapi penulisan langsungnya justru error:

python
import numpy as np

penjualan = np.arange(12).reshape(4, 3)
bonus = np.array([1000.0, 2000.0, 3000.0, 4000.0])
print("shape penjualan:", penjualan.shape)
print("shape bonus    :", bonus.shape)
try:
    print(penjualan + bonus)
except ValueError as e:
    print("ValueError:", e)
print("bonus baris-demi-baris:")
print(penjualan + bonus[:, np.newaxis])

Output yang Dihasilkan:

text
shape penjualan: (4, 3)
shape bonus    : (4,)
ValueError: operands could not be broadcast together with shapes (4,3) (4,) 
bonus baris-demi-baris:
[[1000. 1001. 1002.]
 [2003. 2004. 2005.]
 [3006. 3007. 3008.]
 [4009. 4010. 4011.]]

Bedah baris demi baris:

  1. Baris 3-4: penjualan berbentuk (4, 3) — empat baris cabang, tiga kolom produk — dan bonus berbentuk (4,), satu nilai bonus untuk setiap cabang.
  2. Baris 7-10: saat aturan broadcasting menyelaraskan dimensi dari kanan, axis terakhir penjualan bernilai 3 sedangkan bonus bernilai 4; keduanya berbeda dan tak ada yang bisa diperluas, sehingga muncul ValueError berpesan (4,3) (4,).
  3. Baris 11-12: solusinya adalah mengubah bonus menjadi bentuk (4, 1) lewat bonus[:, np.newaxis]; axis terakhir bernilai 1 kini dapat diperluas menjadi 3, dan setiap baris matriks mendapat bonusnya sendiri.

Perhatikan bahwa (4, 3) + (4,) error, sedangkan (4, 3) + (3,) justru berhasil — axis terakhir yang disamakan. Kalau maksud kita adalah bonus per baris, bentuk (3,) akan di-broadcast ke kolom dan hasilnya diam-diam salah. Ketepatan membaca axis inilah inti debugging shape.

np.broadcast_shapes sebagai Alat Diagnosis #

Fungsi np.broadcast_shapes menerima beberapa shape dan mengembalikan shape hasil broadcasting tanpa perlu membuat array. Fungsi ini menjadi “simulator” yang aman untuk menguji dugaan kita:

python
import numpy as np

print(np.broadcast_shapes((3, 4), (4,)))
try:
    np.broadcast_shapes((3,), (4,))
except ValueError as e:
    print("ValueError:", e)
try:
    np.broadcast_shapes((4, 3), (4,))
except ValueError as e:
    print("ValueError:", e)
print(np.broadcast_shapes((4, 3), (4, 1)))

Output yang Dihasilkan:

text
(3, 4)
ValueError: shape mismatch: objects cannot be broadcast to a single shape.  Mismatch is between arg 0 with shape (3,) and arg 1 with shape (4,).
ValueError: shape mismatch: objects cannot be broadcast to a single shape.  Mismatch is between arg 0 with shape (4, 3) and arg 1 with shape (4,).
(4, 3)

Penjelasan tiap baris:

  1. Baris 3: (3, 4) bersama (4,) menghasilkan (3, 4) — axis terakhir bernilai sama, axis pertama 3 dibiarkan.
  2. Baris 4-7: (3,) bersama (4,) tidak punya axis bernilai 1 untuk diperluas, sehingga fungsi memunculkan ValueError. Perhatikan pesannya sedikit berbeda dari pesan ufunc dan menyebut argumen mana yang bentrok.
  3. Baris 8-11: kasus (4, 3) bersama (4,) kembali terkonfirmasi sebagai benturan axis terakhir.
  4. Baris 12: (4, 3) bersama (4, 1) berhasil menjadi (4, 3) — inilah bentuk yang harus kita kejar pada blok sebelumnya, dan bonus[:, np.newaxis] adalah jalannya.

Saat Broadcasting Berhasil tetapi Hasilnya Salah #

Error adalah teman: ia memberhentikan kita di tempat kejadian. Versi yang lebih berbahaya adalah operasi yang berhasil tanpa error tetapi maknanya berbeda dari niat kita. Contoh berikut mengalikan matriks skor dengan array bobot — berhasil, tanpa keluhan:

python
import numpy as np

skor = np.array([[80, 85, 90],
                 [70, 75, 65]])
bobot = np.array([0.2, 0.5, 0.3])
print("shape skor :", skor.shape)
print("shape bobot:", bobot.shape)
hasil = skor * bobot
print("hasil broadcast:")
print(hasil)
print("shape hasil:", hasil.shape)

Output yang Dihasilkan:

text
shape skor : (2, 3)
shape bobot: (3,)
hasil broadcast:
[[16.  42.5 27. ]
 [14.  37.5 19.5]]
shape hasil: (2, 3)

Mari kita baca:

  1. Baris 3-6: skor berbentuk (2, 3) (dua mahasiswa, tiga mata pelajaran) dan bobot berbentuk (3,) (bobot per mata pelajaran).
  2. Baris 7-9: penyelarasan dari kanan membuat (3,) cocok dengan axis kolom, sehingga tiap kolom dikalikan bobotnya — kebetulan persis niat kita, terbukti dari 80 * 0.2 = 16.0 pada elemen pertama.
  3. Baris 10-11: shape hasil (2, 3) konsisten dengan np.broadcast_shapes((2, 3), (3,)).

Kasus ini berhasil karena orientasi data kebetulan benar. Masalah muncul ketika orientasinya terbalik — misalnya bobot berbentuk (2,) untuk “bobot per baris” — maka operasi tetap bisa berhasil dan mengalikan kolom dengan nilai yang salah. Dua pertahanannya sederhana:

  1. Verifikasi angka satu elemen: hitung manual satu elemen hasil dan bandingkan dengan keluaran, seperti 80 * 0.2 = 16.0 di atas.
  2. Kunci dimensi secara eksplisit: tulis bobot[np.newaxis, :] atau bobot[:, np.newaxis] meskipun broadcasting 1-D sudah cocok; kejelasan bentuk mengalahkan keberuntungan.

Checklist diagnosis shape mismatch yang bisa kita pegang: cetak shape semua array yang terlibat; simulasikan dengan np.broadcast_shapes; putuskan sisi mana yang perlu reshape atau newaxis; lalu uji satu elemen hasil secara manual. Alat pendukung lain seperti np.broadcast_arrays dan penghapusan dimensi dibahas lebih lanjut pada Chapter Menambah dan Menghapus Dimensi serta Chapter Memeriksa Kompatibilitas Bentuk.