Introduzione ai dtype e alla gestione della memoria in NumPy

by theArchitect
SHARE
Introduzione ai dtype e alla gestione della memoria in NumPy
© Guida-HTML5.it

Introduzione

Quando si inizia a usare NumPy, è facile concentrarsi subito sugli array e sulle operazioni matematiche. Tuttavia, uno dei veri punti di forza della libreria è la gestione efficiente dei tipi di dato e della memoria. Capire come funziona il parametro dtype ti permette di scrivere codice più veloce, più preciso e meno soggetto a errori.

In questo tutorial vedremo un aspetto molto pratico e spesso sottovalutato: come scegliere il dtype giusto e come questo influenza prestazioni, occupazione di memoria e risultati numerici. È un argomento fondamentale per chi lavora con dati scientifici, serie temporali, immagini, simulazioni o dataset numerosi.

Imparerai a:

  • creare array con tipi di dato diversi;
  • capire la differenza tra interi, float e tipi booleani;
  • ridurre l’uso di memoria scegliendo dtype più adatti;
  • evitare conversioni indesiderate e risultati imprevisti;
  • usare alcune tecniche pratiche per controllare il comportamento degli array.

Codice completo

import numpy as np

# 1. Creazione di array con dtype diversi
interi_64 = np.array([1, 2, 3, 4], dtype=np.int64)
interi_32 = np.array([1, 2, 3, 4], dtype=np.int32)
float_64 = np.array([1.5, 2.5, 3.5], dtype=np.float64)
float_32 = np.array([1.5, 2.5, 3.5], dtype=np.float32)
booleani = np.array([True, False, True], dtype=np.bool_)

print("interi_64:", interi_64, interi_64.dtype)
print("interi_32:", interi_32, interi_32.dtype)
print("float_64:", float_64, float_64.dtype)
print("float_32:", float_32, float_32.dtype)
print("booleani:", booleani, booleani.dtype)

# 2. Confronto dell´occupazione di memoria
print("nMemoria occupata:")
print("int64 :", interi_64.nbytes, "byte")
print("int32 :", interi_32.nbytes, "byte")
print("float64:", float_64.nbytes, "byte")
print("float32:", float_32.nbytes, "byte")

# 3. Conversione esplicita tra dtype
numeri = np.array([10, 20, 30], dtype=np.int32)
numeri_float = numeri.astype(np.float64)

print("nConversione:")
print("Originale:", numeri, numeri.dtype)
print("Convertito:", numeri_float, numeri_float.dtype)

# 4. Attenzione alle conversioni implicite
a = np.array([1, 2, 3], dtype=np.int32)
b = np.array([0.5, 1.5, 2.5], dtype=np.float64)

somma = a + b
print("nSomma tra int32 e float64:")
print(somma)
print("dtype risultato:", somma.dtype)

# 5. Rischio di overflow con interi piccoli
piccoli = np.array([120, 121, 122], dtype=np.int8)
risultato = piccoli + 10

print("nOverflow potenziale:")
print("Array originale:", piccoli)
print("Risultato +10:", risultato)
print("dtype:", risultato.dtype)

# 6. Precisione nei calcoli con float32 e float64
valori32 = np.array([0.1, 0.2, 0.3], dtype=np.float32)
valori64 = np.array([0.1, 0.2, 0.3], dtype=np.float64)

somma32 = np.sum(valori32)
somma64 = np.sum(valori64)

print("nPrecisione:")
print("Somma float32:", somma32)
print("Somma float64:", somma64)

# 7. Creazione efficiente di array grandi
grande_float32 = np.ones(1_000_000, dtype=np.float32)
grande_float64 = np.ones(1_000_000, dtype=np.float64)

print("nArray grandi:")
print("float32 nbytes:", grande_float32.nbytes)
print("float64 nbytes:", grande_float64.nbytes)

# 8. Controllo del dtype in una funzione
def normalizza_dati(array):
    """
    Converte l´input in float64 per garantire calcoli stabili.
    """
    array = np.asarray(array, dtype=np.float64)
    return (array - np.mean(array)) / np.std(array)

dati = [10, 12, 14, 16, 18]
normalizzati = normalizza_dati(dati)

print("nDati normalizzati:")
print(normalizzati)
print("dtype:", normalizzati.dtype)

Spiegazione

Il parametro dtype indica il tipo di dato memorizzato nell’array. È uno degli aspetti più importanti di NumPy, perché ogni elemento di un array omogeneo usa lo stesso formato in memoria. Questo rende gli array molto efficienti rispetto alle liste Python.

1. Tipi di dato più comuni

Nel codice abbiamo usato alcuni dtype frequenti:

  • np.int32 e np.int64: interi con dimensioni diverse;
  • np.float32 e np.float64: numeri decimali con precisione diversa;
  • np.bool_: valori booleani, cioè True e False.

La differenza tra 32 e 64 bit è importante: un tipo a 32 bit occupa meno memoria, ma offre un intervallo o una precisione inferiore rispetto al corrispondente a 64 bit.

2. Memoria occupata

Il metodo nbytes mostra quanti byte occupa l’array. Questo è utile quando lavori con milioni di elementi. Per esempio, un array float32 occupa metà della memoria di un float64 a parità di numero di elementi.

Questo può fare una grande differenza in applicazioni reali, come:

  • elaborazione di immagini;
  • simulazioni numeriche;
  • machine learning;
  • analisi di dataset molto grandi.

3. Conversione esplicita con astype()

Il metodo astype() serve a convertire un array in un altro dtype. È una scelta sicura quando vuoi controllare il tipo finale dei dati.

Per esempio, se stai preparando dati per un algoritmo che richiede numeri decimali, convertire tutto in float64 può evitare problemi di divisione intera o di arrotondamento.

4. Conversioni implicite nei calcoli

Quando combini array con dtype diversi, NumPy applica delle regole di promozione del tipo. Nell’esempio, sommando un array int32 con uno float64, il risultato diventa float64.

Questo comportamento è utile, ma va compreso bene. Se non lo conosci, potresti aspettarti un risultato intero o perdere di vista il motivo per cui la memoria aumenta dopo un’operazione.

5. Attenzione all’overflow

Con tipi interi piccoli, come int8, è possibile superare il valore massimo rappresentabile. Questo fenomeno si chiama overflow. Nel codice, valori vicini al limite possono produrre risultati inattesi dopo una somma.

È un errore tipico in chi lavora con dati grezzi senza controllare il tipo. Per questo, quando non hai vincoli di memoria troppo stretti, usare int32 o int64 è spesso più sicuro.

6. Precisione numerica

I float32 occupano meno memoria, ma hanno meno precisione dei float64. In molti casi questo è accettabile, ma in calcoli scientifici delicati può introdurre piccole differenze che, accumulate, diventano significative.

La scelta tra float32 e float64 dipende dal contesto:

  • float32: utile quando vuoi risparmiare memoria e la precisione richiesta non è estrema;
  • float64: consigliato per analisi numeriche generiche e calcoli più stabili.

7. Uso di np.asarray nelle funzioni

Nella funzione normalizza_dati abbiamo usato np.asarray(array, dtype=np.float64). Questa è una buona pratica perché accetta input diversi, come liste Python o array NumPy, e li converte in un formato coerente.

In questo modo la funzione lavora sempre con float64, rendendo i calcoli di media e deviazione standard più affidabili.

Best practice

  • Scegli il dtype in base al problema: non usare sempre float64 o int64 per abitudine.
  • Controlla la memoria con nbytes quando gestisci array grandi.
  • Converti esplicitamente i dati con astype() o np.asarray() quando vuoi evitare ambiguità.
  • Evita dtype troppo piccoli se i valori possono crescere molto, per ridurre il rischio di overflow.
  • Usa float64 nei calcoli numerici sensibili, soprattutto se devi fare statistiche, normalizzazioni o operazioni iterative.
  • Verifica sempre array e dtype con array.dtype prima di eseguire operazioni importanti.

Un consiglio pratico: quando sviluppi una pipeline di dati, definisci il dtype all’ingresso. Questo ti aiuta a evitare conversioni ripetute e comportamenti incoerenti tra funzioni diverse.

Riepilogo

In questa introduzione ai dtype e alla gestione della memoria in NumPy hai visto un aspetto fondamentale ma spesso trascurato. Il tipo di dato non è solo un dettaglio tecnico: influenza prestazioni, precisione e stabilità dei calcoli.

In sintesi:

  • gli array NumPy sono omogenei e ogni elemento ha lo stesso dtype;
  • scegliere il dtype giusto aiuta a risparmiare memoria;
  • float32 e float64 hanno compromessi diversi tra precisione e spazio;
  • gli interi piccoli possono causare overflow;
  • le conversioni esplicite rendono il codice più chiaro e prevedibile.

Se stai iniziando con NumPy, imparare a controllare il dtype è un passo molto importante per scrivere codice professionale e robusto.

Approfondisci con risorse ufficiali

  • Documentazione ufficiale NumPy sui tipi di dato: https://numpy.org/doc/stable/user/basics.types.html
  • Riferimento API per ndarray: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html
  • Funzione astype(): https://numpy.org/doc/stable/reference/generated/numpy.ndarray.astype.html
  • Documentazione su np.asarray(): https://numpy.org/doc/stable/reference/generated/numpy.asarray.html

SHARE