Chapter Fungsi Universal (ufunc) memperkenalkan ufunc (universal function) sebagai fungsi yang bekerja elemen demi elemen dengan kecepatan C. Yang belum dibahas: ufunc bukan sekadar fungsi — ia adalah objek yang membawa beberapa method siap pakai. np.add.reduce, np.add.accumulate, np.multiply.outer, dan np.add.at adalah method milik ufunc yang sama dengan np.add, masing-masing mengubah pola pemakaiannya.
Bab ini membahas empat method tersebut, lalu menutup dengan dua pembungkus Python untuk menjadikan fungsi buatan sendiri berperilaku seperti ufunc — beserta biaya yang menyertainya.
Metode reduce dan accumulate #
reduce melipat seluruh array menjadi satu nilai dengan operasi berulang, sedangkan accumulate menyimpan hasil pelipatan setiap langkah:
import numpy as np
nilai = np.array([4, 7, 2, 9])
print(np.add.reduce(nilai))
print(np.add.accumulate(nilai))
print(np.maximum.reduce(nilai))Output yang Dihasilkan:
22
[ 4 11 13 22]
9Mari kita bedah:
- Baris 4 (
np.add.reduce(nilai)): menjumlahkan berantai4 + 7 + 2 + 9menjadi22— hasil identik dengannilai.sum(). - Baris 5 (
np.add.accumulate(nilai)): menjalankan penjumlahan berjalan:4, lalu4+7=11,11+2=13,13+9=22. Hasilnya identik dengannp.cumsum(nilai). - Baris 6 (
np.maximum.reduce(nilai)): setiap ufunc bisa dilipat;maximummelipat perbandingan dua-dua dan menghasilkan nilai terbesar, setara dengannilai.max().
Nilai method ini bukan sekadar gula sintaksis: setiap ufunc yang tersedia — termasuk ufunc buatan sendiri — otomatis mendapat keempat method bab ini.
Tabel Kombinasi dengan outer #
outer menerapkan ufunc pada seluruh pasangan elemen dua array, menghasilkan matriks kombinasi. Ini cara paling ringkas membuat tabel perkalian, misalnya harga dikali banyaknya unit:
import numpy as np
harga = np.array([15000, 20000])
jumlah = np.array([1, 2, 3])
print(np.multiply.outer(harga, jumlah))Output yang Dihasilkan:
[[15000 30000 45000]
[20000 40000 60000]]Penjelasan:
- Baris 5 (
np.multiply.outer(harga, jumlah)): setiap elemenhargadipasangkan dengan setiap elemenjumlah; hasilnya matriks2 x 3di manahasil[i, j] = harga[i] * jumlah[j]. - Bentuknya sama dengan broadcasting
harga.reshape(2, 1) * jumlahyang dibahas pada Chapter Aturan Broadcasting —outerhanya menuliskannya secara eksplisit. outerbekerja pada ufunc apa pun:np.subtract.outermenghasilkan tabel selisih,np.greater.outermenghasilkan tabel boolean.
add.at untuk Indeks yang Berulang #
Method at melakukan operasi tanpa buffering — penting saat indeks target berulang. Bandingkan dengan fancy indexing biasa yang memanggil operasi sekali per langkah:
import numpy as np
saldo = np.array([100, 100, 100])
idx = np.array([0, 1, 0])
np.add.at(saldo, idx, 50)
print(saldo)
saldo2 = np.array([100, 100, 100])
saldo2[idx] += 50
print(saldo2)Output yang Dihasilkan:
[200 150 100]
[150 150 100]Bedah baris demi baris:
- Baris 5 (
np.add.at(saldo, idx, 50)):add.atmenambahkan50sebanyak tiga kali berturut-turut ke indeks0,1,0. Indeks0dikenai dua penambahan, sehingga menjadi200. - Baris 8 (
saldo2[idx] += 50): fancy indexing yang dibahas pada Chapter Fancy Indexing membacasaldo2[idx]sebagai salinan sementara, menambahkan50pada salinan itu, lalu menulis balik satu kali. Penambahan kedua ke indeks0menimpa yang pertama, bukan menumpuk. - Hasilnya
150pada indeks0— berbeda dari200versiat. Untuk agregasi berbasis indeks yang berulang,atadalah method yang benar.
Membungkus Fungsi Python: frompyfunc dan vectorize #
Fungsi Python biasa dapat dijadikan “ufunc” dengan np.frompyfunc atau np.vectorize. Keduanya menjalankan fungsi Python per elemen — kenyamanan meningkat, kecepatan tidak:
import numpy as np
def klasifikasi(x):
return "lunas" if x >= 0 else "hutang"
vf = np.frompyfunc(klasifikasi, 1, 1)
a1 = vf(np.array([-1, 0, 5]))
print(a1, a1.dtype)
vv = np.vectorize(klasifikasi)
a2 = vv(np.array([-1, 0, 5]))
print(a2, a2.dtype)Output yang Dihasilkan:
['hutang' 'lunas' 'lunas'] object
['hutang' 'lunas' 'lunas'] <U6Penjelasan tiap bagian:
- Baris 6 (
np.frompyfunc(klasifikasi, 1, 1)): dua angka menyatakan jumlah input dan output fungsi. Hasilnya berdtypeobject— setiap elemen adalah objek string Python biasa. - Baris 9 (
np.vectorize(klasifikasi)): tipe output disimpulkan dari pemanggilan pertama, sehingga hasilnya array string Unicode<U6yang lebih nyaman dipakai. - Argumen
otypespadanp.vectorizedapat memaksa tipe output secara eksplisit bila penyimpulan otomatis tidak diinginkan.
Ringkasan method ufunc:
| Method | Peran | Padanan lazim |
|---|---|---|
reduce | lipat semua elemen jadi satu nilai | sum(), max() |
accumulate | simpan hasil pelipatan tiap langkah | np.cumsum |
outer | semua pasangan elemen dua array | broadcasting manual |
at | operasi di indeks tanpa buffering | a[idx] += ... (berisiko jika indeks berulang) |
Kapan Butuh apply_along_axis #
frompyfunc dan vectorize di atas membungkus fungsi per elemen. Kadang fungsi kita justru membutuhkan satu baris atau satu kolom utuh — misalnya menghitung rentang (max − min) tiap baris. Untuk pola itu NumPy menyediakan np.apply_along_axis: fungsi Python biasa diterapkan pada setiap potongan 1D sepanjang axis tertentu, lalu hasil tiap potongan disusun menjadi array baru:
import numpy as np
def rentang(baris):
return baris.max() - baris.min()
def normalisasi(baris):
return (baris - baris.min()) / (baris.max() - baris.min())
penjualan = np.array([[120, 300, 250], [80, 90, 400], [200, 350, 150]])
print(np.apply_along_axis(rentang, 1, penjualan))
print(np.apply_along_axis(normalisasi, 1, penjualan).round(3))
print(np.apply_along_axis(rentang, 0, penjualan))
print(np.ptp(penjualan, axis=1))Output yang Dihasilkan:
[180 320 200]
[[0. 1. 0.722]
[0. 0.031 1. ]
[0.25 1. 0. ]]
[120 260 250]
[180 320 200]Penjelasan:
- Baris 10-11 (argumen
axis=1): fungsi menerima satu baris utuh per pemanggilan;rentangmengembalikan satu angka per baris, sedangkannormalisasimengembalikan array sepanjang baris sehingga bentuk asli array kembali. - Baris 12 (argumen
axis=0): potongan yang diteruskan kini berupa kolom, sehingga rentang dihitung antartoko untuk setiap produk. - Baris 13 (
np.ptp): rentang max − min ternyata memiliki padanan ufunc bawaan; hasilnya identik dengan versiapply_along_axis.
Catatan performa: np.apply_along_axis adalah loop Python terselubung — fungsi kita dipanggil sekali per potongan, tanpa kecepatan C milik ufunc. Karena itu, cari dulu alternatif vektor penuh seperti np.ptp pada baris terakhir contoh; gunakan apply_along_axis hanya untuk fungsi yang memang tidak dapat ditulis sebagai operasi array.