La libreria Python per il calcolo scientifico e tecnico: ottimizzazione, matrici sparse, algoritmi su grafi, dati spaziali, interpolazione e test statistici. Con esempi pratici ed esercizi.
Introduzione a SciPy
SciPy (Scientific Python) è una libreria open-source per il calcolo scientifico e tecnico. Costruita sopra NumPy, aggiunge moduli specializzati per ottimizzazione, algebra lineare, integrazione, interpolazione, statistica, elaborazione di segnali e molto altro.
Se NumPy fornisce gli array e le operazioni di base, SciPy fornisce gli algoritmi scientifici pronti all'uso che ci lavorano sopra.
Sottomodulo
A cosa serve
scipy.constants
Costanti fisiche e matematiche
scipy.optimize
Trovare radici e minimi di funzioni
scipy.sparse
Matrici sparse (efficienti in memoria)
scipy.sparse.csgraph
Algoritmi su grafi
scipy.spatial
Distanze, triangolazioni, KD-Tree
scipy.interpolate
Interpolazione di dati
scipy.stats
Distribuzioni e test statistici
scipy.io
Lettura/scrittura formati (Matlab, ...)
scipy.linalg
Algebra lineare avanzata
💡 SciPy è scritto in gran parte in linguaggi compilati (C, C++, Fortran), quindi gli algoritmi sono velocissimi pur essendo richiamabili da Python.
Installazione e importazione
Installazione
# Nel terminale
pip install scipy
# In un notebook Jupyter
!pip install scipy
# Spesso insieme a NumPy (richiesto)
pip install numpy scipy
Importazione e versione
import scipy
print(scipy.__version__) # es. 1.13.0
# I sottomoduli vanno importati esplicitamente
from scipy import constants, optimize, stats
import numpy as np
print(constants.pi) # 3.141592653589793
⚠️ Importare solo import scipy non basta: i sottomoduli (come scipy.optimize) vanno importati esplicitamente con from scipy import optimize.
Costanti SciPy
Il modulo scipy.constants offre un grande catalogo di costanti fisiche e matematiche, oltre a fattori di conversione tra unità di misura.
from scipy import constants
# Tempo (in secondi)
print(constants.minute) # 60.0
print(constants.hour) # 3600.0
print(constants.day) # 86400.0
# Lunghezza (in metri)
print(constants.inch) # 0.0254
print(constants.mile) # 1609.344
print(constants.kilo) # 1000.0
# Massa (in kg)
print(constants.gram) # 0.001
print(constants.pound) # 0.45359237
# Categorie disponibili
print(constants.unit("speed of light in vacuum"))
Categoria
Esempi
Metrico
kilo, centi, milli
Tempo
minute, hour, year
Lunghezza
inch, foot, mile
Massa
gram, pound, ounce
Pressione
atm, bar, mmHg
Ottimizzatori (optimize)
Il modulo scipy.optimize offre algoritmi per trovare le radici di un'equazione e per minimizzare funzioni — operazioni centrali nel machine learning e nell'ingegneria.
Trovare le radici di un'equazione
root — risolvere f(x) = 0
from scipy.optimize import root
from math import cos
# Risolvi x + cos(x) = 0
def equazione(x):
return x + cos(x)
soluzione = root(equazione, 0) # 0 = stima iniziale
print(soluzione.x) # [-0.73908513]
print(soluzione.fun) # ~0 (valore di f nella soluzione)
Minimizzare una funzione
minimize — trovare il minimo
from scipy.optimize import minimize
# Minimizza f(x) = x^2 + x + 2
def f(x):
return x**2 + x + 2
ris = minimize(f, x0=0, method="BFGS")
print(ris.x) # [-0.5] → punto di minimo
print(ris.fun) # 1.75 → valore minimo
# Funzione a più variabili: f(x,y) = (x-1)^2 + (y-2.5)^2
def g(v):
x, y = v
return (x-1)**2 + (y-2.5)**2
ris2 = minimize(g, x0=[0, 0])
print(ris2.x) # [1. 2.5]
Metodo
Quando usarlo
BFGS
Funzioni lisce, derivabili (default)
Nelder-Mead
Senza derivate, robusto
Powell
Senza derivate, direzioni coniugate
L-BFGS-B
Con vincoli (bounds) sulle variabili
Dati sparsi (sparse)
Una matrice sparsa è una matrice in cui la maggior parte degli elementi è zero. Memorizzare solo gli elementi non-zero risparmia enormi quantità di memoria — fondamentale in NLP, sistemi di raccomandazione e reti.
import numpy as np
from scipy.sparse import csr_matrix
m = np.array([[0, 0, 1],
[1, 0, 0],
[0, 2, 0]])
s = csr_matrix(m)
# Elimina gli zeri duplicati / memorizzati inutilmente
s.eliminate_zeros()
# Converti di nuovo in array denso
print(s.toarray())
# CSC = Compressed Sparse Column (efficiente per colonne)
from scipy.sparse import csc_matrix
sc = csc_matrix(m)
# Somma, prodotto e altre operazioni funzionano come NumPy
print(s.sum()) # 4
Formato
Ottimizzato per
csr_matrix
Operazioni per riga, prodotti matrice-vettore
csc_matrix
Operazioni per colonna
coo_matrix
Costruzione rapida da coordinate
lil_matrix
Modifiche incrementali
Grafi (csgraph)
Il modulo scipy.sparse.csgraph implementa algoritmi classici sui grafi, rappresentati come matrici di adiacenza sparse.
Componenti connesse
import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import connected_components
# Matrice di adiacenza (grafo non orientato)
arr = np.array([
[0, 1, 2],
[1, 0, 0],
[2, 0, 0]
])
grafo = csr_matrix(arr)
n, etichette = connected_components(grafo)
print(n) # numero di componenti connesse
print(etichette) # a quale componente appartiene ogni nodo
Cammino minimo (Dijkstra)
import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import dijkstra, floyd_warshall
# Grafo pesato
arr = np.array([
[0, 1, 2],
[1, 0, 0],
[2, 0, 0]
])
grafo = csr_matrix(arr)
# Dijkstra dal nodo 0
dist, predecessori = dijkstra(
grafo, return_predecessors=True, indices=0
)
print(dist) # distanza minima dal nodo 0 a ogni nodo
# Floyd-Warshall: distanze tra tutte le coppie
print(floyd_warshall(grafo, directed=False))
Algoritmo
Funzione
Componenti connesse
connected_components
Cammino minimo (sorgente singola)
dijkstra, bellman_ford
Cammini tra tutte le coppie
floyd_warshall
Albero ricoprente minimo
minimum_spanning_tree
Dati spaziali (spatial)
Il modulo scipy.spatial gestisce dati nello spazio: calcolo di distanze, triangolazioni, inviluppi convessi e ricerche di vicinato efficienti con i KD-Tree.
Distanze tra punti
from scipy.spatial.distance import euclidean, cityblock, cosine
p1 = (1, 0, 0)
p2 = (10, 2, 7)
# Distanza euclidea (in linea d'aria)
print(euclidean(p1, p2)) # 11.575...
# Distanza Manhattan (a blocchi di città)
print(cityblock(p1, p2)) # 18
# Distanza coseno (similarità tra vettori)
print(cosine([1, 0], [0, 1])) # 1.0 (ortogonali)
print(cosine([1, 1], [1, 1])) # 0.0 (identici)
Triangolazione e Convex Hull
import numpy as np
from scipy.spatial import Delaunay, ConvexHull
punti = np.array([
[0, 0], [0, 1], [1, 0],
[1, 1], [0.5, 0.5]
])
# Triangolazione di Delaunay
tri = Delaunay(punti)
print(tri.simplices) # indici dei triangoli
# Inviluppo convesso (perimetro esterno)
hull = ConvexHull(punti)
print(hull.vertices) # punti sul bordo esterno
print(hull.area) # perimetro
print(hull.volume) # area racchiusa
KD-Tree: ricerca del vicino più prossimo
import numpy as np
from scipy.spatial import KDTree
punti = np.array([[1, 1], [2, 2], [3, 3], [5, 5]])
tree = KDTree(punti)
# Trova il punto più vicino a (2.1, 2.1)
dist, idx = tree.query([2.1, 2.1])
print(f"Vicino: {punti[idx]}, distanza: {dist:.3f}")
# I 2 vicini più prossimi
dist, idx = tree.query([2.1, 2.1], k=2)
print(punti[idx])
Array Matlab (io)
SciPy può leggere e scrivere file in formato Matlab (.mat), utile quando si collabora con chi usa Matlab o si riutilizzano dataset scientifici esistenti.
Salvare e caricare file .mat
import numpy as np
from scipy import io
# Dati da salvare
arr = np.arange(10)
# Salva in formato Matlab
io.savemat("dati.mat", {"vettore": arr})
# Carica un file .mat
contenuto = io.loadmat("dati.mat")
print(contenuto["vettore"]) # [[0 1 2 3 4 5 6 7 8 9]]
# I dati Matlab sono sempre 2D: usa squeeze per appiattire
print(contenuto["vettore"].squeeze()) # [0 1 ... 9]
💡 Matlab memorizza tutto come matrici 2D, quindi un vettore 1D salvato e ricaricato torna come matrice riga (1, n). Usa .squeeze() o squeeze_me=True in loadmat per ottenere di nuovo un array 1D.
Interpolazione (interpolate)
L'interpolazione stima nuovi valori "in mezzo" a punti dati noti. È usata per ricostruire curve, riempire dati mancanti o aumentare la risoluzione di un campionamento.
Interpolazione 1D
import numpy as np
from scipy.interpolate import interp1d
# Punti noti
x = np.arange(10)
y = 2 * x + 1 # retta: y = 2x + 1
# Crea la funzione interpolante
f = interp1d(x, y)
# Stima valori intermedi
print(f(2.5)) # 6.0
print(f([1.5, 4.5, 8.2])) # valori interpolati
# Interpolazione cubica (curva liscia)
f_cubica = interp1d(x, y, kind="cubic")
print(f_cubica(2.5))
Interpolazione con spline e dati rumorosi
import numpy as np
from scipy.interpolate import UnivariateSpline
# Dati con un po' di rumore
x = np.linspace(0, 10, 20)
y = np.sin(x) + np.random.normal(0, 0.1, 20)
# Spline che leviga il rumore
spline = UnivariateSpline(x, y, s=1)
# Valuta su una griglia più fitta
x_fine = np.linspace(0, 10, 200)
y_smooth = spline(x_fine)
print(y_smooth[:5])
kind / metodo
Risultato
linear
Segmenti retti tra i punti (default)
nearest
Valore del punto più vicino
cubic
Curva cubica liscia
UnivariateSpline
Spline che leviga dati rumorosi
Test di significatività (stats)
Il modulo scipy.stats è una cassetta degli attrezzi statistica completa: distribuzioni di probabilità, statistica descrittiva e test di ipotesi per verificare se un risultato è statisticamente significativo.
Concetti chiave
Ipotesi nulla (H₀): l'ipotesi "non c'è effetto / nessuna differenza".
p-value: probabilità di osservare i dati se H₀ è vera. Se p < 0.05 si rifiuta H₀.
T-test (confronto tra due gruppi)
import numpy as np
from scipy import stats
# Due gruppi di misurazioni
gruppo_a = np.array([20, 22, 19, 24, 21, 23, 20])
gruppo_b = np.array([28, 27, 30, 26, 29, 31, 28])
# T-test indipendente
t_stat, p_value = stats.ttest_ind(gruppo_a, gruppo_b)
print(f"t = {t_stat:.3f}")
print(f"p-value = {p_value:.5f}")
if p_value < 0.05:
print("Differenza SIGNIFICATIVA (rifiuto H0)")
else:
print("Nessuna differenza significativa")
Statistica descrittiva e normalità
import numpy as np
from scipy import stats
dati = np.array([12, 15, 14, 10, 18, 20, 13, 16, 11, 17])
# Statistiche descrittive
print(stats.describe(dati))
print("Media:", np.mean(dati))
print("Moda:", stats.mode(dati, keepdims=True).mode)
print("Asimmetria:", stats.skew(dati))
print("Curtosi:", stats.kurtosis(dati))
# Test di normalità (Shapiro-Wilk)
stat, p = stats.shapiro(dati)
print(f"Normalità p-value: {p:.4f}")
# p > 0.05 → i dati possono essere normali
Test
Domanda a cui risponde
ttest_ind
Due gruppi hanno medie diverse?
ttest_rel
Misure appaiate prima/dopo diverse?
f_oneway (ANOVA)
Tre o più gruppi differiscono?
chi2_contingency
Due variabili categoriche sono associate?
pearsonr
Due variabili sono correlate linearmente?
shapiro
I dati seguono una distribuzione normale?
Esercizi pratici
🏋️ Esercizio 1 — Trovare il minimo
Usa scipy.optimize.minimize per trovare il punto di minimo della funzione f(x) = (x - 3)² + 5. Verifica che il minimo sia in x = 3 con valore 5.
Soluzione Esercizio 1
from scipy.optimize import minimize
def f(x):
return (x - 3)**2 + 5
ris = minimize(f, x0=0)
print(f"Minimo in x = {ris.x[0]:.4f}") # 3.0000
print(f"Valore minimo = {ris.fun:.4f}") # 5.0000
🏋️ Esercizio 2 — T-test
Due classi hanno svolto lo stesso esame. Verifica con un t-test se i voti delle due classi sono significativamente diversi (soglia p < 0.05).
Soluzione Esercizio 2
import numpy as np
from scipy import stats
classe_a = np.array([6, 7, 5, 8, 7, 6, 7, 5])
classe_b = np.array([8, 9, 7, 9, 8, 9, 8, 7])
t, p = stats.ttest_ind(classe_a, classe_b)
print(f"p-value = {p:.5f}")
if p < 0.05:
print("Le due classi differiscono significativamente")
else:
print("Nessuna differenza significativa")
🏋️ Esercizio 3 — Distanza e vicino più prossimo
Dati 4 città su un piano cartesiano, costruisci un KD-Tree e trova quale città è più vicina al punto (3, 4).
Soluzione Esercizio 3
import numpy as np
from scipy.spatial import KDTree
citta = {"Roma": [0,0], "Milano": [2,5],
"Napoli": [4,3], "Torino": [1,6]}
nomi = list(citta.keys())
punti = np.array(list(citta.values()))
tree = KDTree(punti)
dist, idx = tree.query([3, 4])
print(f"Città più vicina: {nomi[idx]}")
print(f"Distanza: {dist:.3f}")
E ora?
Complimenti! Hai completato il corso SciPy e ora conosci gli strumenti per affrontare problemi scientifici reali in Python: ottimizzazione, algebra su matrici sparse, algoritmi su grafi, geometria computazionale, interpolazione e test statistici.
Cosa hai imparato:
Costanti fisiche e conversioni di unità
Ottimizzazione: radici e minimi di funzioni
Matrici sparse e algoritmi sui grafi
Dati spaziali: distanze, Convex Hull, KD-Tree
Interpolazione di dati e spline
Test di significatività e statistica inferenziale
Insieme a NumPy e Pandas, SciPy completa il trio fondamentale del calcolo scientifico in Python. Il passo successivo? Mettere tutto in pratica nel corso completo di Data Science.