Array disebut contiguous apabila elemennya berdempetan di memori tanpa celah, tepat seperti urutan yang dijanjikan layoutnya. Masalahnya, operasi sehari-hari seperti slicing kolom dan transpose sering menghasilkan array yang hanya “berpura-pura” multidimensi: data aslinya tetap di tempat, tetapi elemen yang bersebelahan secara logika kini melompat-lompat di memori. Kondisi ini disebut non-contiguous.

Bab ini membahas cara membaca status kontinuitas dari flags, operasi apa yang memutusnya, bagaimana memulihkannya, serta konsekuensi praktisnya — dari perilaku ravel yang berubah hingga salinan yang terjadi tanpa kita sadari.

Mengenali Array Non-contiguous #

Status kontinuitas dibaca dari bendera C_CONTIGUOUS. Kita bandingkan array asli dengan hasil slicing yang mengambil dua kolom sekaligus:

python
import numpy as np

a = np.arange(12).reshape(3, 4)
s = a[:, ::2]
print("a C_CONTIGUOUS     :", a.flags["C_CONTIGUOUS"])
print("s C_CONTIGUOUS     :", s.flags["C_CONTIGUOUS"])
print("shares_memory(s,a) :", np.shares_memory(s, a))
print(s)

Output yang Dihasilkan:

text
a C_CONTIGUOUS     : True
s C_CONTIGUOUS     : False
shares_memory(s,a) : True
[[ 0  2]
 [ 4  6]
 [ 8 10]]

Bedah baris demi baris:

  1. Baris 3 (a = ...): array 3x4 dibuat utuh, sehingga dua belas elemennya berdempetan dan C_CONTIGUOUS bernilai True.
  2. Baris 4 (s = a[:, ::2]): slicing dengan langkah dua mengambil kolom 0 dan 2 saja. Karena baris aslinya tetap berisi kolom 1 dan 3 di antaranya, elemen s yang bersebelahan terpisah oleh data yang tidak diambil — kontinuitas putus.
  3. Baris 7 (np.shares_memory): s hanyalah view; ia dan a menunjuk blok memori yang sama.

Memulihkan Kontinuitas dengan ascontiguousarray #

Cara memulihkan kontinuitas adalah menyalin data ke blok baru yang rapat. Fungsi np.ascontiguousarray melakukannya, dengan satu sifat praktis: bila array sudah contiguous, fungsi ini mengembalikan array asli tanpa menyalin.

python
import numpy as np

a = np.arange(12).reshape(3, 4)
s = a[:, ::2]
c = np.ascontiguousarray(s)
print("s C_CONTIGUOUS :", s.flags["C_CONTIGUOUS"])
print("c C_CONTIGUOUS :", c.flags["C_CONTIGUOUS"])
print("shares_memory  :", np.shares_memory(c, a))

Output yang Dihasilkan:

text
s C_CONTIGUOUS : False
c C_CONTIGUOUS : True
shares_memory  : False

Cara membacanya:

  1. Baris 5 (np.ascontiguousarray(s)): NumPy mengalokasikan blok baru dan menyalin elemen s secara rapat; c kini sepenuhnya berdiri sendiri.
  2. Baris 8: c tidak lagi berbagi memori dengan a, sehingga perubahan pada satu pihak tidak menular ke pihak lain.

Konsekuensi pada ravel #

Perilaku paling nyata dari array non-contiguous muncul saat kita meratakannya. ravel hanya bisa mengembalikan view bila meratakan berarti menyalin byte secara berurutan; jika tidak, ia diam-diam membuat salinan:

python
import numpy as np

a = np.arange(12).reshape(3, 4)
baris = a[1]
kolom = a[:, 1]
print("baris C_CONTIGUOUS :", baris.flags["C_CONTIGUOUS"])
print("kolom C_CONTIGUOUS :", kolom.flags["C_CONTIGUOUS"])
print("ravel baris = view :", np.shares_memory(baris.ravel(), a))
print("ravel kolom = copy :", np.shares_memory(kolom.ravel(), a))

Output yang Dihasilkan:

text
baris C_CONTIGUOUS : True
kolom C_CONTIGUOUS : False
ravel baris = view : True
ravel kolom = copy : False
  1. Baris 4 (baris = a[1]): satu baris utuh adalah potongan memori yang berdempetan, maka statusnya contiguous.
  2. Baris 5 (kolom = a[:, 1]): satu kolom melompat sepanjang satu baris untuk setiap langkah, maka statusnya non-contiguous.
  3. Baris 6-7: ravel pada baris mengembalikan view, sedangkan ravel pada kolom harus menyalin karena urutan elemen kolom tersebar di memori.

Perilaku ravel dan flatten dibandingkan lebih rinci pada Chapter flatten dan ravel.

transpose: Non-contiguous yang Paling Sering Terjadi #

Hasil .T selalu view tanpa salinan, tetapi hampir selalu non-contiguous untuk matriks C-order. Menariknya, ia justru berstatus F-contiguous karena kolom-kolomnya kini berdempetan:

python
import numpy as np

a = np.arange(12).reshape(3, 4)
t = a.T
print("t C_CONTIGUOUS    :", t.flags["C_CONTIGUOUS"])
print("t F_CONTIGUOUS    :", t.flags["F_CONTIGUOUS"])
print("copy C_CONTIGUOUS :", t.copy().flags["C_CONTIGUOUS"])

Output yang Dihasilkan:

text
t C_CONTIGUOUS    : False
t F_CONTIGUOUS    : True
copy C_CONTIGUOUS : True
  1. Baris 4-5: transpose hanya menukar shape dan strides (lihat Chapter Strides dan Memory Access), sehingga status C hilang dan status F muncul.
  2. Baris 6: copy() menghasilkan blok baru yang kembali rapat dalam urutan C — cara paling umum “merapikan” array hasil transpose.
OperasiView atau copyStatus kontinuitas
a[1] (satu baris)viewC-contiguous
a[:, 1] (satu kolom)viewnon-contiguous
a[:, ::2] (langkah dua)viewnon-contiguous
a.TviewF-contiguous
np.ascontiguousarray(s)copy bila perluC-contiguous
a.copy()selalu copyC-contiguous