Menyalin array berarti mengalokasikan blok baru lalu memindahkan seluruh byte-nya — pada data besar, operasi ini jauh lebih mahal daripada operasi aritmetika itu sendiri. Masalahnya, beberapa pemanggilan fungsi menyalin data tanpa memberi tanda, dan kebiasaan defensif seperti “buat saja salinannya” membuat program memboros memori dan waktu.

Kabar baiknya: status view atau copy dapat dibuktikan, bukan ditebak. Alat utamanya adalah np.shares_memory, yang memeriksa apakah dua array menunjuk blok memori yang sama. Bab ini membedah fungsi-fungsi yang sering dipakai dan status salinannya.

asarray vs array #

np.array secara bawaan selalu menyalin, sedangkan np.asarray menyalin hanya bila perlu — ketika diberi objek yang bukan array. Untuk input yang sudah berupa ndarray, np.asarray mengembalikan objek yang sama:

python
import numpy as np

data = np.arange(5)
a = np.array(data)
b = np.asarray(data)
print("np.array(ndarray)   :", np.shares_memory(a, data))
print("np.asarray(ndarray) :", np.shares_memory(b, data))

Output yang Dihasilkan:

text
np.array(ndarray)   : False
np.asarray(ndarray) : True

Cara membacanya:

  1. Baris 4 (np.array(data)): argumen copy=True bawaan membuat NumPy mengalokasikan blok baru, sekalipun inputnya sudah array dengan dtype yang sama.
  2. Baris 5 (np.asarray(data)): karena input sudah ndarray dan tidak ada permintaan dtype lain, fungsi mengembalikan objek yang sama persis.
  3. Kedua baris print: False berarti salinan penuh; True berarti view — bukti nyata, bukan asumsi.

Kebiasaan yang aman: pakai np.asarray pada batas fungsi yang menerima “array-like”, sehingga pemanggil yang sudah membawa ndarray tidak kena biaya salinan.

ravel vs flatten #

Kedua fungsi menghasilkan array 1-D, tetapi keduanya berbeda sikap terhadap memori:

python
import numpy as np

a = np.arange(12).reshape(3, 4)
f = a.flatten()
r = a.ravel()
print("flatten shares:", np.shares_memory(f, a))
print("ravel   shares:", np.shares_memory(r, a))

Output yang Dihasilkan:

text
flatten shares: False
ravel   shares: True
  1. Baris 4 (flatten): fungsi ini menjamin salinan baru dalam urutan C, apa pun kondisi array asalnya — aman, tetapi selalu berbayar.
  2. Baris 5 (ravel): fungsi ini mengembalikan view bila memungkinkan dan menyalin hanya bila array aslinya non-contiguous (lihat Chapter Contiguous vs Non-contiguous).

astype dengan copy=False #

astype bawaannya juga selalu menyalin. Argumen copy=False mengubahnya menjadi “salin hanya bila dtype harus berubah”:

python
import numpy as np

a = np.arange(5, dtype=np.int64)
sama = a.astype(np.int64, copy=False)
beda = a.astype(np.float64, copy=False)
print("dtype sama  :", np.shares_memory(sama, a))
print("dtype beda  :", np.shares_memory(beda, a))

Output yang Dihasilkan:

text
dtype sama  : True
dtype beda  : False
  1. Baris 4 (astype(np.int64, copy=False)): dtype tujuan sama dengan dtype asal, sehingga tidak ada pekerjaan konversi — NumPy mengembalikan array asli.
  2. Baris 5 (astype(np.float64, copy=False)): menyimpan int64 sebagai float64 butuh ukuran penyimpanan berbeda, maka salinan tetap dibuat.

Pola copy=False menjadikan fungsi konversi hemat: ia hanya membayar bila memang perlu.

fancy Indexing Selalu Menyalin #

Berbeda dari slicing biasa, fancy indexing — mengindeks dengan list atau array bilangan bulat — selalu menghasilkan salinan, karena urutan dan jumlah elemennya bisa apa saja:

python
import numpy as np

a = np.arange(10)
fancy = a[[1, 3, 5]]
irisan = a[1:6:2]
print("fancy indexing :", np.shares_memory(fancy, a))
print("slice biasa    :", np.shares_memory(irisan, a))
print(np.array_equal(fancy, irisan))

Output yang Dihasilkan:

text
fancy indexing : False
slice biasa    : True
True
  1. Baris 4 vs 5: keduanya memilih elemen bernilai sama (baris terakhir membuktikannya dengan np.array_equal yang mengembalikan True), tetapi fancy adalah salinan sedangkan irisan view.
  2. Implikasi: mengubah fancy tidak memengaruhi a; mengubah irisan mengubah a. Perilaku lengkapnya dibahas pada Chapter Fancy Indexing dan Boolean Indexing.

Hasil filtering dengan mask boolean berperilaku sama seperti fancy indexing — salinan baru:

python
import numpy as np

a = np.arange(10)
hasil = a[a % 2 == 0]
print("shares_memory:", np.shares_memory(hasil, a))
hasil[0] = 999
print("array asli   :", a[0])

Output yang Dihasilkan:

text
shares_memory: False
array asli   : 0
  1. Baris 4 (a[a % 2 == 0]): hasil filtering bernilai False pada shares_memory — data baru terpisah dari a.
  2. Baris 5-6: karena terpisah, mengubah hasil[0] menjadi 999 tidak menyentuh a, dan a[0] tetap 0.
OperasiHasilKapan menyalin
np.array(x)selalu copyselalu (bila copy=True)
np.asarray(x)view bila bisahanya saat input bukan ndarray
a[1:5]viewtidak pernah
a[[1, 3, 5]]selalu copyselalu
a[mask]selalu copyselalu
a.ravel()view bila bisasaat non-contiguous
a.flatten()selalu copyselalu
a.astype(t)copybila dtype berubah; copy=False bila tidak