6.3. Creare array¶
Ogni esempio nel resto di queste pagine parte da un ndarray già pronto. Questa pagina è il catalogo di come quell’array prende vita. Esistono quattro famiglie di costruttori:
Da un iterabile Python – la consueta forma con letterale / lista / tupla.
Pre-riempito a una forma data – zeri, uni, un valore costante, una matrice identità.
Generato come sequenza – valori in intervallo o equispaziati.
Avvolgendo un buffer già in RAM – il caso delle periferiche.
Ogni costruttore accetta una parola chiave dtype= e ha come valore predefinito float. I dati dei sensori vogliono quasi sempre un dtype più piccolo di quello predefinito.
Ogni esempio qui sotto parte da:
from ulab import numpy as np
6.3.1. Da un iterabile Python¶
array() costruisce un ndarray da qualsiasi iterabile di numeri:
a = np.array([1, 2, 3, 4])
print(a)
Output:
array([1.0, 2.0, 3.0, 4.0], dtype=float)
Gli iterabili annidati producono array multidimensionali. Gli iterabili interni devono avere tutti la stessa lunghezza, altrimenti viene sollevata ValueError
m = np.array([[1, 2, 3],
[4, 5, 6]], dtype=np.uint8)
Anche un ndarray preesistente è un input valido; array() copia sempre. Per evitare la copia quando non è necessaria, usa asarray()
b = np.asarray(a, dtype=np.float) # same dtype -> no copy
6.3.2. Pre-riempito a una forma data¶
Quando la forma di destinazione è nota ma il contenuto non lo è ancora, alloca il buffer in anticipo e scrivici dentro in seguito:
zeros()– riempito con zeri.ones()– riempito con uni.full()– riempito con un valore dato.empty()– alias dizeros()(ulabnon lascia il buffer non inizializzato).eye()– matriceN-per-Msimile all’identità con uni sullak-esima diagonale.diag()– una matrice diagonale da un vettore, oppure la diagonale di una matrice.
np.zeros((3, 3)) # 3x3 of zeros
np.ones(5, dtype=np.uint8) # length-5 vector of ones
np.full((2, 3), 7, dtype=np.int8) # 2x3, all 7
np.eye(4) # 4x4 identity
np.diag([1, 2, 3]) # 3x3, [1, 2, 3] on the diagonal
L’argomento shape è un singolo intero (per un array monodimensionale) oppure una tupla.
6.3.3. Generato come sequenza¶
arange()– valori equispaziati come il built-inrange(), ma restituendo sempre unndarraynp.arange(0, 10, 2) # array([0, 2, 4, 6, 8])linspace()–numpunti equispaziati tra due limiti, con il limite superiore incluso quandoendpoint=Truenp.linspace(0, 1, num=11) # 0.0, 0.1, ..., 1.0logspace()– punti spaziati geometricamente.startestopsono esponenti, non estremi; il risultato va dabase ** startabase ** stopnp.logspace(0, 3, num=4) # 1.0, 10.0, 100.0, 1000.0meshgrid()– costruisce due matrici di coordinate da due array monodimensionali in modo che una funzione per pixelf(x, y)possa essere valutata su un’intera griglia in una sola chiamata vettorizzata. Dato un vettore x di lunghezzaWe un vettore y di lunghezzaH,meshgridrestituisce due matriciH-per-W:Xè il vettore x ripetuto lungo ogni riga,Yè il vettore y ripetuto lungo ogni colonna, cosìX[i, j]è la coordinata x eY[i, j]è la coordinata y della cella alla rigaie colonnajx = np.arange(4) # [0, 1, 2, 3] y = np.arange(3) # [0, 1, 2] X, Y = np.meshgrid(x, y) # X = [[0, 1, 2, 3], # [0, 1, 2, 3], # [0, 1, 2, 3]] # Y = [[0, 0, 0, 0], # [1, 1, 1, 1], # [2, 2, 2, 2]]
f(X, Y)valuta quindi la funzione in ogni cella della griglia in un’unica espressione. Una mappa della distanza dal centro su un frame(H, W), per esempio, ènp.sqrt((X - cx)**2 + (Y - cy)**2)calcolata sulle matrici restituite dameshgrid().
6.3.4. Unire¶
concatenate() unisce una tupla di array lungo un asse esistente:
a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
b = np.array([[5, 6]], dtype=np.uint8)
np.concatenate((a, b), axis=0)
# array([[1, 2], [3, 4], [5, 6]], dtype=uint8)
Tutti gli input devono condividere lo stesso dtype e ndim e coincidere su ogni asse diverso da quello di unione. concatenate() alloca un nuovo array abbastanza grande da contenere ogni input e copia i dati al suo interno, quindi è lo strumento giusto per l’unione una tantum di array già esistenti; è lo strumento sbagliato all’interno di un ciclo di streaming, dove il pattern corretto è pre-allocare la destinazione una sola volta e scriverci dentro tramite assegnazione su slice.
6.3.5. Avvolgere un buffer esistente¶
Il costruttore più utile su una camera è frombuffer(). Reinterpreta un buffer di tipo bytes esistente come un ndarray monodimensionale senza copiare un solo byte:
buf = bytearray(8)
audio = np.frombuffer(buf, dtype=np.int16)
# 4 int16 samples, sharing memory with buf
Le scritture tramite audio sono visibili in buf e viceversa. Il dtype scelto deve dividere esattamente la lunghezza del buffer.
offset= salta un’intestazione all’inizio del buffer; count= limita quanti elementi vengono letti:
np.frombuffer(buf, dtype=np.uint8, offset=2, count=4)
Questo è il costruttore giusto ogni volta che una periferica consegna all’applicazione un buffer grezzo – campioni ADC in un bytearray, un payload estratto da SPI. I byte che la periferica ha scritto sono l’array.
Quando una periferica scrive valori multi-byte in un ordine dei byte che la CPU della camera non legge nativamente, byteswap() inverte l’ordine dei byte di ogni elemento in modo che i valori vengano letti correttamente. Per impostazione predefinita restituisce un nuovo array; passando inplace=True modifica la sorgente sul posto.
frombuffer() gestisce solo i dtype che numpy stesso definisce. Per le periferiche che producono campioni interi a 32 bit, from_int32_buffer() e simili convertono in float in una sola passata.
6.3.6. Troncamento della stampa¶
Stampare un array di grandi dimensioni mostra solo i suoi primi e ultimi elementi, con ... nel mezzo, in modo che il terminale dell’IDE non si riempia di migliaia di valori:
>>> print(np.arange(1000, dtype=np.uint16))
array([0, 1, 2, ..., 997, 998, 999], dtype=uint16)
set_printoptions() sovrascrive le soglie quando il debug richiede l’intero buffer:
np.set_printoptions(threshold=2000) # print up to 2000 elements in full
np.set_printoptions(edgeitems=10) # 10 items at each end, not 3
get_printoptions() restituisce le impostazioni correnti come dizionario.