La base per tutto: cos’è NumPy, creare array e fare operazioni semplici
Hai mai provato a sommare 1000 numeri con un calcolatore normale? Ci vorrebbero ore! E se ti dicessi che con NumPy puoi fare operazioni su milioni di numeri in pochi millisecondi?
In questo modulo scoprirai NumPy, la libreria che trasforma Python in una macchina da calcolo super-veloce.
Liste Python normali sono come fare la spesa con un carrello piccolo: puoi mettere qualsiasi cosa (mele, libri, scarpe), ma è lento e disorganizzato.
Array NumPy sono come i pallet di un supermercato: tutti gli articoli sono dello stesso tipo, perfettamente organizzati, e puoi muovere 1000 scatole tutte insieme con un solo carrello elevatore! ⚡
NumPy (Numerical Python) è una libreria, cioè un “pacchetto aggiuntivo” che puoi installare in Python per ottenere nuove funzionalità. In particolare, NumPy ti dà un nuovo tipo di dato chiamato array, pensato apposta per lavorare con i numeri in modo velocissimo.
Le operazioni su array NumPy sono fino a 100 volte più veloci delle liste Python normali
Puoi eseguire operazioni su migliaia di elementi contemporaneamente, senza scrivere loop!
Pandas, TensorFlow, Scikit-Learn... tutte le librerie AI usano NumPy come fondamenta
💡 Perché è così veloce?
NumPy è scritto in C e Fortran (linguaggi super-ottimizzati), non in Python puro. È come avere un motore di Formula 1 invece di una bicicletta! 🏎️
✅ Se usi Google Colab o Anaconda: NumPy è già installato! Puoi saltare l’installazione. 🎉
Apri il terminale e digita:
pip install numpyPrima di usare NumPy, devi importarlo. Importare significa dire a Python: “Carica questa libreria, ne ho bisogno!”
import numpy as np
print("NumPy versione:", np.__version__)
print("NumPy è pronto all'uso!")import numpy as npimport numpy → dice a Python: “carica la libreria NumPy nel mio programma”.
as np → le assegna un soprannome breve. Invece di scrivere numpy.qualcosa() ogni volta, scriverai np.qualcosa(). Meno lettere, stesso risultato!
Tutti i programmatori del mondo usano il soprannome np per NumPy. È una convenzione universale.
Da questo momento, ogni volta che vedi np. nel codice, sappi che stiamo usando una funzione di NumPy.
Le liste Python sono flessibili ma lente per i calcoli matematici. Per fare operazioni su tutti gli elementi devi sempre scrivere cicli for:
voti = [7, 8, 6, 9, 7.5, 8.5]
# Calcolare la media con una lista
somma = sum(voti) # sum() esiste anche per le liste
media = somma / len(voti)
print(f"Media: {media:.2f}") # 7.67
# Ma prova a fare questo con una lista:
voti_aumentati = voti + 1 # ERRORE! Non puoi fare operazioni matematiche
voti_doppi = voti * 2 # Ripete la lista, non moltiplica! [7,8,6,9,7.5,8.5,7,8,6,9,7.5,8.5]⚠️ Attenzione! Con le liste Python:
❌ lista + 1 → ERRORE (non puoi sommare un numero)
❌ lista * 2 → Ripete la lista 2 volte, non moltiplica i valori!
❌ lista / 2 → ERRORE (non esiste)
Gli array NumPy sono progettati per i calcoli matematici. Le operazioni funzionano elemento per elemento automaticamente:
import numpy as np
voti_np = np.array([7, 8, 6, 9, 7.5, 8.5])
# Tutte queste operazioni funzionano!
media = voti_np.mean() # 7.67
voti_aumentati = voti_np + 1 # [8. 9. 7. 10. 8.5 9.5]
voti_doppi = voti_np * 2 # [14. 16. 12. 18. 15. 17.]
voti_divisi = voti_np / 2 # [3.5 4. 3. 4.5 3.75 4.25]
print(f"Media: {media:.2f}")
print(f"Aumentati di 1: {voti_aumentati}")
print(f"Doppiati: {voti_doppi}")Le liste Python sono pensate per contenere qualsiasi cosa (numeri, testo, altre liste...) e sono flessibili ma lente per la matematica.
Gli array NumPy contengono solo numeri dello stesso tipo e sono ottimizzati in C/Fortran per fare calcoli velocissimi.
Con NumPy puoi scrivere array + 5 invece di fare un ciclo for: NumPy applica l'operazione a tutti gli elementi in automatico!
✓ Flessibili (qualsiasi tipo)
✓ Hanno sum(), len()
✗ Niente lista + 1
✗ Niente .mean(), .std()
✗ Lente per calcoli
✓ Solo un tipo di numero
✓ Operazioni + - * / automatiche
✓ Funzioni statistiche integrate
✓ 100x più veloci
✓ Base per AI/ML
import numpy as np
temperature = [22, 25, 18, 30, 15, 28]
temp_array = np.array(temperature)
print("Temperature:", temp_array)
print("Tipo elementi:", temp_array.dtype).dtypedtype sta per “data type” (tipo di dato). Ti dice che tipo di numeri ci sono dentro l’array.
int64 = numeri interi, float64 = numeri con la virgola.
Non ci sono le parentesi perché dtype non è una funzione ma una proprietà (un’informazione).
zeri = np.zeros(5)
print("5 zeri:", zeri) # [0. 0. 0. 0. 0.]
uni = np.ones(5)
print("5 uni:", uni) # [1. 1. 1. 1. 1.]
# Trucco: moltiplica per avere un array con il valore che vuoi
array_di_10 = np.ones(5) * 10
print("5 dieci:", array_di_10) # [10. 10. 10. 10. 10.]arange() e linspace()# arange(inizio, fine, passo) - numeri a salti regolari
# "Parti da 0, vai fino a 10 (escluso), a salti di 2"
numeri = np.arange(0, 10, 2)
print("arange(0, 10, 2):", numeri) # [0 2 4 6 8]
# linspace(inizio, fine, quanti) - N punti equidistanti
# "Dammi 5 punti distribuiti uniformemente tra 0 e 1"
punti = np.linspace(0, 1, 5)
print("linspace(0, 1, 5):", punti) # [0. 0.25 0.5 0.75 1. ]Con arange decidi il passo (ogni quanto saltare) e NumPy calcola quanti numeri escono.
Con linspace decidi quanti numeri vuoi e NumPy calcola il passo automaticamente.
Attenzione: arange esclude il valore finale (come range()), mentre linspace lo include!
# 5 numeri casuali decimali tra 0 e 1
casuali = np.random.random(5)
print("Casuali:", casuali)
# 10 numeri interi casuali tra 1 e 6 (come un dado)
dadi = np.random.randint(1, 7, size=10)
print("10 lanci di dado:", dadi)np.random.randint(1, 7, size=10)np.random → il sotto-modulo di NumPy dedicato ai numeri casuali.
.randint(1, 7, ...) → genera numeri interi casuali. Il primo numero (1) è il minimo, il secondo (7) è il massimo escluso. Quindi i valori possibili sono: 1, 2, 3, 4, 5, 6.
size=10 → quanti numeri casuali generare.
Ogni array NumPy ha due proprietà fondamentali che ti dicono quanti elementi contiene e come sono organizzati:
.size - Quanti elementi in totale?import numpy as np
voti = np.array([7, 8, 6, 9, 7.5, 8.5])
print("Quanti voti?", voti.size) # 6
temperature = np.array([22, 25, 18, 30, 15, 28, 19, 21])
print("Quante temperature?", temperature.size) # 8.size.size conta tutti gli elementi dell'array, dal primo all'ultimo.
È come chiedere: "Quanti numeri ci sono dentro?"
Nota: .size è una proprietà, quindi NON ha le parentesi!
.shape - Come sono organizzati?La proprietà .shape ti dice la forma dell'array. Per ora lavoriamo solo con array unidimensionali (una sola riga di numeri):
voti = np.array([7, 8, 6, 9, 7.5, 8.5])
print("Forma dell'array:", voti.shape) # (6,)
temperature = np.array([22, 25, 18, 30])
print("Forma:", temperature.shape) # (4,)(6,) e non solo 6?shape restituisce sempre una tupla (una sequenza tra parentesi).
(6,) significa: "array con 6 elementi in una dimensione" (una riga).
La virgola indica che è una tupla con un solo valore. Nei prossimi moduli vedrai array 2D come matrici, con shape tipo (3, 4) = 3 righe, 4 colonne.
Array: [10, 20, 30, 40, 50]
.size = 5 (conta tutti gli elementi)
.shape = (5,) (5 elementi in una dimensione)
import numpy as np
# Creiamo un array di temperature
temp = np.array([22, 25, 18, 30, 15, 28])
print(f"Array completo: {temp}")
print(f"Tipo di dato: {temp.dtype}") # int64 o float64
print(f"Quante misure: {temp.size}") # 6
print(f"Forma: {temp.shape}") # (6,)
print(f"Media: {temp.mean():.2f}°C") # 23.00°C💡 Ricorda:
.size → Numero totale di elementi
.shape → Come sono organizzati (per ora sempre una riga: (n,))
.dtype → Che tipo di numeri (int, float...)
Quando lavori con i dati, è fondamentale stampare i risultati in modo ordinato e leggibile. Le f-string di Python ti permettono di formattare numeri, testo e tabelle con precisione millimetrica!
nome = "Mario"
età = 17
media = 7.66666
# f-string base: metti variabili dentro {}
print(f"Ciao {nome}, hai {età} anni e la tua media è {media}")
# Output: Ciao Mario, hai 17 anni e la tua media è 7.66666:.2fPer limitare i decimali usa :.Nf dove N è il numero di cifre dopo la virgola:
import numpy as np
voti = np.array([7, 8, 6.5, 9, 7.5, 8.5])
media = voti.mean()
print(f"Media: {media:.2f}") # 7.75 (2 decimali)
print(f"Media: {media:.3f}") # 7.750 (3 decimali)
print(f"Media: {media:.1f}") # 7.8 (1 decimale, arrotondato){media:.2f}media → la variabile da stampare
: → "inizia la formattazione"
.2 → "usa 2 cifre decimali"
f → "formato numero con virgola" (float)
:<15, :>15, :^15Puoi riservare uno spazio fisso per ogni campo, così le colonne si allineano perfettamente:
# :<15 = allinea a SINISTRA, occupa 15 caratteri
# :>15 = allinea a DESTRA, occupa 15 caratteri
# :^15 = allinea al CENTRO, occupa 15 caratteri
print(f"{'Nome':<15} {'Cognome':<15} {'Età':>5}")
print(f"{'Mario':<15} {'Rossi':<15} {17:>5}")
print(f"{'Anna':<15} {'Verdi':<15} {18:>5}")
print(f"{'Giuseppe':<15} {'Bianchi':<15} {16:>5}"):<15.2fPer i numeri con la virgola, puoi combinare allineamento e precisione decimale:
import numpy as np
nomi = ["Mario", "Anna", "Giuseppe"]
voti_scritto = np.array([7.5, 8.25, 6.75])
voti_orale = np.array([8.0, 7.5, 7.0])
medie = (voti_scritto + voti_orale) / 2
# Tabella formattata con allineamento E decimali
print(f"{'Nome':<15} {'Scritto':>10} {'Orale':>10} {'Media':>10}")
print("-" * 50) # Linea separatrice
for i in range(len(nomi)):
print(f"{nomi[i]:<15} {voti_scritto[i]:>10.2f} {voti_orale[i]:>10.2f} {medie[i]:>10.2f}"){voti_scritto[i]:>10.2f}voti_scritto[i] → il valore da stampare
: → inizia la formattazione
> → allinea a DESTRA
10 → occupa 10 caratteri di spazio
.2f → mostra 2 decimali, formato float
Quando hai molti valori decimali da stampare, ogni variabile dentro le graffe ha bisogno del suo :.2f:
import numpy as np
temp_mattina = 18.66666
temp_pomeriggio = 25.33333
temp_sera = 21.77777
media_giorno = np.mean([temp_mattina, temp_pomeriggio, temp_sera])
# Ogni variabile ha il suo :.2f
print(f"Mattina: {temp_mattina:.2f}°C | Pomeriggio: {temp_pomeriggio:.2f}°C | Sera: {temp_sera:.2f}°C")
print(f"Media del giorno: {media_giorno:.2f}°C")
# Output:
# Mattina: 18.67°C | Pomeriggio: 25.33°C | Sera: 21.78°C
# Media del giorno: 21.93°C⚠️ Errore comune: mettere :.2f FUORI dalle graffe
La formattazione va dentro le graffe {}, non fuori!
# ✗ SBAGLIATO - :.2f fuori dalle graffe
print(f"Media: {media}:.2f") # ERRORE! Python non capisce
# ✓ CORRETTO - :.2f dentro le graffe
print(f"Media: {media:.2f}") # Funziona!
# ✗ SBAGLIATO - pensare che basta una volta
print(f"{temp1} e {temp2:.2f}") # ERRORE! Formatta solo temp2
# ✓ CORRETTO - ogni variabile ha il suo formato
print(f"{temp1:.2f} e {temp2:.2f}") # Funziona!{variabile} → stampa così com'è
{variabile:.2f} → 2 decimali
{variabile:<15} → allineato a sinistra, 15 caratteri
{variabile:>15} → allineato a destra, 15 caratteri
{variabile:^15} → centrato, 15 caratteri
{variabile:>10.2f} → destra, 10 caratteri, 2 decimali
import numpy as np
# Dati di esempio
prodotti = ["Penna", "Quaderno", "Zaino", "Astuccio"]
prezzi = np.array([1.50, 3.75, 25.99, 8.50])
sconti = np.array([0.10, 0.15, 0.20, 0.05]) # 10%, 15%, 20%, 5%
prezzi_finali = prezzi * (1 - sconti)
# Stampa tabella formattata
print(f"{'Prodotto':<15} {'Prezzo':>10} {'Sconto':>10} {'Finale':>10}")
print("=" * 50)
for i in range(len(prodotti)):
print(f"{prodotti[i]:<15} {prezzi[i]:>10.2f}€ {sconti[i]*100:>9.0f}% {prezzi_finali[i]:>10.2f}€")Puoi usare un singolo numero con un intero array. NumPy applica quel numero a ogni elemento automaticamente. Questo meccanismo si chiama broadcasting:
prezzi = np.array([10, 20, 30, 40, 50])
prezzi_doppi = prezzi * 2
prezzi_scontati = prezzi - 5
prezzi_aumentati = prezzi * 1.2 # +20%
print(f"Originali: {prezzi}")
print(f"Doppi: {prezzi_doppi}")
print(f"Scontati 5€: {prezzi_scontati}")
print(f"+20%: {prezzi_aumentati}")prezzi * 1.2:
NumPy "espande" il numero 1.2 e lo applica a ogni elemento:
10 * 1.2 = 12.0
20 * 1.2 = 24.0
30 * 1.2 = 36.0
40 * 1.2 = 48.0
50 * 1.2 = 60.0
Risultato: [12.0, 24.0, 36.0, 48.0, 60.0]Quando usi +, -, * o / tra due array della stessa lunghezza, NumPy applica l’operazione posizione per posizione:
scritto = np.array([7, 8, 6, 9, 7.5])
orale = np.array([8, 7.5, 7, 8.5, 8])
# Media ponderata: scritto vale 60%, orale vale 40%
voto_finale = scritto * 0.6 + orale * 0.4
print(f"Scritto: {scritto}")
print(f"Orale: {orale}")
print(f"Finale: {voto_finale}")scritto * 0.6 + orale * 0.4:
Prima: scritto * 0.6
7*0.6=4.2 8*0.6=4.8 6*0.6=3.6 9*0.6=5.4 7.5*0.6=4.5
Poi: orale * 0.4
8*0.4=3.2 7.5*0.4=3.0 7*0.4=2.8 8.5*0.4=3.4 8*0.4=3.2
Infine: somma posizione per posizione
4.2+3.2=7.4 4.8+3.0=7.8 3.6+2.8=6.4 5.4+3.4=8.8 4.5+3.2=7.7
Risultato: [7.4, 7.8, 6.4, 8.8, 7.7]Immagina una radio che trasmette (broadcast) un messaggio a tutti. Quando scrivi prezzi * 2, NumPy “trasmette” il numero 2 a tutti gli elementi dell’array, come una radio che invia lo stesso segnale a tutti i ricevitori!
voti = np.array([5, 7, 8, 4, 6, 9, 5.5, 7, 8.5, 6])
print(f"Voti: {voti}")
print(f"Media: {voti.mean():.2f}")
print(f"Minimo: {voti.min():.2f}")
print(f"Massimo: {voti.max():.2f}")
print(f"Somma: {voti.sum():.2f}")
print(f"Quanti voti: {voti.size}")💡 Funzione vs Proprietà: quando mettere le parentesi?
.mean(), .min(), .max(), .sum() → con parentesi perché sono funzioni (eseguono un calcolo).
.size, .dtype, .shape → senza parentesi perché sono proprietà (informazioni già pronte).
❌ ERRORE 1: Confondere lista e array
lista = [1, 2, 3]
array = np.array([1, 2, 3])
# print(lista.mean()) # ERRORE! Le liste non hanno .mean()
print(array.mean()) # CORRETTO: 2.0
# lista + 1 # ERRORE! Non puoi fare operazioni matematiche
print(array + 1) # CORRETTO: [2 3 4]❌ ERRORE 2: Dimenticare di importare NumPy
# array([1, 2, 3]) # ERRORE: NumPy non è importato
import numpy as np
np.array([1, 2, 3]) # CORRETTO❌ ERRORE 3: Tipi di dati misti
strano = np.array([1, 2, "tre"])
print(strano) # ['1' '2' 'tre'] Tutti diventano testo!
buono = np.array([1, 2, 3]) # Tutti numeri interi OK
buono2 = np.array([1.5, 2.0, 3.7]) # Tutti decimali OK❌ ERRORE 4: Moltiplicare una lista (non è come NumPy!)
# Con le LISTE, * ripete la lista:
lista = [1, 2, 3]
print(lista * 2) # [1, 2, 3, 1, 2, 3] Ripetuta!
# Con NUMPY, * moltiplica ogni elemento:
array = np.array([1, 2, 3])
print(array * 2) # [2 4 6] Moltiplicato!❌ ERRORE 5: Formattazione f-string fuori posto
media = 7.66666
# ✗ SBAGLIATO: :.2f fuori dalle graffe
# print(f"Media: {media}:.2f") # ERRORE!
# ✓ CORRETTO: :.2f dentro le graffe
print(f"Media: {media:.2f}") # Media: 7.67Hai registrato i tuoi pesi sollevati in panca piana per 6 allenamenti: [50, 52.5, 55, 52.5, 57.5, 60] kg
.size, .shape e .dtypeLe tue spese settimanali di gennaio: [120, 85, 150, 95] euro
.sum())Settimana | Spesa Attuale | Spesa RisparmiosaHai corso per 7 giorni e registrato i km: [3.2, 4.5, 0, 5.1, 3.8, 6.0, 4.2] (lo 0 è il giorno di riposo)
.max())Giorno | KM | Metri usando {'Lunedì':<12} per i nomiSimula un esperimento con i dadi!
np.random.randint(1, 7, size=100)Gestisci un negozio con 5 prodotti:
["Smartphone", "Tablet", "Cuffie", "Smartwatch", "Speaker"][699, 449, 89, 299, 149] euro[500, 320, 60, 200, 100] euro(margine / acquisto) * 100Esplora le funzioni di creazione array:
np.arange()np.linspace()np.random.random()linspace per 3 e sottrai 5. Qual è il nuovo min e max?Sfida avanzata che combina tutto!
[1200, 1350, 1180, 1420, 1500] e vendite_2024 = [1450, 1520, 1380, 1680, 1750]vendite_2024 - vendite_2023((2024 - 2023) / 2023) * 100.max() sull'array delle differenze).sum())# La tabella dovrebbe essere simile a questa:
# Mese 2023 2024 Diff€ Crescita%
# Gennaio 1200 1450 250 20.83%
# Febbraio 1350 1520 170 12.59%
# ...
# TOTALE 6650 7780 1130 17.00%
#
# Usa: {mese:<12} {v2023:>8.0f} {v2024:>8.0f} {diff:>8.0f} {crescita:>10.2f}%Nel Modulo 3.2 imparerai le statistiche avanzate: