6.6. İndeksleme ve dilimleme

Bir ndarray dört şekilde adreslenir: tek indeksler, dilimler, mantıksal maskeler ve bunların her birinin atama biçimleri.

6.6.1. Tek öğeler

Köşeli parantezle indeksleme, verilen konumdaki değeri döndürür:

a = np.arange(10, dtype=np.uint8)
print(a[0], a[-1])      # 0 9
print(a[1], a[-2])      # 1 8

Negatif indeksler, tıpkı bir Python list nesnesinde olduğu gibi sondan saymaya başlar. Aralık dışında bir indeks IndexError yükseltir.

Daha yüksek dereceli diziler için her eksen bir indeks alır. İndeksler tek bir köşeli parantez kümesinin içine, virgüllerle ayrılarak yazılır:

m = np.arange(9, dtype=np.uint8).reshape((3, 3))
print(m[1, 1])          # 4
print(m[2, 0])          # 6

Eksen sayısından daha az indeks verildiğinde, indekslenmeyen eksenler olduğu gibi kalır. Sonuç, kaynağın bir azaltılmış dereceli görünümüdür:

print(m[0])             # the first row, as a 1-D view of m

6.6.2. Dilimler

Bir start:stop:step dilimi, dizinin bir görünümünü döndürür. Görünüm, alttaki veri arabelleğini kaynakla paylaşır; görünüm aracılığıyla yazmak kaynağa yazar:

a = np.arange(10, dtype=np.uint8)
v = a[::2]              # array([0, 2, 4, 6, 8], dtype=uint8)
v[0] = 99
print(a)
# array([99, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8)

Bağımsız bir arabellek gerektiğinde, copy() açıkça bir tane üretir.

Dilimleme, daha yüksek boyutlara doğal olarak genişler. Her eksen kendi dilimini alır:

m = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]], dtype=np.uint8)

m[0]            # first row
m[0, :2]        # first two elements of row 0
m[:, 0]         # column 0 (still 2-D in ulab)
m[-1]           # last row
m[::2, ::2]     # every other row, every other column

Bir tamsayıyı (tek indeks, ekseni düşürür) ve bir dilimi (ekseni korur) karıştırmak mümkündür ve tek satır/tek sütun erişimi normalde böyle yazılır.

6.6.3. Mantıksal maskeler

Kaynakla aynı şekle sahip mantıksal bir dizi, maskenin True olduğu öğeleri seçer. Mantıksal indeksleme şu anda 1 boyutlu dizilerde çalışır; daha yüksek dereceli girişler NotImplementedError yükseltir:

a = np.arange(9, dtype=np.float)
mask = a < 5
print(a[mask])

Çıktı:

array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)

Maske sıradan bir bool ndarray nesnesidir, dolayısıyla bunu üreten herhangi bir ifade işe yarar:

b = np.array([4, 4, 4, 3, 3, 3, 13, 13, 13], dtype=np.uint8)
a = np.arange(9, dtype=np.uint8)
print(a[a * a > np.sin(b) * 100.0])

Mantıksal indeksleme bir kopya döndürür. Seçilen öğeler, maskenin True olduğu konumlarda bulunur – kaynak boyunca düzenli bir adımda değil – bu yüzden bir görünümün onları adreslemek için kullanabileceği bir tanımlayıcı yoktur ve sonuç kendi arabelleğine somutlaştırılır.

6.6.4. Tamsayı dizisiyle indeksleme

Köşeli parantez içinde bir indeks listesi veya dizisi geçirmek, bu öğeleri tek adımda seçer:

a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
a[[0, 2, 4]]
# array([10, 30, 50], dtype=uint8)

Sonuç bir kopyadır; seçilen öğeler artık kaynakla depolama alanı paylaşmaz. Aynı biçim bir atamanın solunda da çalışır:

a[[0, 2, 4]] = 0
# array([0, 20, 0, 40, 0], dtype=uint8)

take() (Seçim ve yeniden düzenleme sayfasında ele alınmıştır) aynı işlemin fonksiyon biçimidir ve akışlı bir döngüde ayırmasız kullanım için bir out= anahtar sözcüğünü kabul eder.

6.6.5. Dilim ataması

Dilimler ve maskeler bir atamanın sağında olduğu kadar solunda da görünür. Sağ taraf bir skaler, başka bir dizi veya bir görünüm olabilir:

m = np.zeros((3, 3), dtype=np.uint8)
m[0]      = 1            # whole row 0 set to 1
m[:, 2]   = 3            # whole column 2 set to 3
m[1, 1:3] = [7, 8]       # row 1, columns 1 and 2

Soldaki mantıksal maskeler, koşulu sağlayan öğeleri değiştirir:

a = np.arange(9, dtype=np.uint8)
a[a < 3] = 99
# array([99, 99, 99, 3, 4, 5, 6, 7, 8], dtype=uint8)

a = np.arange(9, dtype=np.uint8)
b = np.array(range(9)) + 12
a[b < 15] = b[b < 15]
# array([12, 13, 14, 3, 4, 5, 6, 7, 8], dtype=uint8)

6.6.6. Bir kamerada dilim ataması neden önemlidir

Dilim ataması, zaten var olan bir dizi aracılığıyla yazar. Yeni bir dizi ayrılmaz. Aradaki fark şudur:

out = a + b              # makes a new array the size of a
out = out * 2            # makes another new array

ve:

out[:] = a               # writes into the existing out
out   += b               # in place
out   *= 2               # in place

İlk sürüm kameradan iki yeni dizi değerinde RAM ister; ikinci sürüm hiçbir şey istemez. Sınırlı RAM’e sahip bir mikrodenetleyicide bu fark, çoğu zaman rahatça çalışan bir betikle bellek yetersizliğine düşen bir betik arasındaki farktır.

Performans bu deseni ayrıntılı olarak ele alır. Şimdilik önemli kural şudur: dilim ataması, yerinde aritmetik operatörler (+=, *=, …) ve evrensel fonksiyonlardaki out= anahtar sözcüğü, ayırmasız güncellemeleri mümkün kılan üç araçtır.