6.7. Views en kopieën¶
Een view is een tweede venster op hetzelfde datablok als de bron. Er wordt geen data gekopieerd; de view bevat een nieuwe descriptor (zijn eigen vorm, strides en dtype) maar deelt de buffer. Views zijn in wezen gratis.
Een kopie vraagt de cam om een nieuwe buffer en doorloopt de bron om die te vullen. Kopieën kosten zowel tijd als RAM.
De meeste vormveranderende methoden produceren views. De meeste datatransformerende methoden produceren kopieën. Weten welke welke is bepaalt of een hot loop de cam zonder RAM laat komen.
6.7.1. Reshape¶
reshape() retourneert een array van de gevraagde vorm. Het totale aantal elementen moet ongewijzigd blijven, anders wordt er een ValueError veroorzaakt:
a = np.arange(12, dtype=np.uint8)
m = a.reshape((3, 4))
Het resultaat is een view – m en a delen data. Schrijven via m[0, 0] = 99 verandert ook a[0].
Een nieuwe tuple toewijzen aan shape is een afkorting voor dezelfde bewerking:
a = np.arange(9)
a.shape = (3, 3)
6.7.2. Transpose¶
transpose() (of de snelkoppeling .T) keert de assen om. Geïmplementeerd door de strides om te keren – er wordt geen data verplaatst:
m = np.arange(6, dtype=np.uint8).reshape((2, 3))
t = m.T # shape (3, 2), shares m's buffer
Een getransponeerde view doorloopt het datablok niet aaneengesloten. Het rij-voor-rij lezen van t hierboven bezoekt geheugenposities 0, 3, 1, 4, 2, 5, niet de onderliggende volgorde 0, 1, 2, 3, 4, 5 waarin de bytes zijn opgeslagen. Gewoon rekenwerk en reducties gaan daar prima mee om – ze stappen door de strides – maar tobytes() kan dat niet, omdat deze de onderliggende buffer rechtstreeks teruggeeft zonder te kopiëren. De bytes die de buffer bevat komen niet overeen met de volgorde die de vorm van de view impliceert, dus de methode veroorzaakt een ValueError op elke niet-aaneengesloten view. Wanneer de bytes in de getransponeerde volgorde nodig zijn, forceer dan eerst een nieuwe aaneengesloten kopie:
bytes_out = t.copy().tobytes()
6.7.3. Flatten en flat¶
flatten() retourneert een 1-D-kopie van de array:
f = m.flatten() # new dense 1-D ndarray
Geef order='C' (standaard) door om eerst de laatste as te doorlopen of order='F' om eerst de eerste as te doorlopen:
m = np.arange(6, dtype=np.uint8).reshape((2, 3))
# m = [[0, 1, 2],
# [3, 4, 5]]
m.flatten() # array([0, 1, 2, 3, 4, 5], dtype=uint8)
m.flatten(order='F') # array([0, 3, 1, 4, 2, 5], dtype=uint8)
flat is de iterator-vorm. Deze levert elk element van een ndarray van willekeurige rang op als scalairen, zonder een platte kopie te alloceren:
for x in m.flat:
print(x)
Wanneer de applicatie elk element moet doorlopen, geef dan de voorkeur aan flat; wanneer deze een dichte 1-D-buffer nodig heeft om aan een andere functie door te geven, gebruik dan flatten().
6.7.4. Iteratie¶
Het itereren van een 1-D-array levert scalairen op; het itereren van een array van hogere rang levert (n-1)-D-views op:
m = np.array([[0, 1, 2], [3, 4, 5]], dtype=np.uint8)
for row in m:
print(row) # array([0, 1, 2]), array([3, 4, 5])
De rijen die door het itereren van een matrix worden opgeleverd zijn views, dus het wijzigen ervan wijzigt de bron.
6.7.5. Kopieën¶
copy() is de expliciete manier om een onafhankelijke ndarray te krijgen waarvan de wijzigingen het origineel niet beïnvloeden. Er wordt een nieuwe buffer gealloceerd en de bron wordt erin doorlopen:
c = a.copy()
tobytes() retourneert een bytearray die geheugen deelt met het datablok van de array. Schrijven via de bytearray wijzigt de array in-place. Veroorzaakt een ValueError als de array niet dicht is (een geslicete view, een transpose, …).
tolist() retourneert de inhoud als een mogelijk geneste Python-list. Nuttig voor het serialiseren van kleine resultaten; duur voor grote, omdat elk element een apart Python-object wordt.
6.7.6. Welke bewerkingen welke retourneren¶
De volledige regel:
De volgende bewerkingen retourneren views:
slicing –
a[1:5],a[::2],m[:, 0];indexering op één as van een array van hogere rang –
m[0];het itereren van een n-D-array;
reshape(), wanneer de gevraagde indeling compatibel is;transpose()/.T;asarray(), wanneer de dtype overeenkomt.
De volgende bewerkingen retourneren kopieën:
booleaanse indexering –
a[mask];rekenwerk –
a + b,a * 2,np.sin(a);array()– kopieert altijd, zelfs vanuit een andere array;
Grijp alleen naar een expliciete kopie wanneer er werkelijk een onafhankelijke buffer nodig is. Op een camera met beperkt RAM is het verschil tussen een view en een kopie vaak het verschil tussen code die past en code die dat niet doet.