Cours Pandas Complet 🐼

Les 12 chapitres essentiels — DataFrame, nettoyage, agrégation, visualisation et analyse

12
Chapitres
100+
Exemples de code
Pandas 2+
Version
A1→C2
Niveaux

CHAPITRE 01

Introduction et installation

🐼 Premiers pas
# Installation
pip install pandas

# Import conventionnel
import pandas as pd
import numpy as np

# Lire un fichier CSV
df = pd.read_csv('data.csv')

# Aperçu rapide
df.head() # 5 premières lignes
df.tail(3) # 3 dernières lignes
df.shape # (lignes, colonnes)
df.dtypes # Types de chaque colonne
df.info() # Résumé complet (types, null, mémoire)
df.describe() # Statistiques (mean, std, min, max, quartiles)
df.columns # Noms des colonnes
df.nunique() # Valeurs uniques par colonne
df.sample(5) # 5 lignes aléatoires

Pandas est LA bibliothèque Python pour l'analyse de données. Créée par Wes McKinney en 2008, elle fournit les structures Series (1D) et DataFrame (2D, comme un tableur). Utilisée dans la data science, la finance, la recherche et le machine learning. Construite sur NumPy.

📁 Lire / écrire des fichiers
# CSV
df = pd.read_csv('data.csv', sep=';', encoding='utf-8')
df.to_csv('output.csv', index=False)

# Excel
df = pd.read_excel('data.xlsx', sheet_name='Feuille1')
df.to_excel('output.xlsx', index=False)

# JSON
df = pd.read_json('data.json')
df.to_json('output.json', orient='records')

# Parquet (rapide, compressé — recommandé pour gros fichiers)
df = pd.read_parquet('data.parquet')
df.to_parquet('output.parquet')

# SQL
import sqlite3
conn = sqlite3.connect('db.sqlite')
df = pd.read_sql('SELECT * FROM users', conn)

CHAPITRE 02

Series et DataFrame

📊 Créer des structures
# Series — 1 dimension (comme une colonne)
s = pd.Series([10, 20, 30], name='prix')
s = pd.Series()

# DataFrame — 2 dimensions (comme un tableur)
df = pd.DataFrame()

# Accéder à une colonne → retourne une Series
df['nom'] df.nom # Notation point (si pas d'espaces/caractères spéciaux)

# Accéder à plusieurs colonnes → retourne un DataFrame
df[['nom', 'salaire']]

# Ajouter une colonne
df['bonus'] = df['salaire'] * 0.1

# Supprimer une colonne
df = df.drop(columns=['bonus'])

CHAPITRE 03

Sélection et filtrage

🔍 loc, iloc et filtres booléens
# loc — par label (nom de ligne/colonne)
df.loc[0] # Ligne 0 (par label d'index)
df.loc[0, 'nom'] # Cellule (ligne 0, colonne 'nom')
df.loc[0:2, ['nom', 'age']] # Lignes 0-2, colonnes nom+age

# iloc — par position (index numérique)
df.iloc[0] # Première ligne
df.iloc[0:3, 0:2] # 3 premières lignes, 2 premières colonnes
df.iloc[1] # Dernière ligne

# Filtrage booléen — le plus utilisé
df[df['age'] > 25] # Age > 25
df[df['ville'] == 'Paris'] # Ville = Paris
df[df['ville'].isin(['Paris', 'Lyon'])] # Paris ou Lyon
df[df['nom'].str.contains('li')] # Nom contient « li »

# Conditions multiples (& = ET, | = OU, ~ = NOT)
df[(df['age'] > 25) & (df['salaire'] > 47000)] df[(df['ville'] == 'Paris') | (df['age'] < 27)]

# query() — syntaxe plus lisible
df.query('age > 25 and salaire > 47000')

Toujours des parenthèses autour de chaque condition avec & et |. Sans parenthèses, Python évalue dans le mauvais ordre : df[df['age'] > 25 & df['salaire'] > 47000] → erreur. Correct : df[(df['age'] > 25) & (df['salaire'] > 47000)].

CHAPITRE 04

Nettoyage des données

🧹 Valeurs manquantes (NaN)
# Détecter les NaN
df.isna().sum() # Nombre de NaN par colonne
df.isna().sum().sum() # Total de NaN

# Supprimer les lignes avec NaN
df.dropna() # Toute ligne avec au moins 1 NaN
df.dropna(subset=['email']) # Seulement si NaN dans 'email'

# Remplir les NaN
df['age'].fillna(df['age'].median()) # Par la médiane
df['ville'].fillna('Inconnu') # Par une valeur fixe
df['prix'].fillna(method='ffill') # Forward fill (dernière valeur connue)

# Doublons
df.duplicated().sum() # Nombre de doublons
df.drop_duplicates() # Supprimer les doublons
df.drop_duplicates(subset=['email'], keep='last')

# Types
df['age'] = df['age'].astype(int)
df['prix'] = pd.to_numeric(df['prix'], errors='coerce') # Invalides → NaN

# Texte
df['nom'] = df['nom'].str.strip() # Espaces en trop
df['nom'] = df['nom'].str.lower() # Minuscules
df['email'] = df['email'].str.replace(' ',  ») # Supprimer espaces

CHAPITRE 05

Transformation

🔄 apply, map, assign
# apply — appliquer une fonction sur chaque élément/ligne
df['salaire_net'] = df['salaire'].apply(lambda x: x * 0.75)

# apply sur une ligne entière (axis=1)
df['label'] = df.apply(
lambda row: f » () », axis=1
)

# map — remplacer des valeurs
df['niveau'] = df['age'].map()

# assign — créer des colonnes de manière chaînable
df = (
df
.assign(bonus=lambda x: x['salaire'] * 0.1)
.assign(total=lambda x: x['salaire'] + x['bonus'])
)

# rename — renommer des colonnes
df = df.rename(columns=)

# sort
df.sort_values('salaire', ascending=False)
df.sort_values(['ville', 'salaire'], ascending=[True, False])

# Catégoriser avec cut / qcut
df['tranche_age'] = pd.cut(df['age'], bins=[0, 25, 35, 100],
labels=['jeune', 'moyen', 'senior'])
df['quartile'] = pd.qcut(df['salaire'], q=4, labels=['Q1','Q2','Q3','Q4'])

CHAPITRE 06

GroupBy et agrégation

📊 GroupBy
# Grouper et agréger
df.groupby('ville')['salaire'].mean() # Salaire moyen par ville
df.groupby('ville')['salaire'].sum() # Somme par ville
df.groupby('ville').size() # Nombre par ville

# Plusieurs agrégations avec agg()
df.groupby('ville')['salaire'].agg(['mean', 'median', 'min', 'max', 'count'])

# Agrégations différentes par colonne
df.groupby('ville').agg()

# Grouper par plusieurs colonnes
df.groupby(['ville', 'tranche_age'])['salaire'].mean()

# value_counts — compteur rapide
df['ville'].value_counts() # Nombre par valeur
df['ville'].value_counts(normalize=True) # Pourcentages

GroupBy suit le pattern split-apply-combine : séparer les données en groupes (split), appliquer une fonction à chaque groupe (apply), recombiner les résultats (combine). C'est l'équivalent de GROUP BY en SQL.

CHAPITRE 07

Dates et séries temporelles

📅 Datetime
# Convertir en datetime
df['date'] = pd.to_datetime(df['date'])
df['date'] = pd.to_datetime(df['date'], format='%d/%m/%Y')

# Extraire des composants
df['date'].dt.year # Année
df['date'].dt.month # Mois
df['date'].dt.day # Jour
df['date'].dt.day_name() # « Monday », « Tuesday »…
df['date'].dt.quarter # Trimestre

# Filtrer par date
df[df['date'] > '2024-01-01'] df[df['date'].between('2024-01-01', '2024-12-31')]

# Resample — agréger par période (nécessite index datetime)
df.set_index('date').resample('M')['ventes'].sum() # Par mois
df.set_index('date').resample('W')['ventes'].mean() # Par semaine
df.set_index('date').resample('Q')['ventes'].sum() # Par trimestre

# Rolling — moyenne mobile
df['ma_7j'] = df['ventes'].rolling(window=7).mean()

CHAPITRE 08

Jointures et merge

🔗 merge, concat, join
# merge — comme un JOIN SQL
users = pd.DataFrame()
orders = pd.DataFrame()

# Inner join (intersection)
pd.merge(users, orders, on='user_id')

# Left join (garder tous les users)
pd.merge(users, orders, on='user_id', how='left')

# Right join / Outer join
pd.merge(users, orders, on='user_id', how='right')
pd.merge(users, orders, on='user_id', how='outer')

# Colonnes différentes
pd.merge(df1, df2, left_on='id_client', right_on='customer_id')

# concat — empiler des DataFrames
pd.concat([df1, df2]) # Vertical (lignes)
pd.concat([df1, df2], ignore_index=True) # Reset index
pd.concat([df1, df2], axis=1) # Horizontal (colonnes)

TypeSQLPandas
IntersectionINNER JOINhow='inner' (défaut)
Garder la gaucheLEFT JOINhow='left'
Garder la droiteRIGHT JOINhow='right'
Tout garderFULL OUTER JOINhow='outer'

CHAPITRE 09

Visualisation

📈 Pandas + Matplotlib
import matplotlib.pyplot as plt

# Pandas intègre matplotlib directement
df['salaire'].plot(kind='hist', bins=20, title='Distribution salaires')
plt.show()

# Types de graphiques
df.plot(kind='line', x='date', y='ventes') # Ligne
df.plot(kind='bar', x='ville', y='salaire') # Barres
df.plot(kind='scatter', x='age', y='salaire') # Nuage de points
df.plot(kind='box') # Box plot
df['ville'].value_counts().plot(kind='pie') # Camembert

# GroupBy + plot
df.groupby('ville')['salaire'].mean().plot(kind='barh')

Pour des visualisations plus poussées, utilisez Seaborn (basé sur matplotlib, plus beau par défaut) ou Plotly (graphiques interactifs). Pandas .plot() est parfait pour l'exploration rapide.

CHAPITRE 10

Pivot et reshape

🔄 Pivot, melt, crosstab
# pivot_table — tableau croisé dynamique (comme Excel)
pd.pivot_table(
df,
values='salaire',
index='ville',
columns='tranche_age',
aggfunc='mean',
fill_value=0,
)

# crosstab — tableau de fréquences
pd.crosstab(df['ville'], df['tranche_age'])

# melt — wide → long (inverse de pivot)
# Avant: nom | jan | fev | mar
# Après: nom | mois | ventes
df_long = pd.melt(
df,
id_vars=['nom'],
value_vars=['jan', 'fev', 'mar'],
var_name='mois',
value_name='ventes',
)

# stack / unstack
df.stack() # Colonnes → lignes
df.unstack() # Lignes → colonnes

CHAPITRE 11

Performance

⚡ Optimiser Pandas
# 1. Utiliser les bons types (économie mémoire)
df['age'] = df['age'].astype('int8') # int64 → int8 (si < 128)
df['prix'] = df['prix'].astype('float32') # float64 → float32
df['ville'] = df['ville'].astype('category') # String → category

# 2. Lire seulement les colonnes nécessaires
df = pd.read_csv('big.csv', usecols=['nom', 'salaire'])

# 3. Parquet au lieu de CSV (5-10x plus rapide, compressé)
df.to_parquet('data.parquet')
df = pd.read_parquet('data.parquet')

# 4. Vectoriser au lieu de boucles
# ❌ Lent
for i in range(len(df)):
df.loc[i, 'bonus'] = df.loc[i, 'salaire'] * 0.1

# ✅ Rapide (vectorisé)
df['bonus'] = df['salaire'] * 0.1

# 5. Lire par chunks (très gros fichiers)
for chunk in pd.read_csv('huge.csv', chunksize=10000):
process(chunk)

Pour les très gros datasets (> 1 Go), considérez Polars (syntaxe similaire à Pandas, 10-100x plus rapide, écrit en Rust) ou DuckDB (SQL analytique en mémoire). Pandas reste le standard pour les datasets qui tiennent en mémoire.

CHAPITRE 12

Bonnes pratiques

🧩 Écosystème data Python
OutilUsage
PandasManipulation de données tabulaires
NumPyCalcul numérique (arrays, algèbre linéaire)
MatplotlibVisualisation de base
SeabornVisualisation statistique (beau par défaut)
PlotlyGraphiques interactifs
Scikit-learnMachine learning
PolarsAlternative rapide à Pandas (Rust)
Jupyter NotebookExploration interactive
✅ Bonnes pratiques

✅ À FAIRE
• Toujours df.info() + df.describe() d'abord
• Opérations vectorisées (pas de boucles)
query() pour les filtres complexes
category dtype pour les colonnes répétitives
• Parquet pour le stockage
.copy() pour éviter les SettingWithCopy
• Method chaining (assign, query, sort)
usecols pour les gros CSV
• Jupyter pour l'exploration

❌ À ÉVITER
• Boucles for sur les lignes d'un DataFrame
iterrows() (très lent)
• Modifier un DataFrame pendant une itération
• Ignorer les types (tout en object/float64)
• CSV pour les gros fichiers (utiliser Parquet)
df['col'] = … sur une copie (SettingWithCopy)
inplace=True (chaîner est mieux)
• Ignorer les NaN avant l'analyse
• Tout charger en mémoire (utiliser chunks)

🧠 Quiz
Quelle est la différence entre loc et iloc ?
loc sélectionne par label (nom de l'index/colonne) et inclut la borne de fin. iloc sélectionne par position entière (index numérique) et exclut la borne de fin (comme le slicing Python). df.loc[0:2] retourne 3 lignes (0,1,2). df.iloc[0:2] retourne 2 lignes (0,1).
Pandas vs Polars — quand choisir quoi ?
Pandas = écosystème mature, intégration scikit-learn/matplotlib, plus de tutoriels, suffisant pour < 1 Go. Polars = datasets volumineux (> 1 Go), parallélisme automatique, lazy evaluation, 10-100x plus rapide. Si vous débutez ou faites du ML, commencez par Pandas. Si la performance est critique, passez à Polars.

Cours Pandas Complet — DataFrame, nettoyage, agrégation et visualisation

Référence : pandas.pydata.org | Polars