Avant d’optimiser un groupby, vous affichez ce que le tableau coûte vraiment :
df.dtypes
df.memory_usage(deep=True).sort_values(ascending=False)Sans deep=True, Pandas compte les pointeurs des colonnes object, pas les chaînes Python derrière. Le rapport « 80 Mo » peut cacher 2 Go.
object sur du texte. Chaque cellule est un PyObject. Huit octets de pointeur, plus la chaîne, plus l’overhead de l’allocateur. Mille codes pays distincts répétés sur dix millions de lignes : vous payez dix millions de chaînes.
float64 pour des entiers troués. Une seule valeur manquante force souvent l’inférence CSV vers float64 : 8 octets, plus des 12.0 qui n’étaient pas des décimales. Le dtype nullable Int64 (majuscule) garde l’entier et le NA sans passer par le flottant — plus correct, pas toujours plus petit.
int64 / float64 par défaut. Un identifiant sur 0–50 000 tient en uint32 ou int32. Un indicateur 0/1 tient en int8 ou boolean. Le downcast n’est pas de la micro-optimisation : sur cinquante colonnes, il décide si le job tient en RAM.
Les timestamps. datetime64[ns] est compact et vectorisé. Une date laissée en object est le pire des deux mondes : lente, et volumineuse.
object.category une colonne dont la cardinalité est basse devant le nombre de lignes — pays, canal, statut. La mémoire devient un entier de code plus la table des modalités.astype("string") (nullable) plutôt que object pour du texte vraiment distinct : plus sûr sur les NA, pas forcément plus léger.get_dummies mal placé peut détruire le gain du category.df = pd.read_csv(
"ventes.csv",
usecols=["date", "pays", "montant"],
dtype={"pays": "category", "montant": "float32"},
parse_dates=["date"],
)Le tutoriel nettoyer des données sales commence par dtype="string" justement pour empêcher un code postal de devenir float.
La mémoire d’un DataFrame n’est pas « le nombre de lignes × une constante ». C’est la somme des représentations. Tant que les dtypes sont faux, toute discussion de performance est prématurée.