Nelle lezioni precedenti, abbiamo esplorato le potenzialità di Numpy familiarizzando con i tipi di dati offerti e gestiti da questo framework e prendendo un po’ di confidenza con la principale struttura dati, gli ndarray
.
In questa lezione, vedremo come effettuare delle semplici ma estremamente utili operazioni di creazione e manipolazione degli ndarray
, che torneranno utili non solo a livello di calcolo matematico ma anche quando vorrete utilizzare i tensori con framework di machine learning, come scikit learn, e di deep learning, come TensorFlow o PyTorch.
Creazione di un ndarray
Numpy offre diversi metodi per la creazione di nuovi ndarray
np.zeros()
zeros_matrix = np.zeros((3,4))
zeros_matrix
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
Come possiamo vedere dall’output, la matrice risultante sarà composta da elementi di tipo float64
dtype
In modo analogo, è possibile creare una matrice composta da soli uno tramite il metodo np.ones()
ones_matrix = np.ones((3,2))
ones_matrix
array([[1., 1.],
[1., 1.],
[1., 1.]])
Come nel caso precedente, anche in questo avremo una matrice i cui elementi sono di tipo float64
Se invece è necessario inizializzare una matrice ad uno specifico valore è possibile impiegare il metodo np.full()
full_matrix = np.full((2,3), 5)
full_matrix
array([[5, 5, 5],
[5, 5, 5]])
In questo caso specifico, è stato sufficiente specificare la shape
int64
dtype
Spesso, nei calcoli matriciali, è però necessario lavorare anche con le matrici di identità, che ricordiamo sono matrici NxN
np.eye()
3x3
identity_matrix = np.eye(3)
identity_matrix
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
La matrice risultante avrà valori di tipo float64
Qualora invece fosse necessario definire una matrice diagonale, ossia una matrice quadrata in cui solamente i valori della diagonale principale possono essere diversi da 0
np.diag()
diagonal_matrix = np.diag([1,2,3,4])
diagonal_matrix
array([[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]])
Altri due approcci estremamente utili per creare vettori con elementi che ricadono in uno specifico intervallo sono np.arange()
np.linspace()
La funzione np.arange()
N
ndarray
[0, N-1
vect1 = np.arange(10)
vect1
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Diversamente, specificando due input, con il primo minore del secondo, verrà creato un ndarray
vect2 = np.arange(4, 9)
vect2
array([4, 5, 6, 7, 8])
Come si può facilmente intuire l’estremo superiore, 9
Infine, questo metodo permette anche di definire un passo che identifica la distanza tra due valori adiacenti.
vect = np.arange(1,14,3)
vect
array([ 1, 4, 7, 10, 13])
Nonostante l’utilità di np.arange()
np.linspace()
vect3 = np.linspace(0,25,10)
vect3
array([ 0. , 2.77777778, 5.55555556, 8.33333333, 11.11111111,
13.88888889, 16.66666667, 19.44444444, 22.22222222, 25. ]
In questo caso abbiamo definito un vettore di tipo float64
[0, 25]
Infine, Numpy offre la possibilità di creare vettori e matrici in modo randomico sfruttando il modulo random
3x3
float64
random()
rand_matrix = np.random.random((3,3))
rand_matrix
array([[0.38092162, 0.22859676, 0.96004006],
[0.6855616 , 0.70534742, 0.05605433],
[0.72397115, 0.40938473, 0.94926686]])
Invece, se volessimo una matrice quadrata 3x3
randint()
rand_matrix = np.random.randint(4, 13, (3,3))
rand_matrix
array([[ 6, 9, 8],
[11, 11, 6],
[12, 4, 6]])
Se ancora volessimo creare un ndarray
normal()
X = np.random.normal(0, 0.1, size=(1000,1000))
In questo caso, viene creata una matrice 1000x1000
Quanto riportato finora è solo una rappresentazione dei principali metodi offerti da Numpy per la creazione di ndarray
Modifica della shape
ndarray
shape
N
np.reshape()
vector = np.arange(20)
vect2matrix = np.reshape(vector, (4,5))
vect2matrix
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
Al contrario, se volessimo trasformare una matrice in un vettore, basterebbe usare il metodo flatten()
ndarray
vect2matrix.flatten()
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
In questo caso, abbiamo convertito la matrice in un vettore concatenando le righe tra loro. Se volessimo fare la conversione concatenando le colonne, basterebbe specificare l’ordine tramite la keyword order
F
vect2matrix.flatten(order='F')
array([ 0, 5, 10, 15, 1, 6, 11, 16, 2, 7, 12, 17, 3, 8, 13, 18, 4, 9, 14, 19])
Aggiunta e rimozione di elementi
Quando si lavora con gli ndarray
, è spesso necessario aggiungere o rimuovere elementi. Vediamo come, definendo un vettore e una matrice di esempio.
vect = np.array([1, 2, 3, 4, 5])
matrix = np.array([[1,2,3],[4,5,6],[7,8,9]])
Iniziamo con la rimozione di un elemento da un vettore tramite il metodo np.delete()
, specificando il vettore e la lista di indici in cui si trovano gli elementi che vogliamo rimuovere dal vettore.
np.delete(vect, [0,2])
array([2, 4, 5])
In questo caso, abbiamo rimosso dal vettore gli elementi in posizione 0
e 2
.
Con le matrici il discorso si complica leggermente, in quanto va specificato se deve essere cancellata una riga o una colonna tramite l’utilizzo della keyword axis
.
Per cancellare, ad esempio, la prima riga della nostra matrice basterà:
np.delete(matrix, [0,0], axis=0)
array([[4, 5, 6],
[7, 8, 9]])
Mentre, per cancellare la prima colonna, sarà sufficiente impostare il valore di axis=1
.
Per quanto riguarda l’aggiunta di dati a un ndarray
, ci sono due possibilità:
- l’operazione di appending, che aggiunge in coda a un
ndarray
- l’operazione di inserting, che aggiunge in un punto qualsiasi di un
ndarray
Partiamo dall’operazione di appending, che può essere eseguita tramite la funzione np.append()
. Ad esempio, per aggiungere un valore in coda a un vettore, basta passare in input il vettore e il nuovo dato.
np.append(vect, 6)
array([1, 2, 3, 4, 5, 6])
Per aggiungere nuovi dati a una matrice, è necessario specificare non solo la matrice e i dati, ma anche se aggiungerli sulle righe o sulle colonne. Ad esempio, per aggiungere una nuova riga:
np.append(matrix, [[10, 11, 12]], axis=0)
array([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
Spesso però, si ha l’esigenza di inserire dei nuovi dati in un ndarray
in una determinata posizione. Per farlo, il modulo numpy
offre il metodo np.insert()
.
Ad esempio, se volessimo aggiungere al nostro vettore i valori 10
e 11
in posizione 2
, basterebbe passare i seguenti input al metodo insert()
.
np.insert(vect, 2, [10, 11])
array([ 1, 2, 10, 11, 3, 4, 5])
Se il nostro ndarray
axis
[10,11,12]
np.insert(matrix,1,[10,11,12], axis=0)
array([[ 1, 2, 3],
[10, 11, 12],
[ 4, 5, 6],
[ 7, 8, 9]])
Il codice di questa lezione, con alcuni esempi in più, è disponibile su GitHub