La libreria numpy consente di lavorare con vettori e matrici in maniera più efficiente e veloce di quanto non si possa fare con le liste e le liste di liste (matrici).
Il costrutto di base è l'ndarray, che può avere dimensioni qualunque e tipi corrispondenti a quelli classici del C o del Fortran, che restano i linguaggi tuttora più utilizzati per il calcolo numerico.
Uno dei punti di forza di numpy è di poter lavorare sui vettori sfruttando le ottimizzazioni di calcolo vettoriale del processore della macchina. Ciò rende particolarmente efficiente i calcoli, rispetto alle liste.
Esempio: genera un vettore di 100 numeri casuali. Nota: la libreria numpy ha al suo interno un'altra libreria che contiene le funzioni per generare numeri random secondo diverse funzioni di distribuzione. Una di queste è la funzione "random_sample", che genera una sequenza di numeri tra zero e uno (escluso!) secondo una distribuzione piatta (cioé tutti i numeri sono equiprobabili!). Se chiamata con una tupla, genera una matrice di numeri random.
import numpy as np
vett=np.random.random_sample(100)
print(vett)
Lo stesso risultato si può ottenere, con una sintassi più semplice, come segue:
import numpy as np
from numpy import random as r
v=r.random_sample(10)
print(v)
w=r.random_sample((10,2))
print(w)
Esempi:
import numpy as np
from numpy import random as r
v=r.random_sample(10)
w=np.round(50 * r.random_sample((3,3,4)) + 1) # Genera una matrice 3x3x4 di numeri random tra 1 e 50!
print("v = ", v)
print("w = ", w)
print(v.mean())
print(w.mean())
print(v.sum())
print(w.sum())
print(v.mean() * v.size)
print(w.mean() * w.size)
print(v.std())
print(w.std())
print(v.min(), v.max())
print(w.min(), w.max())
print(v.ndim, v.shape, v.size, v.itemsize, v.dtype)
print(w.ndim, w.shape, w.size, w.itemsize, w.dtype)
ww=w.astype(int)
print(ww.ndim, ww.shape, ww.size, ww.itemsize, ww.dtype)
x=np.zeros((10,2))
y=np.arange(0.0,2.0*np.pi,0.1)
z=np.linspace(0.0,2.0*np.pi,11)
print(x)
print(y)
print(z)
La funzione array(lista) converte una lista python in un vettore numpy. Viceversa, la funzione tolist() converte l'array numpy in una lista!
Esempio:
xx=x.tolist()
type(xx)
yy=np.array(xx)
type(yy)
xx=np.ndarray((11,2),dtype=float) # Crea una matrice di 11x2 elementi di tipo reale in singola precisione
xx[:,0]=np.linspace(0.0,1.0,11)
xx[:,1]=r.random_sample(11)
print("xx = ", xx)
xx.tofile("dati.dat", sep='\t') # Scrive i due vettori come 2 colonne di dati in "dati.dat"
yy=np.fromfile("dati.dat", sep='\t') # Legge le due colonne nel vettore yy
print("yy = ", yy) # Nota come ora yy e' un singolo vettore! Cosi' non va bene!
print("xx = ", xx)
np.savetxt("nuovo_dati.dat", xx) # savetxt salva le due colonne come prima
yy=np.loadtxt("nuovo_dati.dat") # ma loadtxt le legge correttamente in una matrice 11x2!
print("yy = ", yy) # Cosi' va molto meglio!
E' possibile anche inserire commenti nel file scritto, ad esempio per dare un'idea del contenuto del file, che poi verranno saltati in fase di lettura.
Esempio:
np.savetxt("altri_dati.dat", xx, header="Colonne scritte: x e numeri random", footer="Qui il file finisce...")
yy=np.loadtxt("altri_dati.dat")
print("Ora yy vale: ", yy)
La libreria matplotlib consente di disegnare grafici 2D in vari modi. Le quantità da plottare sono ndarrays di numpy, ma anche una lista viene trasformata direttamente in un array quando viene passata come argomento del plot.
Esempio:
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
plt.plot([0.0, 1.0, 4.0, 9.0, 16.0, 25.0])
plt.show()
Una lista unica viene interpretata come la funzione da plottare lungo \(y\), ma se ne passiamo due la prima viene interpretata come asse \(x\). Infine, una stringa come terzo parametro viene interpretata come un descrittore per il colore della linea, lo stile, ecc.
Esempio: disegno di dati contenuti in due liste con linea continua (parametro '-') e pallini rossi (parametro 'or')...
import matplotlib.pyplot as plt
import numpy as np
plt.plot([0.0, 1.0, 2.0, 3.0, 4.0, 5.0],[0.0, 1.0, 4.0, 9.0, 16.0, 25.0],'or-')
plt.show()
Oppure si possono definire due o più ndarrays, da passare al plot:
x=np.linspace(0.0,2.0*np.pi,101)
y=np.sin(x)
z=np.cos(x)
plt.plot(x,y,'o-')
plt.plot(x,z,'^-')
plt.show()
Si possono definire varie funzioni da plottare con un unico comando (nota il colore differente prima del simbolo!):
plt.plot(x,y,'g-',x,z,'b-')
plt.show()
Tuttavia, è possibile indicare il colore anche con il nome completo (es.: green), con una sequenza RGB esadecimale (es: #FF0000 rappresenta il rosso!), con una tupla RGB o come toni di griglio tramite una stringa contenente un numero tra 0.0 e 1.0.
Esempio: (seno in verde, coseno in rosso)...
plt.plot(x,y,'g',x,z,'#A0A0A0')
plt.show()
(seno in giallo, coseno in grigio):
plt.plot(x,y,'yellow',x,z,'0.5')
plt.show()
Esempi:
plt.plot(x,y,'g-',x,z,'b--')
plt.show()
Mettere tutti i grafici in un unico comando di plot è semplice, ma separandoli si può avere una maggiore "flessibilità " nello specificare in maniera fine le caratteristiche della linea utilizzando delle keywords per indicare caratteristiche aggiuntive del grafico.
Esempio:
plt.plot(x,y,color='c',marker='x',linestyle=':')
plt.plot(x,z,color='#0000FF',marker='+',linestyle='--')
plt.show()
Esempi:
plt.plot(x,y,linestyle='-',linewidth=2.0,color='b',marker="x")
plt.plot(x,z,ls='-',lw=1.0,marker='x',markersize=10.0,color='y',markevery=(10))
plt.show()
Un altro modo di specificare le caratteristiche di una linea è farsi restituire un oggetto "linea" dalla funzione plot e modificarne le proprietà tramite la funzione setp().
linea = plt.plot(x,y)
plt.setp(linea,linestyle="-.",color="green")
plt.show()
E' naturalmente possibile migliorare il grafico inserendo un titolo, delle labels sugli assi, una griglia, del testo in posizione arbitraria, ecc.
def esponenziale_smorzato(x):
y=np.exp(-0.5*x)*np.cos(2.0*x)
return y
pi=np.pi
x=np.linspace(0.0,2.0*np.pi,101)
y=esponenziale_smorzato(x)
plt.plot(x,y,'ko-')
plt.title("Un esponenziale smorzato")
plt.xlabel("x")
plt.ylabel("y")
plt.grid(True)
plt.text(pi,np.exp(-0.5*pi),"Max 1")
plt.text(0.5*pi,-np.exp(-0.25*pi),"Min 1")
plt.axis([0.0,2.0*pi,-1.2,1.2]) # Nota la lista che specifica min e max degli assi!
plt.show()
Nota:
Una delle caratteristiche fondamentali di mathplotlib è quella di poter utilizzare il rendering in TeX o in LaTeX delle stringhe, facendole precedere da una "r" (che sta per "raw") per indicare a python di NON interpretare come sequenze di escape i codici TeX!
L'esempio di prima, un po' più carino:
plt.plot(x,y,'ko-')
plt.title(r"Un esponenziale smorzato: $e^{-\frac{x}{2}}\cos(2x)$")
plt.xlabel(r"$x$")
plt.ylabel(r"$y$")
plt.grid(True)
plt.text(pi,1.4*np.exp(-0.5*pi),"Max 1",horizontalalignment="center",fontsize=18)
plt.text(0.5*pi,-1.1*np.exp(-0.25*pi),"Min 1",horizontalalignment="right",verticalalignment="top")
plt.xlim(0.0,2.0*pi) # Nota che qui utilizziamo xlim e ylim per min e max degli assi invece di axis!
plt.ylim(-0.7,1.2)
plt.show()
E' possibile annotare un grafico con un testo e una freccia con la funzione "annotate()":
plt.plot(x,y,'ko-')
plt.title(r"Un esponenziale smorzato: $e^{-\frac{x}{2}}\cos(2x)$")
plt.xlabel(r"$x$")
plt.ylabel(r"$y$")
plt.grid(False) # Nota che qui abbiamo rimosso la griglia!
xpos=pi
ypos=np.exp(-0.5*pi)
# Nota i parametri della freccia: shrink allontana la punta e la base della freccia dal punto!
plt.annotate("Max 1", xy=(xpos,ypos), xytext=(xpos+1.0,ypos+0.5), arrowprops={"facecolor":"blue","shrink":0.05})
plt.xlim(0.0,2.0*pi) # Nota che qui utilizziamo xlim e ylim per min e max degli assi invece di axis!
plt.ylim(-0.7,1.2)
plt.show()
subplot(num_righe,num_cols,posizione)
Esempio:
plt.subplot(211)
plt.plot(x,y,'ko-')
plt.title("Un esponenziale smorzato")
plt.xlabel(r"$x$")
plt.ylabel(r"$y$")
plt.subplot(212)
plt.plot(x,np.abs(y),'ko-')
plt.title("Valore assoluto di un esponenziale smorzato")
plt.xlabel(r"$x$")
plt.ylabel(r"$|y|$")
plt.show()
xscale("log|linear") yscale("log|linear")
Esempio:
plt.subplot(211)
plt.plot(x,y,'ko-')
plt.title("Un esponenziale smorzato in scala y lineare")
plt.xlabel(r"$x$")
plt.ylabel(r"$y$")
plt.subplot(212)
plt.yscale("log")
plt.plot(x,np.abs(y),'ko-')
plt.title("Modulo di un esponenziale smorzato in scala y-log")
plt.xlabel(r"$x$")
plt.ylabel(r"$\log(|y|)$")
plt.show()
def power_law(x,n):
y=np.power(x,n)
return y
x=np.linspace(1.0,101.0,101)
y=power_law(x,-5.0/3.0)
plt.subplot(211)
plt.plot(x,y,'ko-')
plt.title("Una legge di potenza in scala lineare")
plt.xlabel(r"$x$")
plt.ylabel(r"$y$")
plt.subplot(212)
plt.xscale("log")
plt.yscale("log")
plt.plot(x,y,'ko-')
plt.title("Legge di potenza in scala x-y log")
plt.xlabel(r"$\log(x)$")
plt.ylabel(r"$\log(y)$")
plt.show()
E' possibile disegnare grafici in coordinate polari, a torta, a barre, ecc., con varie funzioni.
Esempio in coordinate polari:
r = np.linspace(0.0,5.0,101)
theta = 2.0 * np.pi * r
plt.polar(r, theta)
plt.show()
dims=[10.0,30.0,40.0,10.0]
etichette=["dato1","dato2","dato3","dato4"]
colori=['b','r','y','magenta']
plt.pie(dims,labels=etichette,colors=colori)
plt.show()
E' infine possibile produrre contour plots.
Esempio:
x=np.zeros((101,51),dtype=float)
y=np.zeros((101,51),dtype=float)
xx=np.linspace(-2.0,2.0,101)
yy=np.linspace(-2.0,2.0,51)
for j in range(51):
x[:,j]=xx
for i in range(101):
y[i,:]=yy
z=np.exp(-(x*x + y*y))
plt.contour(x,y,z)
plt.show()