Riferimenti: SoftPython - numpy 1
Esc
Summer School Data Science 2023 - Modulo 1 informatica: Moodle
Docente: David Leoni david.leoni@unitn.it
Esercitatore: Luca Bosotti luca.bosotti@studenti.unitn.it
Ci sono sostanzialmente due modi in Python di rappresentare matrici:
Liste di liste (già viste):
Numpy: SoftPython - numpy
import numpy as np # rinominiamo in `np`
numpy di solito crea in un colpo solo tutta la matrice
regione contigua di memoria
mat = np.zeros( (2,3) ) # UN SOLO PARAMETRO tupla: 2 righe, 3 colonne
mat
array([[0., 0., 0.], [0., 0., 0.]])
ATTENZIONE: anche se vedi delle quadre, gli oggetti di numpy non sono liste di liste!
type(mat)
numpy.ndarray
Se PROPRIO avete necessità, potete creare una matrice numpy da una lista di liste:
mat = np.array( [ [5.0,8.0,1.0],
[4.0,3.0,2.0]] )
DOMANDA: quand'è che potrebbe aver senso farlo?
DOMANDA: e se ho una matrice numpy, ha senso convertirla a lista di liste?
k
¶np.full( (3,5), 7)
array([[7, 7, 7, 7, 7], [7, 7, 7, 7, 7], [7, 7, 7, 7, 7]])
mat = np.array( [ [5.0,8.0,1.0],
[4.0,3.0,2.0] ] )
mat.shape
(2, 3)
DOMANDA 1: Che cos'è?
DOMANDA 2: .shape
è un attributo o un metodo?
ATTENZIONE: la notazione mat[i,j]
funziona solo in numpy!
mat = np.array( [ [5.0,8.0,1.0],
[4.0,3.0,2.0]])
mat[0,1]
8.0
mat[0,1] = 9
mat
array([[5., 9., 1.], [4., 3., 2.]])
Per efficienza Numpy usa regioni di memoria contigue e omogenee:
mat[0,0] = "c"
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
/tmp/ipykernel_45876/3318513095.py in <module>
----> 1 mat[0,0] = "c"
ValueError: could not convert string to float: 'c'
mat = np.array( [ [5, 8, 1],
[4, 3, 2],
[6, 7, 9],
[9, 3, 4],
[8, 2, 7]])
Esempio:
righe dalla 0
inclusa alla 4
esclusa
colonne dalla 1
inclusa alla 3
esclusa
mat[0:4, 1:3]
array([[8, 1], [3, 2], [7, 9], [3, 4]])
mat = np.array( [ [5, 8, 1],
[4, 3, 2],
[6, 7, 9],
[9, 3, 4]])
mat[0:4, 0:1] # tutta la prima colonna
array([[5], [4], [6], [9]])
mat[0:4, 0] # ATTENZIONE: senza slice ritorna un vettore, non una matrice
array([5, 4, 6, 9])
mat[0:1,:] # tutta la prima riga
array([[5, 8, 1]])
DOMANDA: questo cosa produce? Qual'è la differenza con sopra? mat[0,:]
mat = np.array( [ [5, 8, 1],
[4, 3, 2],
[6, 7, 9],
[9, 3, 4],
[8, 2, 7]])
mat[0:5:2, :] # prende righe alternate
array([[5, 8, 1], [6, 7, 9], [8, 2, 7]])
mat = np.array( [ [5, 8, 1],
[4, 3, 2],
[6, 7, 9] ])
sotto_mat = mat[0:2, 1:3]
sotto_mat
array([[8, 1], [3, 2]])
sotto_mat[0,0] = 999
sotto_mat
array([[999, 1], [ 3, 2]])
mat
array([[ 5, 999, 1], [ 4, 3, 2], [ 6, 7, 9]])
ATTENZIONE: modifica slice di numpy -> modifica anche la matrice originale!
mat = np.array( [ [5, 8, 1],
[4, 3, 2],
[6, 7, 9],
[9, 3, 4],
[8, 2, 5]])
mat[0:4, 1:3] = 7
mat
array([[5, 7, 7], [4, 7, 7], [6, 7, 7], [9, 7, 7], [8, 2, 5]])
ATTENZIONE: le dimensioni della slice e della matrice a destra devono coincidere!
mat = np.array( [ [5, 8, 1],
[4, 3, 2],
[6, 7, 9],
[9, 3, 4],
[8, 2, 5]])
mat[0:4, 1:3] = np.array([
[10,50],
[11,51],
[12,52],
[13,53],
])
mat
array([[ 5, 10, 50], [ 4, 11, 51], [ 6, 12, 52], [ 9, 13, 53], [ 8, 2, 5]])
Gli array di numpy sono mutabili:
va = np.array([1,2,3])
vb = va
vb[0] = 100
vb
array([100, 2, 3])
va
array([100, 2, 3])
ma = np.array([[1,2,3],
[4,5,6]])
mc = ma.copy()
mc
array([[1, 2, 3], [4, 5, 6]])
mc[0][0] = 100
mc
array([[100, 2, 3], [ 4, 5, 6]])
ma
array([[1, 2, 3], [4, 5, 6]])
va = np.array([10,20,30])
vb = np.array([1,2,3])
operazioni algebriche creano un NUOVO array:
vc = va + vb
vc
array([11, 22, 33])
va
array([10, 20, 30])
m = np.array([[5, 9, 7],
[6, 8, 0]])
m * 3
array([[15, 27, 21], [18, 24, 0]])
m + 3
array([[ 8, 12, 10], [ 9, 11, 3]])
*
¶ATTENZIONE: *
in numpy moltiplica elemento per elemento!
Richiede quindi matrici di dimensioni identiche:
ma = np.array([[1, 2, 3],
[10, 20, 30]])
mb = np.array([[1, 0, 1],
[4, 5, 6]])
ma * mb
array([[ 1, 0, 3], [ 40, 100, 180]])
@
¶mc = np.array([[1, 2, 3],
[10, 20, 30]])
md = np.array([[1, 4],
[0, 5],
[1, 6]])
mc @ md
array([[ 4, 32], [ 40, 320]])
ma = np.array([[1, 2, 0.0],
[10, 0.0, 30]])
ma / 4
array([[0.25, 0.5 , 0. ], [2.5 , 0. , 7.5 ]])
Attenzione a divisione per 0.0
print(ma / 0.0)
print("DOPO")
[[inf inf nan] [inf nan inf]] DOPO
nan
e inf
possono causare problemiEsempi funzioni che iniziano con np.
:
m = np.array([[5, 4, 6],
[3, 7, 1]])
np.sum(m)
26
np.max(m)
7
np.min(m)
1
parametro axis
: aggregazione colonna o riga:
m = np.array([[5, 4, 6],
[3, 7, 1]])
np.sum(m, axis=0) # somma ogni colonna
array([ 8, 11, 7])
np.sum(m, axis=1) # somma ogni riga
array([15, 11])
mat = np.array([[5, 2, 6],
[1, 4, 3]])
mat[ mat > 2 ]
array([5, 6, 4, 3])
mat > 2 # matrice di booleani
array([[ True, False, True], [False, True, True]])
messa dentro mat[ ]
funge da filtro:
mat[ mat > 2 ]
array([5, 6, 4, 3])
mat = np.array([[5, 2, 6],
[1, 4, 3]])
mat[ (mat > 3) & (mat < 6) ]
array([5, 4])
mat[ (mat < 2) | (mat > 4) ]
array([5, 6, 1])
ATTENZIONE: RICORDATI LE PARENTESI TONDE TRA LE VARIE ESPRESSIONI!
prova a ometterle, che succede?
ATTENZIONE: and
E or
NON FUNZIONANO!
prova a usarli, che succede?
np.arange
¶La funzione standard range
Python non permette incrementi con la virgola
range(3.2)
, che succede?Usiamo invece np.arange
, specificando:
np.arange(0.0, 1.0, 0.2)
array([0. , 0.2, 0.4, 0.6, 0.8])
np.linspace
¶Alternativamente, possiamo usare np.linspace
, specificando:
np.linspace(0, 0.8, 5)
array([0. , 0.2, 0.4, 0.6, 0.8])
np.linspace(0, 0.8, 10)
array([0. , 0.08888889, 0.17777778, 0.26666667, 0.35555556, 0.44444444, 0.53333333, 0.62222222, 0.71111111, 0.8 ])
A volte durante i calcoli accadono condizioni estreme, esempio:
10e99999999999999999999999
inf
10e99999999999999999999999 / 10e99999999999999999999999
nan
Numpy adotta lo standard IEEE 754 per l’Aritmetica in virgola mobile binaria
NaN: Not a Number
import math
math.nan
nan
type(math.nan)
float
math.nan == math.nan # che fa?
False
NaN *NON* E’ UGUALE A SE’ STESSO !!!
Per vedere se numero
è un NaN
, non puoi scrivere così:
import math
numero = math.nan
if numero == math.nan: # SBAGLIATO
print("Sono un NaN ")
else:
print("numero è qualcos'altro ??")
numero è qualcos'altro ??
Invece, usa la funzione math.isnan
:
import math
numero = math.nan
if math.isnan(numero): # CORRETTO
print("Sono un NaN ")
else:
print("numero è qualcos'altro ??")
Sono un NaN
np.nan
nan
np.inf
inf
DOMANDA: Che succede se chiamiamo np.sum(malefico)
?
malefico = np.array([1.0,2.0,np.nan, 3.0, np.nan])
np.sum(malefico)
nan
np.nansum(malefico) # Numpy spesso offre modi per ignorare i nan
6.0
diversi esercizi di solito con due soluzioni proposte:
for
Prima provate a risolvere senza cicli, se non riuscite usate dei for
o while
(ai fini dell'esame non valuto le performance, mi basta che passi i test)
Un modo più interessante per imparare le matrici