Pada bab-bab sebelumnya kita mengambil satu elemen atau rentang elemen yang berurutan. Sering kali posisi data yang dibutuhkan justru tersebar: “ambil harga produk ke-0, ke-2, dan ke-4”, atau “tampilkan baris Cabang Medan dan Cabang Bandung dalam urutan itu”. Menulis slice untuk tiap posisi tentu merepotkan. Untuk kebutuhan ini NumPy menyediakan fancy indexing (juga disebut integer array indexing): mengindeks array menggunakan list atau array yang berisi banyak indeks sekaligus.

Bab ini membahas empat hal: seleksi banyak elemen pada array satu dimensi, pola pasangan indeks pada array dua dimensi yang sering disalahpahami, pola grid untuk mengambil submatriks, dan sifat hasil fancy indexing yang selalu berupa copy.

Memilih Banyak Elemen dengan Daftar Indeks #

Cukup berikan daftar indeks di dalam tanda kurung siku ganda, dan NumPy mengembalikan elemen-elemen pada posisi tersebut dalam urutan daftar.

python
import numpy as np

harga = np.array([15000, 4500, 20000, 3000, 12500])
print(harga[[0, 2, 4]])
print(harga[[-1, -2]])
print(harga[[0, 2, 4]].shape)

Output yang Dihasilkan:

text
[15000 20000 12500]
[12500  3000]
(3,)

Mari kita periksa baris demi baris:

  1. Baris 3 (harga[[0, 2, 4]]): daftar Python [0, 2, 4] otomatis diubah menjadi array indeks. Hasilnya [15000 20000 12500] — urutan hasil mengikuti urutan daftar indeks, bukan urutan asli data.
  2. Baris 4 (harga[[-1, -2]]): indeks negatif juga sah dalam fancy indexing, sehingga [-1, -2] mengambil dua elemen terakhir dalam urutan terbalik.
  3. Baris 5 (harga[[0, 2, 4]].shape): bentuk hasil ditentukan oleh bentuk daftar indeks; daftar satu dimensi berisi tiga angka menghasilkan (3,).

Pola Pasangan Baris-Kolom pada Array Dua Dimensi #

Ketika array dua dimensi diindeks dengan dua array indeks, NumPy memasangkan elemen kedua array satu per satu (pairwise), lalu mengambil elemen pada tiap pasangan koordinat tersebut. Perilaku ini sering disalahpahami oleh pemula.

python
import numpy as np

penjualan = np.array([
    [120, 85, 200, 65],
    [90, 150, 110, 40],
    [175, 60, 95, 130],
])
print(penjualan[[0, 2], [1, 3]])
print(penjualan[[0, 2], [1, 3]].shape)

Output yang Dihasilkan:

text
[ 85 130]
(2,)

Penjelasannya:

  1. Baris 7 (penjualan[[0, 2], [1, 3]]): array indeks baris [0, 2] dipasangkan dengan array indeks kolom [1, 3] menjadi dua pasangan koordinat: (0, 1) dan (2, 3). Hasilnya elemen penjualan[0, 1] yaitu 85, dan penjualan[2, 3] yaitu 130.
  2. Baris 8: bentuk hasil (2,) mengikuti panjang pasangan, bukan (2, 2).

Mengambil Submatriks dengan Pola Grid #

Untuk mendapatkan submatriks berukuran dua-kali-dua, lakukan seleksi dalam dua langkah: pilih dulu baris-barisnya dengan fancy indexing, lalu pilih kolomnya dari hasil tersebut dengan slice.

python
import numpy as np

penjualan = np.array([
    [120, 85, 200, 65],
    [90, 150, 110, 40],
    [175, 60, 95, 130],
])
sub = penjualan[[0, 2]][:, [1, 3]]
print(sub)
print(sub.shape)

Output yang Dihasilkan:

text
[[ 85  65]
 [ 60 130]]
(2, 2)

Rincian langkahnya:

  1. Bagian penjualan[[0, 2]] (dievaluasi lebih dulu): mengambil baris ke-0 dan ke-2 utuh, menghasilkan array sementara berbentuk (2, 4).
  2. Bagian [:, [1, 3]]: pada array sementara itu, slice : mengambil semua baris dan fancy indexing [1, 3] mengambil kolom ke-1 dan ke-3. Hasilnya submatriks [[ 85 65] [ 60 130]] berbentuk (2, 2).
  3. Baris 8-9: hasil akhirnya benar-benar matriks dua dimensi — berbeda dengan pola pasangan pada bagian sebelumnya yang menghasilkan (2,).

Hasil Fancy Indexing Selalu Copy #

Berbeda dengan basic slicing yang menghasilkan view, fancy indexing selalu menyalin data ke buffer baru. Kita dapat membuktikannya dengan pola ubah-dan-amati beserta atribut base.

python
import numpy as np

penjualan = np.array([
    [120, 85, 200, 65],
    [90, 150, 110, 40],
    [175, 60, 95, 130],
])
salin = penjualan[[0, 2]]
salin[0, 0] = 999
print(salin)
print(penjualan[0, 0])
print(penjualan[[0, 2]].base)

Output yang Dihasilkan:

text
[[999  85 200  65]
 [175  60  95 130]]
120
None

Yang perlu kita perhatikan:

  1. Baris 8 (salin = penjualan[[0, 2]]): fancy indexing menyalin baris ke-0 dan ke-2 ke array baru bernama salin.
  2. Baris 9 (salin[0, 0] = 999): kita mengubah elemen pertama milik salin.
  3. Baris 10-11 (print): perubahan hanya terjadi pada salin; penjualan[0, 0] tetap 120. Data asli aman karena tidak berbagi memori.
  4. Baris 12 (...base): atribut base milik hasil fancy indexing bernilai None, artinya array tersebut memiliki buffer sendiri dan bukan view dari array lain. Pembandingnya, hasil slice memiliki base yang menunjuk ke array aslinya.

Karena selalu menghasilkan copy, fancy indexing aman dipakai ketika hasil seleksi akan dimodifikasi. Perbandingan lengkap antara operasi yang berbagi memori dan yang tidak dibahas pada Chapter View vs Copy.