Dunia teknik penuh dengan besaran yang tersimpan rapi sebagai sepasang bilangan: impedansi rangkaian listrik AC ditulis R + jX (hambatan ditambah reaktansi), titik pada bidang dua dimensi disimpan sebagai koordinat x dan y, dan setiap komponen frekuensi hasil Transformasi Fourier membawa besaran sekaligus sudut fase. Matematika menyatukan pola-pola itu sebagai bilangan kompleks, bilangan berbentuk a + bj dengan bagian real a dan bagian imajiner b. Python punya tipe complex bawaan untuk satu nilai; NumPy membawanya ke level array dengan dtype complex64 dan complex128, sehingga seratus ribu nilai kompleks dapat dihitung sekaligus dengan kecepatan vectorization.

Bab ini mencakup empat hal:

  • dua tipe kompleks NumPy dan aturan memilihnya;
  • atribut .real dan .imag beserta np.conj, np.angle, dan np.abs;
  • aritmetika dasar array kompleks, termasuk trik rotasi vektor;
  • np.emath.sqrt sebagai cara menghitung akar bilangan negatif tanpa NaN.

Materi ini menjadi fondasi langsung untuk section Transformasi Fourier, karena output np.fft.fft berupa array complex128. Bab ini juga melengkapi deretan anggota keluarga tipe yang telah kita bahas — dari tipe numerik pada Memilih Tipe Data yang Tepat hingga tipe non-numerik pada Tipe Data String dan Object.

complex64 dan complex128 #

Satu elemen bilangan kompleks menyimpan dua float sekaligus: bagian real dan bagian imajiner. NumPy menyediakan tepat dua tipe kompleks, dan keduanya adalah pasangan tipe float yang sudah kita kenal:

TipeUkuran per elemenBagian realBagian imajiner
complex648 bytefloat32float32
complex12816 bytefloat64float64

Tanpa argumen dtype, NumPy memilih complex128 setiap kali list berisi literal seperti 3 + 4j, karena pasangan float64 adalah default presisi ganda:

python
import numpy as np

z = np.array([3 + 4j, 1 - 2j, -2 + 1j])
print(z.dtype)
z32 = z.astype(np.complex64)
print(z32)
print(z.real)
print(z.imag)

Output yang Dihasilkan:

text
complex128
[ 3.+4.j  1.-2.j -2.+1.j]
[ 3.  1. -2.]
[ 4. -2.  1.]

Mari kita bedah:

  1. Baris 3 (np.array(...)): list berisi tiga literal Python berakhiran j. Interpreter menafsirkan masing-masing sebagai complex, dan NumPy menetapkan dtype complex128 untuk arraynya.
  2. Baris 5 (z.astype(np.complex64)): konversi memangkas setiap komponen ke float32; cetakan array tetap memakai notasi .+j dengan bagian real terlebih dahulu.
  3. Baris 6-7 (z.real dan z.imag): atribut ini mengembalikan array float yang berisi komponen masing-masing. Keduanya adalah view ke data yang sama (prinsipnya dibahas pada Chapter View vs Copy), sehingga mengubah z.real[0] juga mengubah z itu sendiri.

real, imag, conj, angle, dan abs pada impedansi #

Sebagai kasus pembahasan, kita hitung impedansi tiga rangkaian AC. Impedansi ditulis Z = R + jX, dengan R hambatan (bagian real) dan X reaktansi (bagian imajiner). Dari sebuah Z, kita membaca tiga angka yang lazim dipakai teknisi: besaran impedansi |Z| lewat np.abs, sudut fase lewat np.angle (radian secara default; beri deg=True untuk derajat), dan konjugasi lewat np.conj, yang menukar tanda bagian imajiner.

python
import numpy as np

R = np.array([12.0, 4.0, 8.0])    # hambatan (ohm)
X = np.array([16.0, -3.0, 6.0])   # reaktansi (ohm)
Z = R + 1j * X                    # impedansi Z = R + jX
print(np.abs(Z).round(1))
print(np.angle(Z, deg=True).round(1))
print(np.conj(Z))
print((Z * np.conj(Z)).real.round(1))

Output yang Dihasilkan:

text
[20.  5. 10.]
[ 53.1 -36.9  36.9]
[12.-16.j  4. +3.j  8. -6.j]
[400.  25. 100.]

Bedah baris demi baris:

  1. Baris 5 (R + 1j * X): 1j * X menghasilkan array yang seluruhnya imajiner, lalu penjumlahan array menggabungkannya dengan R menjadi array complex128.
  2. Baris 6 (np.abs(Z)): menghitung sqrt(R² + X²) per elemen — 20, 5, dan 10 ohm. Inilah besaran yang dibaca multimeter.
  3. Baris 7 (np.angle(Z, deg=True)): sudut fase tiap rangkaian. Nilai positif menandakan sifat induktif (53.1°), negatif menandakan kapasitif (-36.9°).
  4. Baris 9 (Z * np.conj(Z)): mengalikan Z dengan konjugasinya menghilangkan bagian imajiner, sehingga hasilnya tepat |Z|² — 400, 25, dan 100.

Aritmetika array kompleks dan rotasi vektor #

Operator array kompleks mengikuti aljabar kompleks per elemen. Penjumlahan menjumlahkan real dengan real dan imajiner dengan imajiner; perkalian menerapkan rumus (a + bj)(c + dj) = (ac - bd) + (ad + bc)j pada setiap pasang elemen. Dari rumus itulah muncul trik yang sering dipakai pada grafika dan pengolahan sinyal: mengalikan bilangan kompleks dengan exp(1j·θ) sama dengan memutar titik sebesar θ derajat mengelilingi titik asal.

python
import numpy as np

titik = np.array([2 + 0j, 1 + 1j])   # dua titik pada bidang
rotor = np.exp(1j * np.deg2rad(90))  # faktor rotasi 90 derajat
hasil = titik * rotor
print(hasil.real.round(4))
print(hasil.imag.round(4))

Output yang Dihasilkan:

text
[ 0. -1.]
[2. 1.]
  1. Baris 3 (titik = ...): titik (2, 0) dan (1, 1) ditulis sebagai bilangan kompleks.
  2. Baris 4 (rotor = ...): exp(1j·90°) kira-kira 6.1e-17 + 1j — hampir murni imajiner. Sisa real berukuran 6.1e-17 itu bukan galat program, melainkan konsekuensi cos(90°) pada aritmetika biner yang tidak tepat nol.
  3. Baris 5-7 (perkalian dan cetak): setelah pembulatan empat desimal, 2 + 0j menjadi 0 + 2j dan 1 + 1j menjadi -1 + 1j. Kedua titik terputar 90° berlawanan arah jarum jam, persis seperti yang dijanjikan rumus.

emath: akar bilangan negatif tanpa NaN #

Fungsi akar kuadrat standar bekerja pada bilangan real, sehingga np.sqrt(-4) tidak punya jawaban real: NumPy mengembalikan nan dan melempar RuntimeWarning. Keluarga fungsi np.emath (extended math) menawarkan domain otomatis (automatic domain): ketika masukan keluar dari domain real, perhitungan diperluas ke bilangan kompleks tanpa NaN.

python
import numpy as np
import warnings

with warnings.catch_warnings(record=True) as terekam:
    warnings.simplefilter("always")
    print(np.sqrt(np.array([-4.0, 9.0])))
print("Peringatan:", terekam[0].message)
print(np.emath.sqrt(np.array([-4.0, 9.0])))
print(np.emath.sqrt(-1))

Output yang Dihasilkan:

text
[nan  3.]
Peringatan: invalid value encountered in sqrt
[0.+2.j 3.+0.j]
1j
  1. Baris 5-8 (blok with): kita menangkap RuntimeWarning menjadi objek agar pesannya tercetak sebagai output yang terukur. Hasil np.sqrt untuk -4.0 berupa nan, sedangkan 9.0 tetap 3.0.
  2. Baris 9 (np.emath.sqrt(...)): masukan negatif otomatis dialihkan ke domain kompleks — -4.0 menghasilkan 0.+2.j, dan masukan positif tetap riil sebagai 3.+0.j.
  3. Baris 10 (np.emath.sqrt(-1)): akar -1 menghasilkan satuan imajiner 1j, nilai yang tidak pernah bisa diberikan np.sqrt biasa.

Dengan tipe kompleks, atribut pemisah komponen, dan emath yang menolak menghasilkan NaN secara diam-diam, kita siap membaca output np.fft.fft pada section Transformasi Fourier. Untuk melengkapi strategi pemilihan tipe pada array data nyata, kembali ke Memilih Tipe Data yang Tepat, dan untuk perbandingan dengan keluarga tipe non-numerik, lanjutkan ke Tipe Data String dan Object.