ITSkillsCenter
Blog

Python pandas : traiter Excel et CSV en pratique

13 min de lecture

Lecture : 11 minutes · Niveau : intermédiaire · Mise à jour : avril 2026

pandas est la bibliothèque Python qui transforme la manipulation de données tabulaires en une activité productive. Là où Excel atteint ses limites (gros volumes, opérations répétitives, automatisation), pandas prend le relais avec une syntaxe puissante. Ce tutoriel se concentre sur les patterns vraiment utiles en PME, sans dériver vers le data science sophistiqué.

Voir aussi → Python pour PME : guide pratique.


Sommaire

  1. Lire un fichier proprement
  2. Inspecter et explorer
  3. Sélectionner et filtrer
  4. Nettoyer les données
  5. Agrégations et groupby
  6. Joindre plusieurs sources
  7. Pivot tables et tableaux croisés
  8. Écrire un Excel formaté
  9. Pièges classiques et solutions
  10. FAQ

1. Lire un fichier proprement

import pandas as pd

# CSV simple
df = pd.read_csv("ventes.csv")

# CSV avec subtilités
df = pd.read_csv(
    "ventes.csv",
    encoding="utf-8",       # ou "latin-1" pour des fichiers anciens
    sep=";",                 # séparateur français
    decimal=",",             # virgule décimale française
    parse_dates=["date_vente"],
    dtype={"code_produit": str},   # éviter conversion en int
    na_values=["N/A", "-", ""],
)

# Excel avec feuille spécifique
df = pd.read_excel("rapport.xlsx", sheet_name="Ventes 2026")

# Toutes les feuilles d'un fichier Excel
feuilles = pd.read_excel("rapport.xlsx", sheet_name=None)
# feuilles est un dict {nom_feuille: DataFrame}

Encoding : le piège classique

Les fichiers exportés depuis Excel sur Windows sont souvent en cp1252 ou latin-1, pas UTF-8. Si vous voyez é qui devient é, vous avez un problème d’encoding.

# Détecter automatiquement
import chardet
with open("ventes.csv", "rb") as f:
    encoding = chardet.detect(f.read())["encoding"]
df = pd.read_csv("ventes.csv", encoding=encoding)

2. Inspecter et explorer

Avant tout traitement, comprendre la structure du DataFrame.

# Aperçu
df.head(10)              # 10 premières lignes
df.tail(5)               # 5 dernières
df.sample(20)            # 20 lignes aléatoires

# Dimensions
df.shape                 # (lignes, colonnes)
len(df)                  # nombre de lignes

# Métadonnées
df.dtypes                # type de chaque colonne
df.info()                # vue compacte : type + non-null count
df.describe()            # statistiques pour les colonnes numériques
df.describe(include="object")  # idem pour les colonnes texte

# Valeurs uniques
df["region"].unique()
df["region"].value_counts()  # comptage par valeur

# Valeurs manquantes
df.isna().sum()          # nombre de NaN par colonne
df.isna().sum().sort_values(ascending=False)

df.info() est souvent la première commande à lancer : elle révèle d’un coup les colonnes mal typées (numérique stocké en string, dates en string).


3. Sélectionner et filtrer

# Une colonne (Series)
df["client"]

# Plusieurs colonnes (DataFrame)
df[["client", "produit", "montant"]]

# Filtre booléen
df[df["montant"] > 1000]

# Filtres combinés
df[(df["montant"] > 1000) & (df["region"] == "Dakar")]
df[(df["region"] == "Dakar") | (df["region"] == "Thiès")]
df[df["region"].isin(["Dakar", "Thiès", "Saint-Louis"])]

# Texte : startswith, contains
df[df["client"].str.contains("SARL", case=False, na=False)]
df[df["produit"].str.startswith("PRD-")]

# Dates
df[df["date_vente"] >= "2026-01-01"]
df[df["date_vente"].dt.year == 2026]
df[df["date_vente"].dt.month == 4]

# Sélection par index avec loc
df.loc[df["region"] == "Dakar", ["client", "montant"]]

loc permet de combiner sélection de lignes et de colonnes en une seule opération, plus lisible que les filtres successifs.


4. Nettoyer les données

Doublons

# Détecter
df.duplicated().sum()
df[df.duplicated(keep=False)]   # voir les doublons

# Supprimer
df = df.drop_duplicates()
df = df.drop_duplicates(subset=["email"])  # selon une colonne

Valeurs manquantes

# Supprimer les lignes avec NaN
df = df.dropna()
df = df.dropna(subset=["email"])     # seulement si colonnes critiques NaN

# Remplir
df["montant"] = df["montant"].fillna(0)
df["region"] = df["region"].fillna("Inconnue")
df["prix"] = df["prix"].fillna(df["prix"].mean())  # moyenne

Texte

# Strip + casse
df["client"] = df["client"].str.strip().str.upper()

# Remplacer
df["region"] = df["region"].replace({"DKR": "Dakar", "THS": "Thiès"})

# Regex
df["telephone"] = df["telephone"].str.replace(r"\s+", "", regex=True)
df["email"] = df["email"].str.lower()

Types

# Conversion
df["montant"] = df["montant"].astype(float)
df["date_vente"] = pd.to_datetime(df["date_vente"], errors="coerce")
df["code"] = df["code"].astype(str).str.zfill(6)  # pad avec zéros

errors="coerce" transforme les valeurs invalides en NaN au lieu de planter — précieux pour des données sales.


5. Agrégations et groupby

# Total par région
df.groupby("region")["montant"].sum()

# Plusieurs agrégations
df.groupby("region").agg(
    total=("montant", "sum"),
    moyenne=("montant", "mean"),
    nombre=("montant", "count"),
    max=("montant", "max"),
)

# Plusieurs colonnes de groupement
df.groupby(["region", "produit"])["montant"].sum()

# Avec tri
df.groupby("region")["montant"].sum().sort_values(ascending=False)

# Top 5 produits par CA
df.groupby("produit")["montant"].sum().nlargest(5)

# Agrégation sur date par mois
df.groupby(df["date_vente"].dt.to_period("M"))["montant"].sum()

groupby + agg couvre 80 % des besoins de reporting : sommes, moyennes, comptages, par dimension. C’est l’équivalent SQL GROUP BY mais beaucoup plus flexible.


6. Joindre plusieurs sources

Cas typique : un fichier de ventes avec un code client, un fichier clients avec les détails.

ventes = pd.read_csv("ventes.csv")
clients = pd.read_csv("clients.csv")

# Inner join (seulement les correspondances)
fusion = ventes.merge(clients, on="code_client", how="inner")

# Left join (toutes les ventes, infos clients si dispo)
fusion = ventes.merge(clients, on="code_client", how="left")

# Clés différentes des deux côtés
fusion = ventes.merge(
    clients,
    left_on="ref_client",
    right_on="id_client",
    how="left",
)

# Vérifier après merge
print(f"Lignes ventes: {len(ventes)}, après merge: {len(fusion)}")
# Si le total a explosé : problème de clé dupliquée

Vérifier l’intégrité de la jointure

# Quels codes clients sont dans ventes mais pas clients ?
manquants = set(ventes["code_client"]) - set(clients["code_client"])
print(f"Codes clients orphelins : {len(manquants)}")

Toujours vérifier le nombre de lignes avant et après merge — c’est l’erreur la plus courante en pandas (jointure qui multiplie les lignes silencieusement).


7. Pivot tables et tableaux croisés

L’équivalent du tableau croisé dynamique Excel.

# CA par produit (lignes) et par mois (colonnes)
pivot = df.pivot_table(
    values="montant",
    index="produit",
    columns=df["date_vente"].dt.to_period("M"),
    aggfunc="sum",
    fill_value=0,
    margins=True,        # ajoute les totaux ligne et colonne
    margins_name="Total",
)

# Plusieurs valeurs
pivot = df.pivot_table(
    values=["montant", "quantite"],
    index="region",
    columns="produit",
    aggfunc={"montant": "sum", "quantite": "sum"},
)

Le résultat est un DataFrame qui peut directement être exporté en Excel pour présentation.

Inverse : depivot avec melt

Quand on reçoit un tableau « wide » (mois en colonnes) et qu’on veut le format « long » (une ligne par observation) :

df_long = df.melt(
    id_vars=["produit"],
    value_vars=["jan", "fev", "mar"],
    var_name="mois",
    value_name="ventes",
)

8. Écrire un Excel formaté

# Simple
df.to_excel("rapport.xlsx", index=False)

# Plusieurs feuilles dans le même fichier
with pd.ExcelWriter("rapport.xlsx") as writer:
    df_ventes.to_excel(writer, sheet_name="Ventes", index=False)
    df_synthese.to_excel(writer, sheet_name="Synthèse", index=False)
    df_top.to_excel(writer, sheet_name="Top 10", index=False)

Mise en forme avec openpyxl

from openpyxl import load_workbook
from openpyxl.styles import Font, PatternFill, Alignment

# D'abord écrire avec pandas
df.to_excel("rapport.xlsx", index=False)

# Puis ouvrir et mettre en forme
wb = load_workbook("rapport.xlsx")
ws = wb.active

# En-tête en gras avec fond
header_font = Font(bold=True, color="FFFFFF")
header_fill = PatternFill("solid", fgColor="305496")
for cell in ws[1]:
    cell.font = header_font
    cell.fill = header_fill
    cell.alignment = Alignment(horizontal="center")

# Largeur auto des colonnes
for col in ws.columns:
    max_len = max(len(str(c.value)) for c in col if c.value)
    ws.column_dimensions[col[0].column_letter].width = max_len + 2

wb.save("rapport.xlsx")

Investir 10 minutes dans une fonction utilitaire de formatage que tous les scripts réutilisent fait la différence entre des rapports pro et des fichiers bruts.


9. Pièges classiques et solutions

SettingWithCopyWarning

Le warning le plus déroutant pour les débutants. Survient quand on modifie un sous-DataFrame sans utiliser .copy() ou .loc.

# Mauvais
sub = df[df["region"] == "Dakar"]
sub["bonus"] = 10  # warning

# Bon
sub = df[df["region"] == "Dakar"].copy()
sub["bonus"] = 10

Lecture de gros fichiers

Pour des CSV de plusieurs Go, la lecture brute échoue par manque de mémoire. Solutions : chunksize pour lire par paquets, usecols pour ne charger que les colonnes nécessaires, ou passer à polars ou duckdb.

# Lire par chunks de 100k lignes
total = 0
for chunk in pd.read_csv("gros.csv", chunksize=100_000):
    total += chunk["montant"].sum()

Dates en format inconnu

# Si le format n'est pas standard
df["date"] = pd.to_datetime(df["date"], format="%d/%m/%Y", errors="coerce")

Forcer le format évite les surprises (pandas peut interpréter 01/02/2026 comme 1er février OU 2 janvier selon la locale).

Apply lent

df.apply(fonction, axis=1) est tentant mais lent sur gros volumes. Préférer les opérations vectorisées quand possible.

# Lent
df["total"] = df.apply(lambda row: row["prix"] * row["quantite"], axis=1)

# Rapide (vectorisé)
df["total"] = df["prix"] * df["quantite"]

Voir aussi → Python pour scripting admin système pour orchestrer ces traitements avec des scripts robustes.


10. FAQ

pandas vs Excel : quand basculer ?

Quelques signaux : Excel rame, le fichier dépasse 100k lignes, le même traitement se répète plus d’une fois par mois, les formules deviennent illisibles, plusieurs personnes touchent au même fichier. Si l’un de ces points apparaît, pandas est rentable.

Faut-il vraiment apprendre pandas si on a SQL ?

Pour les opérations SQL pures (filtres, agrégations, jointures), SQL est souvent plus simple. Mais pandas excelle dans : la manipulation de fichiers (Excel, CSV), les transformations complexes en mémoire, l’intégration avec d’autres bibliothèques Python. Les deux sont complémentaires, pas concurrents.

Comment partager un script pandas avec un collègue qui n’a pas Python ?

Plusieurs options : déployer le script sur un serveur avec une planification automatique qui envoie le résultat par email, créer une mini-interface Streamlit (streamlit.io) consultable dans un navigateur, ou empaqueter en exécutable autonome avec PyInstaller. Streamlit est le compromis le plus accessible.

Quelle version de pandas en 2026 ?

pandas 2.x avec backend pyarrow est la version moderne recommandée. Plus rapide que pandas 1.x, gestion des types plus stricte. Migrer un projet pandas 1.x vers 2.x peut nécessiter quelques ajustements mineurs mais pas de refonte.

polars peut-il remplacer pandas ?

polars (pola.rs) est plus rapide, plus strict sur les types, et a une API plus moderne. Pour un nouveau projet sans contrainte d’écosystème, c’est un excellent choix. Pour un projet existant ou un projet qui s’intègre fortement avec d’autres bibliothèques (matplotlib, scikit-learn), pandas reste plus mature et mieux supporté.

Comment déboguer un script pandas qui produit des résultats faux ?

Réflexes : df.shape à chaque étape pour vérifier la taille, df.head() pour voir le contenu, df.dtypes pour les types, df.isna().sum() pour les valeurs manquantes. Faire chaque transformation séparément et vérifier les intermédiaires. Les notebooks Jupyter sont parfaits pour ce mode exploratoire.


Articles liés (cluster Python pour PME)


Article mis à jour le 25 avril 2026. Pour signaler une erreur ou suggérer une amélioration, écrivez-nous.

Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité