Sensor akselerometer yang dipasang di dek jembatan pejalan kaki merekam getaran sebagai deretan angka: satu nilai tiap milidetik, ribuan nilai per detik. Rekaman semacam ini sifatnya riil — tiap sampel adalah bilangan nyata hasil pengukuran, bukan bilangan kompleks. Ketika rekaman itu ditransformasi dengan np.fft.fft, setengah spektrum yang dihasilkan sebenarnya duplikat: seperti kita buktikan pada fftfreq dan Spektrum Frekuensi, magnitude bin ke-k identik dengan magnitude bin ke-N−k.
Identitas mag[k] = mag[N−k] itu bukan keanehan, melainkan konsekuensi langsung dari sinyal yang riil. NumPy menyediakan pasangan fungsi yang memanfaatkannya: np.fft.rfft menghitung hanya setengah spektrum pertama, dan np.fft.irfft menyusun kembali sinyal riil dari setengah data itu. Fondasi bilangan kompleksnya sudah kita pelajari di Bilangan Kompleks, dan mekanisme fft/ifft-nya di Dari DFT ke FFT.
Yang kita kerjakan pada chapter ini:
- Menghitung setengah spektrum dengan
np.fft.rfftdan membandingkannya dengannp.fft.fft - Menyusun ulang sinyal riil dari setengah spektrum dengan
np.fft.irfft - Menghitung penghematan memori dari N elemen menjadi N/2 + 1 elemen
complex128
Setengah Spektrum dengan np.fft.rfft #
Kita memakai sinyal getaran jembatan yang wajar: komponen utama 3 Hz beramplitudo 1 dan harmonik keduanya 6 Hz beramplitudo 0.5, direkam satu detik penuh dengan laju sampling 1.000 Hz, ditambah derau sensor ber-seed agar skenarionya terasa nyata tanpa kehilangan determinisme. Kali ini kita bandingkan panjang output kedua fungsi dan buktikan bahwa rfft memang setengah pertama fft.
import numpy as np
fs = 1000
t = np.arange(1000) / fs # sensor getaran jembatan, 1 detik
getaran = np.sin(2 * np.pi * 3 * t) + 0.5 * np.sin(2 * np.pi * 6 * t)
rng = np.random.default_rng(42)
getaran += rng.normal(0, 0.05, t.size) # derau pengukuran sensor
spec_fft = np.fft.fft(getaran)
spec_rfft = np.fft.rfft(getaran)
print("len(fft) :", spec_fft.size)
print("len(rfft):", spec_rfft.size)
print("rfft == fft[:501]:", np.allclose(spec_fft[:501], spec_rfft))
mag = np.abs(spec_rfft)
print("mag rfft[3] =", mag[3].round(1), "| mag rfft[6] =", mag[6].round(1))
print("cermin fft[997] =", np.abs(spec_fft[997]).round(1))Output yang Dihasilkan:
len(fft) : 1000
len(rfft): 501
rfft == fft[:501]: True
mag rfft[3] = 500.2 | mag rfft[6] = 248.9
cermin fft[997] = 500.2Mari kita bedah baris demi baris:
- Baris 3–5: laju sampling 1.000 Hz dan satu detik rekaman; dua komponen getaran sesuai skenario — 3 Hz sebagai fundamental dan 6 Hz sebagai harmoniknya.
- Baris 6–7: derau
rng.normal(0, 0.05, ...)dari generator ber-seed 42 meniru ketidaksempurnaan sensor; eksekusi berapa pun menghasilkan derau yang sama. - Baris 9 (
spec_rfft = np.fft.rfft(getaran)):rfftmengembalikan 501 koefisien, bukan 1.000. Rumusnya N/2 + 1 untuk N genap. - Baris 12:
np.allclosemembandingkanfft[:501]denganrfftelemen demi elemen; hasilTruemenegaskan bahwarfftbenar-benar setengah pertama spektrum penuh. - Baris 14–15: puncak 3 Hz dan 6 Hz terbaca di bin 3 dan bin 6 dengan magnitude ~500 dan ~249 — skala (N/2) × amplitudo yang sama seperti pada fftfreq dan Spektrum Frekuensi. Bin 997 = 1000 − 3 adalah cermin bin 3: nilainya identik, dan bagian cermin semacam itulah yang tidak disimpan
rfft.
Bilangan 501 itu layak dicermati. Selain bin frekuensi positif 1 sampai 499, rfft menyertakan bin DC (indeks 0) dan — untuk N genap — bin Nyquist (indeks 500) di ujung array. Dua bin ekstra itu tidak punya pasangan cermin, sehingga totalnya 499 + 2 = 501, bukan sekadar 500.
Menyusun Kembali Sinyal dengan np.fft.irfft #
Setengah spektrum memuat informasi lengkap untuk sinyal riil, sehingga rekonstruksi tidak membutuhkan sisi cermin. np.fft.irfft menerima array setengah spektrum, menyusun cermin konjugatnya secara internal, lalu menjalankan invers. Bedanya dengan np.fft.ifft pada Dari DFT ke FFT: hasilnya langsung float64, bukan complex128, karena sinyal riil memang tidak punya bagian imajiner.
import numpy as np
fs = 1000
t = np.arange(1000) / fs
getaran = np.sin(2 * np.pi * 3 * t) + 0.5 * np.sin(2 * np.pi * 6 * t)
rng = np.random.default_rng(42)
getaran += rng.normal(0, 0.05, t.size)
spektrum = np.fft.rfft(getaran)
hasil = np.fft.irfft(spektrum, n=t.size)
print("dtype hasil :", hasil.dtype)
print("asli [:4] :", getaran[:4].round(4))
print("hasil [:4] :", hasil[:4].round(4))
print("selisih maks:", f"{np.abs(hasil - getaran).max():.2e}")Output yang Dihasilkan:
dtype hasil : float64
asli [:4] : [ 0.0152 -0.0143 0.1129 0.16 ]
hasil [:4] : [ 0.0152 -0.0143 0.1129 0.16 ]
selisih maks: 8.88e-16Uraian baris-per-barisnya:
- Baris 8 (
spektrum = np.fft.rfft(getaran)): 501 koefisien setengah spektrum, dihitung ulang agar blok ini berdiri sendiri. - Baris 9 (
hasil = np.fft.irfft(spektrum, n=t.size)): parameternmenyatakan panjang sinyal asli yang diinginkan, di sini 1.000 sampel. - Baris 10: dtype hasil
float64; kita tidak perlu memanggilnp.realseperti pada alurifft. - Baris 11–12: empat sampel pertama cocok nilai demi nilai hingga pembulatan 4 desimal — rekonstruksi berhasil dari setengah data.
- Baris 13: selisih terbesar
8.88e-16murni residu pembulatanfloat64; pada skala pengukuran sensor, praktis nol.
Berapa Memori yang Dihemat? #
Setiap elemen complex128 menampung dua float64 — 16 byte per elemen. Dengan rfft, jumlah elemen spektrum turun dari N menjadi N/2 + 1. Kita ukur langsung lewat atribut nbytes pada spektrum sinyal bersih:
import numpy as np
n = 1000
t = np.arange(n) / n
getaran = np.sin(2 * np.pi * 3 * t)
spec_fft = np.fft.fft(getaran)
spec_rfft = np.fft.rfft(getaran)
print("elemen fft :", spec_fft.size, "->", spec_fft.nbytes, "byte")
print("elemen rfft:", spec_rfft.size, "->", spec_rfft.nbytes, "byte")
print("hemat :", spec_fft.nbytes - spec_rfft.nbytes, "byte")Output yang Dihasilkan:
elemen fft : 1000 -> 16000 byte
elemen rfft: 501 -> 8016 byte
hemat : 7984 bytePenjelasannya sederhana: 1.000 elemen complex128 menempati 16.000 byte, sedangkan 501 elemen hanya 8.016 byte — hampir separuh. Skalanya makin terasa untuk rekaman panjang:
| Panjang sinyal N | Spektrum fft | Spektrum rfft |
|---|---|---|
| 1.000 | 16.000 byte | 8.016 byte |
| 1.000.000 | 16,0 MB | ≈ 8,0 MB |
| 100.000.000 | 1,6 GB | ≈ 0,8 GB |
Penghematan tidak berhenti di memori: transformasi real FFT juga menjalankan lebih sedikit operasi daripada complex FFT berukuran penuh, sehingga perhitungannya lebih ringan. Untuk rekaman jutaan sampel yang dianalisis berulang-ulang, selisih ini menentukan apakah data muat di RAM.
Dengan rfft dan irfft, seluruh alur analisis sinyal riil — membaca spektrum, menyimpannya, dan mengembalikannya — bisa dikerjakan dengan setengah data. Babak berikutnya memakai keunggulan ini untuk pekerjaan yang lebih aktif: menyaring derau langsung di domain frekuensi pada Filtering Frekuensi dan ifft.