# Fichier: python_cheats/cheatsheets/matplotlib.txt
# Cheatsheet Matplotlib Python - Guide Complet



[OK] INSTALLATION & IMPORT


# Installation
pip install matplotlib

# Imports de base
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

# Backend (pour affichage)
import matplotlib
matplotlib.use('TkAgg')  # GUI backend
matplotlib.use('Agg')    # Non-interactive (serveur)

# Style
plt.style.use('default')
plt.style.use('seaborn-v0_8')
plt.style.use('ggplot')
plt.style.use('dark_background')

# Lister styles disponibles
print(plt.style.available)


[OK] FIGURE & AXES - CRÉATION


# Méthode 1: pyplot (simple, MATLAB-style)
plt.figure()
plt.plot([1, 2, 3], [1, 4, 9])
plt.show()

# Méthode 2: Object-oriented (recommandé)
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
plt.show()

# Figure avec taille
fig, ax = plt.subplots(figsize=(10, 6))  # largeur, hauteur en pouces

# DPI (résolution)
fig, ax = plt.subplots(figsize=(10, 6), dpi=100)

# Plusieurs subplots
fig, axes = plt.subplots(2, 2)  # 2x2 grille
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

# Subplots avec axes partagés
fig, axes = plt.subplots(2, 1, sharex=True)
fig, axes = plt.subplots(1, 2, sharey=True)

# Subplots avec espacement
fig, axes = plt.subplots(2, 2, constrained_layout=True)
fig, axes = plt.subplots(2, 2)
plt.tight_layout()

# GridSpec (layout avancé)
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(10, 8))
gs = GridSpec(3, 3, figure=fig)
ax1 = fig.add_subplot(gs[0, :])    # Première ligne complète
ax2 = fig.add_subplot(gs[1, :-1])  # Deuxième ligne, 2 colonnes
ax3 = fig.add_subplot(gs[1:, -1])  # Deux dernières lignes, dernière colonne
ax4 = fig.add_subplot(gs[-1, 0])
ax5 = fig.add_subplot(gs[-1, 1])

# Subplots irréguliers
fig = plt.figure(figsize=(10, 6))
ax1 = plt.subplot(2, 2, 1)
ax2 = plt.subplot(2, 2, 2)
ax3 = plt.subplot(2, 1, 2)  # Occupe toute la largeur en bas


[OK] TYPES DE GRAPHIQUES - LINE PLOTS


# Plot basique
x = [1, 2, 3, 4, 5]
y = [1, 4, 9, 16, 25]
plt.plot(x, y)

# Plusieurs lignes
plt.plot(x, y1, x, y2, x, y3)

# Style de ligne
plt.plot(x, y, linestyle='-')   # Solide
plt.plot(x, y, linestyle='--')  # Tirets
plt.plot(x, y, linestyle='-.')  # Tiret-point
plt.plot(x, y, linestyle=':')   # Points
plt.plot(x, y, ls='--')         # Raccourci

# Épaisseur et couleur
plt.plot(x, y, linewidth=2, color='red')
plt.plot(x, y, lw=3, c='#FF5733')
plt.plot(x, y, color='blue', alpha=0.5)

# Marqueurs
plt.plot(x, y, marker='o')  # Cercles
plt.plot(x, y, marker='s')  # Carrés
plt.plot(x, y, marker='^')  # Triangles
plt.plot(x, y, marker='*')  # Étoiles
plt.plot(x, y, marker='D')  # Diamants
plt.plot(x, y, marker='x')  # Croix

# Style combiné (raccourci)
plt.plot(x, y, 'ro-')   # Rouge, cercles, ligne solide
plt.plot(x, y, 'b^--')  # Bleu, triangles, tirets
plt.plot(x, y, 'g*:')   # Vert, étoiles, pointillés

# Taille des marqueurs
plt.plot(x, y, marker='o', markersize=10, markerfacecolor='red', markeredgecolor='black')

# Label pour légende
plt.plot(x, y, label='Données série 1')
plt.plot(x, y2, label='Données série 2')
plt.legend()


[OK] SCATTER PLOTS


# Scatter basique
x = np.random.rand(50)
y = np.random.rand(50)
plt.scatter(x, y)

# Taille variable
sizes = np.random.rand(50) * 1000
plt.scatter(x, y, s=sizes)

# Couleur variable
colors = np.random.rand(50)
plt.scatter(x, y, c=colors, cmap='viridis')

# Avec colorbar
plt.scatter(x, y, c=colors, cmap='viridis')
plt.colorbar(label='Valeur')

# Transparence
plt.scatter(x, y, alpha=0.5)

# Forme des marqueurs
plt.scatter(x, y, marker='^', s=100, edgecolors='black', linewidths=2)

# Scatter 3D
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x, y, z, c=colors, cmap='viridis')


[OK] BAR CHARTS


# Barres verticales
categories = ['A', 'B', 'C', 'D']
values = [23, 45, 56, 78]
plt.bar(categories, values)

# Barres horizontales
plt.barh(categories, values)

# Largeur personnalisée
plt.bar(categories, values, width=0.5)

# Couleurs
plt.bar(categories, values, color=['red', 'blue', 'green', 'orange'])

# Bordures
plt.bar(categories, values, edgecolor='black', linewidth=2)

# Barres groupées
x = np.arange(len(categories))
width = 0.35
plt.bar(x - width/2, values1, width, label='Groupe 1')
plt.bar(x + width/2, values2, width, label='Groupe 2')
plt.xticks(x, categories)
plt.legend()

# Barres empilées
plt.bar(categories, values1, label='Série 1')
plt.bar(categories, values2, bottom=values1, label='Série 2')
plt.legend()

# Barres avec erreur
errors = [2, 3, 4, 1]
plt.bar(categories, values, yerr=errors, capsize=5)


[OK] HISTOGRAMMES


# Histogramme basique
data = np.random.randn(1000)
plt.hist(data)

# Nombre de bins
plt.hist(data, bins=50)

# Bins spécifiques
plt.hist(data, bins=[0, 1, 2, 3, 4, 5])

# Densité de probabilité
plt.hist(data, bins=30, density=True)

# Transparence et couleur
plt.hist(data, bins=30, alpha=0.7, color='skyblue', edgecolor='black')

# Histogramme cumulatif
plt.hist(data, bins=30, cumulative=True)

# Orientation horizontale
plt.hist(data, bins=30, orientation='horizontal')

# Plusieurs distributions
data1 = np.random.randn(1000)
data2 = np.random.randn(1000) + 2
plt.hist([data1, data2], bins=30, alpha=0.5, label=['Dist 1', 'Dist 2'])
plt.legend()

# Histogramme 2D
x = np.random.randn(1000)
y = np.random.randn(1000)
plt.hist2d(x, y, bins=50, cmap='Blues')
plt.colorbar()


[OK] PIE CHARTS


# Pie basique
sizes = [25, 35, 20, 20]
labels = ['A', 'B', 'C', 'D']
plt.pie(sizes, labels=labels)

# Avec pourcentages
plt.pie(sizes, labels=labels, autopct='%1.1f%%')

# Exploser une section
explode = (0, 0.1, 0, 0)  # Exploser la 2ème tranche
plt.pie(sizes, labels=labels, explode=explode, autopct='%1.1f%%')

# Couleurs personnalisées
colors = ['gold', 'yellowgreen', 'lightcoral', 'lightskyblue']
plt.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%')

# Angle de départ et ombre
plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90, shadow=True)

# Circle dans le centre (donut)
plt.pie(sizes, labels=labels, autopct='%1.1f%%')
centre_circle = plt.Circle((0, 0), 0.70, fc='white')
fig = plt.gcf()
fig.gca().add_artist(centre_circle)

# Aspect ratio égal (cercle parfait)
plt.pie(sizes, labels=labels)
plt.axis('equal')


[OK] BOX PLOTS


# Box plot basique
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
plt.boxplot(data)

# Horizontal
plt.boxplot(data, vert=False)

# Labels
plt.boxplot(data, labels=['Groupe 1', 'Groupe 2', 'Groupe 3'])

# Notch (intervalle de confiance)
plt.boxplot(data, notch=True)

# Afficher moyennes
plt.boxplot(data, showmeans=True)

# Style
plt.boxplot(data, patch_artist=True, boxprops=dict(facecolor='lightblue'))

# Outliers personnalisés
plt.boxplot(data, flierprops=dict(marker='o', markerfacecolor='red', markersize=8))


[OK] VIOLIN PLOTS


# Violin plot (nécessite positions)
from matplotlib import pyplot as plt
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
positions = [1, 2, 3]
plt.violinplot(data, positions=positions)

# Afficher medians, mins, maxs
parts = plt.violinplot(data, showmedians=True, showextrema=True)

# Couleurs
parts = plt.violinplot(data)
for pc in parts['bodies']:
    pc.set_facecolor('lightblue')
    pc.set_alpha(0.7)


[OK] HEATMAPS


# Heatmap basique
data = np.random.rand(10, 10)
plt.imshow(data, cmap='viridis')
plt.colorbar()

# Interpolation
plt.imshow(data, cmap='hot', interpolation='nearest')

# Limites de couleur
plt.imshow(data, cmap='coolwarm', vmin=0, vmax=1)

# Avec annotations
fig, ax = plt.subplots()
im = ax.imshow(data, cmap='YlOrRd')
# Annotations
for i in range(data.shape[0]):
    for j in range(data.shape[1]):
        text = ax.text(j, i, f'{data[i, j]:.2f}',
                      ha="center", va="center", color="black")
plt.colorbar(im)

# Matrice de corrélation
import pandas as pd
df = pd.DataFrame(np.random.randn(100, 4), columns=['A', 'B', 'C', 'D'])
corr = df.corr()
plt.imshow(corr, cmap='coolwarm', vmin=-1, vmax=1)
plt.colorbar()
plt.xticks(range(len(corr.columns)), corr.columns)
plt.yticks(range(len(corr.columns)), corr.columns)


[OK] CONTOUR PLOTS


# Données 2D
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))

# Contour lines
plt.contour(X, Y, Z)

# Contour rempli
plt.contourf(X, Y, Z, cmap='viridis')
plt.colorbar()

# Nombre de niveaux
plt.contourf(X, Y, Z, levels=20, cmap='viridis')

# Niveaux spécifiques
plt.contourf(X, Y, Z, levels=[-1, -0.5, 0, 0.5, 1], cmap='RdBu')

# Contour + labels
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=True, fontsize=10)


[OK] QUIVER PLOTS (VECTEURS)


# Champ vectoriel
x = np.arange(0, 2*np.pi, 0.2)
y = np.arange(0, 2*np.pi, 0.2)
X, Y = np.meshgrid(x, y)
U = np.cos(X)
V = np.sin(Y)

plt.quiver(X, Y, U, V)

# Couleur par magnitude
M = np.sqrt(U**2 + V**2)
plt.quiver(X, Y, U, V, M, cmap='viridis')
plt.colorbar()

# Stream plot (lignes de flux)
plt.streamplot(X, Y, U, V, color=M, cmap='viridis')


[OK] FILL PLOTS


# Fill between
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
plt.fill_between(x, y1, y2, alpha=0.3)
plt.plot(x, y1, x, y2)

# Fill above/below ligne
plt.fill_between(x, 0, y1, where=(y1 > 0), alpha=0.3, color='green')
plt.fill_between(x, 0, y1, where=(y1 < 0), alpha=0.3, color='red')

# Fill polygon
vertices = [(0, 0), (1, 1), (2, 0), (1, -1)]
from matplotlib.patches import Polygon
poly = Polygon(vertices, facecolor='lightblue', edgecolor='black')
ax.add_patch(poly)


[OK] ERRORBARS


# Errorbars verticales
x = np.arange(0, 4, 0.2)
y = np.exp(-x)
errors = 0.1 * np.abs(y)
plt.errorbar(x, y, yerr=errors)

# Errorbars horizontales
plt.errorbar(x, y, xerr=errors)

# Errorbars asymétriques
lower_errors = errors * 0.5
upper_errors = errors * 1.5
plt.errorbar(x, y, yerr=[lower_errors, upper_errors])

# Style
plt.errorbar(x, y, yerr=errors, fmt='o-', capsize=5, capthick=2,
             ecolor='red', elinewidth=2, markerfacecolor='blue')


[OK] AXES - CONFIGURATION


# Limites
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)
ax.set_xlim([0, 10])  # Alternative

# Auto limites avec marge
ax.margins(0.1)  # 10% de marge
ax.margins(x=0.1, y=0.2)

# Échelle logarithmique
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_xscale('linear')  # Retour normal

# Échelle symlog (log avec zéro)
ax.set_yscale('symlog')

# Inverser axes
ax.invert_xaxis()
ax.invert_yaxis()

# Aspect ratio
ax.set_aspect('equal')
ax.set_aspect('auto')
ax.set_aspect(2)  # 2x plus large que haut

# Ticks
ax.set_xticks([0, 2, 4, 6, 8, 10])
ax.set_yticks(np.arange(0, 1.1, 0.1))

# Tick labels
ax.set_xticklabels(['A', 'B', 'C', 'D', 'E', 'F'])
ax.set_xticklabels(labels, rotation=45, ha='right')

# Minor ticks
ax.minorticks_on()
ax.tick_params(which='minor', length=4, color='gray')

# Formatter les ticks
from matplotlib.ticker import FuncFormatter
def currency(x, pos):
    return f'${x:.2f}'
ax.yaxis.set_major_formatter(FuncFormatter(currency))

# Formatter dates
import matplotlib.dates as mdates
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax.xaxis.set_major_locator(mdates.MonthLocator())

# Grid
ax.grid(True)
ax.grid(True, which='both', linestyle='--', alpha=0.5)
ax.grid(axis='x')  # Seulement vertical

# Spine (bordures)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_linewidth(2)
ax.spines['bottom'].set_position(('data', 0))


[OK] LABELS & TITRES


# Titre
ax.set_title('Mon Graphique')
ax.set_title('Titre', fontsize=16, fontweight='bold')
ax.set_title('Titre', loc='left')  # ou 'right', 'center'

# Labels axes
ax.set_xlabel('Axe X')
ax.set_ylabel('Axe Y')
ax.set_xlabel('X', fontsize=12, color='blue')

# Titre de la figure
fig.suptitle('Titre Principal', fontsize=20)

# Texte libre
ax.text(5, 0.5, 'Annotation', fontsize=12)
ax.text(0.5, 0.5, 'Centre', transform=ax.transAxes,
        ha='center', va='center')

# Annotation avec flèche
ax.annotate('Point Important', xy=(2, 1), xytext=(3, 1.5),
            arrowprops=dict(arrowstyle='->', color='red'))

# LaTeX dans texte
ax.set_title(r'$\alpha > \beta$')
ax.set_xlabel(r'$\sum_{i=1}^{n} x_i$')


[OK] LÉGENDES


# Légende basique
plt.plot(x, y, label='Série 1')
plt.plot(x, y2, label='Série 2')
plt.legend()

# Position
plt.legend(loc='upper right')
# Options: 'upper left', 'upper right', 'lower left', 'lower right',
#          'upper center', 'lower center', 'center left', 'center right',
#          'center', 'best'

# Position custom (coordonnées)
plt.legend(loc=(0.5, 0.5))

# Hors du plot
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# Colonnes
plt.legend(ncol=2)

# Sans cadre
plt.legend(frameon=False)

# Titre de légende
plt.legend(title='Légende')

# Font size
plt.legend(fontsize=12)

# Handles et labels custom
handles = [plt.Line2D([0], [0], color='red', lw=2),
           plt.Line2D([0], [0], color='blue', lw=2)]
labels = ['Rouge', 'Bleu']
plt.legend(handles, labels)


[OK] COULEURS & COLORMAPS


# Couleurs nommées
colors = ['red', 'blue', 'green', 'orange', 'purple', 'brown', 'pink', 'gray', 'olive', 'cyan']

# Couleurs hex
plt.plot(x, y, color='#FF5733')

# RGB (0-1)
plt.plot(x, y, color=(0.5, 0.2, 0.8))

# Colormaps
cmaps = ['viridis', 'plasma', 'inferno', 'magma', 'cividis',
         'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
         'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
         'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn',
         'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone',
         'pink', 'spring', 'summer', 'autumn', 'winter', 'cool',
         'Wistia', 'hot', 'afmhot', 'gist_heat', 'copper',
         'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu', 'RdYlBu',
         'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']

# Utiliser colormap
plt.scatter(x, y, c=values, cmap='viridis')

# Colormap inversée
plt.scatter(x, y, c=values, cmap='viridis_r')

# Créer colormap custom
from matplotlib.colors import LinearSegmentedColormap
colors = ['red', 'yellow', 'green']
n_bins = 100
cmap = LinearSegmentedColormap.from_list('custom', colors, N=n_bins)

# Normalisation
from matplotlib.colors import Normalize
norm = Normalize(vmin=0, vmax=10)
plt.scatter(x, y, c=values, cmap='viridis', norm=norm)


[OK] STYLES & THÈMES


# Utiliser style prédéfini
plt.style.use('ggplot')
plt.style.use('seaborn-v0_8')
plt.style.use('bmh')
plt.style.use('fivethirtyeight')

# Lister styles
print(plt.style.available)

# Style temporaire
with plt.style.context('dark_background'):
    plt.plot(x, y)

# Configuration globale
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['font.size'] = 12
plt.rcParams['lines.linewidth'] = 2
plt.rcParams['axes.grid'] = True

# Reset configuration
plt.rcdefaults()

# Style custom (fichier .mplstyle)
"""
# mystyle.mplstyle
figure.figsize: 10, 6
font.size: 12
axes.labelsize: 14
axes.titlesize: 16
xtick.labelsize: 10
ytick.labelsize: 10
legend.fontsize: 10
lines.linewidth: 2
lines.markersize: 8
"""
# plt.style.use('path/to/mystyle.mplstyle')


[OK] SAUVEGARDER FIGURES


# Sauvegarder basique
plt.savefig('figure.png')

# Format spécifique
plt.savefig('figure.png')
plt.savefig('figure.pdf')
plt.savefig('figure.svg')
plt.savefig('figure.jpg')
plt.savefig('figure.eps')

# DPI (résolution)
plt.savefig('figure.png', dpi=300)

# Transparent background
plt.savefig('figure.png', transparent=True)

# Tight bbox (sans marges)
plt.savefig('figure.png', bbox_inches='tight')

# Padding custom
plt.savefig('figure.png', bbox_inches='tight', pad_inches=0.1)

# Qualité (pour JPEG)
plt.savefig('figure.jpg', quality=95)

# Sauvegarder dans buffer
from io import BytesIO
buf = BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)


[OK] ANIMATIONS


# Animation basique
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
xdata, ydata = [], []
line, = ax.plot([], [], 'r-')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return line,

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    line.set_data(xdata, ydata)
    return line,

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True, interval=20)

# Sauvegarder animation
ani.save('animation.gif', writer='pillow', fps=30)
ani.save('animation.mp4', writer='ffmpeg', fps=30)

# Animation 3D
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

def update(frame):
    ax.clear()
    x = np.linspace(-5, 5, 100)
    y = np.linspace(-5, 5, 100)
    X, Y = np.meshgrid(x, y)
    Z = np.sin(np.sqrt(X**2 + Y**2) + frame/10)
    ax.plot_surface(X, Y, Z, cmap='viridis')
    
ani = FuncAnimation(fig, update, frames=100, interval=50)


[OK] PLOTS 3D


# Setup 3D
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

# Line plot 3D
theta = np.linspace(-4*np.pi, 4*np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
ax.plot(x, y, z)

# Scatter 3D
ax.scatter(x, y, z, c=z, cmap='viridis')

# Surface plot
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
Z = np.sin(np.sqrt(X**2 + Y**2))
surf = ax.plot_surface(X, Y, Z, cmap='viridis', linewidth=0)

# Wireframe
ax.plot_wireframe(X, Y, Z, color='black')

# Contour 3D
ax.contour3D(X, Y, Z, 50, cmap='viridis')

# Bar3D
x = np.arange(5)
y = np.arange(5)
x, y = np.meshgrid(x, y)
x = x.flatten()
y = y.flatten()
z = np.zeros_like(x)
dx = dy = 0.5
dz = np.random.rand(len(x))
ax.bar3d(x, y, z, dx, dy, dz)

# Labels 3D
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')

# Angle de vue
ax.view_init(elev=20, azim=30)


[OK] SUBPLOTS AVANCÉS


# Subplots avec tailles différentes
fig = plt.figure(figsize=(12, 8))
gs = fig.add_gridspec(3, 3, hspace=0.3, wspace=0.3)
ax1 = fig.add_subplot(gs[0, :])     # Top row
ax2 = fig.add_subplot(gs[1, :-1])   # Middle left
ax3 = fig.add_subplot(gs[1:, -1])   # Right column
ax4 = fig.add_subplot(gs[-1, 0])    # Bottom left
ax5 = fig.add_subplot(gs[-1, -2])   # Bottom middle

# Subplots imbriqués
fig, axs = plt.subplots(2, 2, figsize=(10, 8))
inner_gs = axs[0, 0].inset_axes([0.1, 0.1, 0.4, 0.4])

# Axes jumeaux (deux y-axes)
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(x, y1, 'b-')
ax2.plot(x, y2, 'r-')
ax1.set_ylabel('Y1', color='b')
ax2.set_ylabel('Y2', color='r')

# Axes secondaires (deux x-axes)
ax2 = ax1.twiny()
ax1.plot(x, y)
ax2.plot(x2, y)


[OK] INTERACTIVITÉ


# Mode interactif
plt.ion()  # Activé
plt.ioff()  # Désactivé

# Attendre clic
plt.ginput(n=1)  # Attend 1 clic
points = plt.ginput(n=3, timeout=30)  # 3 clics, 30s timeout

# Event handling
def on_click(event):
    if event.inaxes:
        print(f'Clic à x={event.xdata}, y={event.ydata}')

fig, ax = plt.subplots()
fig.canvas.mpl_connect('button_press_event', on_click)

# Slider interactif
from matplotlib.widgets import Slider

fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
line, = ax.plot(x, np.sin(x))

ax_slider = plt.axes([0.2, 0.1, 0.6, 0.03])
slider = Slider(ax_slider, 'Fréquence', 0.1, 10.0, valinit=1)

def update(val):
    freq = slider.val
    line.set_ydata(np.sin(freq * x))
    fig.canvas.draw_idle()

slider.on_changed(update)

# Bouton
from matplotlib.widgets import Button

ax_button = plt.axes([0.8, 0.01, 0.1, 0.05])
button = Button(ax_button, 'Reset')

def reset(event):
    slider.reset()

button.on_clicked(reset)

# CheckButtons
from matplotlib.widgets import CheckButtons

rax = plt.axes([0.01, 0.5, 0.15, 0.15])
labels = ['Line 1', 'Line 2', 'Line 3']
visibility = [True, True, True]
check = CheckButtons(rax, labels, visibility)

def toggle_lines(label):
    index = labels.index(label)
    lines[index].set_visible(not lines[index].get_visible())
    plt.draw()

check.on_clicked(toggle_lines)

# RadioButtons
from matplotlib.widgets import RadioButtons

rax = plt.axes([0.01, 0.5, 0.15, 0.15])
radio = RadioButtons(rax, ('red', 'blue', 'green'))

def color_change(label):
    line.set_color(label)
    plt.draw()

radio.on_clicked(color_change)


[OK] ANNOTATIONS AVANCÉES


# Annotation basique avec flèche
ax.annotate('Point Max', xy=(x_max, y_max), xytext=(x_max+1, y_max+1),
            arrowprops=dict(arrowstyle='->', color='red', lw=2))

# Styles de flèches
arrowprops = dict(arrowstyle='->')     # Simple
arrowprops = dict(arrowstyle='-|>')    # Avec barre
arrowprops = dict(arrowstyle='-[')     # Avec crochet
arrowprops = dict(arrowstyle='fancy')  # Fantaisie
arrowprops = dict(arrowstyle='wedge')  # Coin

# Annotation avec bbox
ax.annotate('Important', xy=(5, 5), xytext=(7, 7),
            bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5),
            arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.3'))

# Box styles
bbox = dict(boxstyle='round')       # Arrondi
bbox = dict(boxstyle='square')      # Carré
bbox = dict(boxstyle='circle')      # Cercle
bbox = dict(boxstyle='roundtooth')  # Bulle de dialogue
bbox = dict(boxstyle='sawtooth')    # Dentelé

# Connection styles
connectionstyle='arc3,rad=0.3'   # Arc
connectionstyle='angle3'         # Angle
connectionstyle='bar'            # Barre

# Texte avec cadre
from matplotlib.patches import FancyBboxPatch
text = ax.text(0.5, 0.5, 'Texte', transform=ax.transAxes)
bbox = FancyBboxPatch((0.45, 0.45), 0.1, 0.1, 
                      transform=ax.transAxes,
                      boxstyle='round,pad=0.01',
                      facecolor='yellow', edgecolor='black')
ax.add_patch(bbox)


[OK] FORMES GÉOMÉTRIQUES


from matplotlib.patches import Circle, Rectangle, Polygon, Ellipse, Wedge, Arc

# Cercle
circle = Circle((0.5, 0.5), 0.2, color='blue', alpha=0.5)
ax.add_patch(circle)

# Rectangle
rect = Rectangle((0.1, 0.1), 0.3, 0.2, color='red', alpha=0.5)
ax.add_patch(rect)

# Ellipse
ellipse = Ellipse((0.5, 0.5), 0.4, 0.2, angle=30, color='green', alpha=0.5)
ax.add_patch(ellipse)

# Polygone
polygon = Polygon([(0, 0), (0.5, 0.5), (1, 0), (0.5, -0.5)],
                  facecolor='yellow', edgecolor='black', linewidth=2)
ax.add_patch(polygon)

# Wedge (portion de cercle)
wedge = Wedge((0.5, 0.5), 0.3, 30, 120, color='orange', alpha=0.7)
ax.add_patch(wedge)

# Arc
arc = Arc((0.5, 0.5), 0.4, 0.4, angle=0, theta1=0, theta2=180,
          color='purple', linewidth=3)
ax.add_patch(arc)

# Arrow (flèche)
from matplotlib.patches import FancyArrow
arrow = FancyArrow(0.1, 0.1, 0.3, 0.3, width=0.05, 
                   head_width=0.1, head_length=0.1, color='red')
ax.add_patch(arrow)


[OK] IMAGES


# Afficher image
img = plt.imread('image.png')
plt.imshow(img)

# Afficher avec extent
plt.imshow(img, extent=[0, 10, 0, 10])

# Aspect ratio
plt.imshow(img, aspect='auto')
plt.imshow(img, aspect='equal')

# Interpolation
plt.imshow(img, interpolation='nearest')
plt.imshow(img, interpolation='bilinear')
plt.imshow(img, interpolation='bicubic')

# Colormap pour grayscale
gray_img = plt.imread('image.png')
plt.imshow(gray_img, cmap='gray')

# Alpha (transparence)
plt.imshow(img, alpha=0.5)

# Origin (coin supérieur ou inférieur)
plt.imshow(img, origin='lower')
plt.imshow(img, origin='upper')

# Combiner image et plot
fig, ax = plt.subplots()
ax.imshow(img, extent=[0, 10, 0, 10], alpha=0.5)
ax.plot([0, 10], [0, 10], 'r-', linewidth=3)


[OK] COLORBARS AVANCÉES


# Colorbar basique
im = ax.imshow(data, cmap='viridis')
plt.colorbar(im)

# Colorbar avec label
plt.colorbar(im, label='Valeur')

# Orientation
plt.colorbar(im, orientation='horizontal')

# Position et taille
cbar = plt.colorbar(im, ax=ax, fraction=0.046, pad=0.04)

# Ticks personnalisés
cbar = plt.colorbar(im)
cbar.set_ticks([0, 0.5, 1])
cbar.set_ticklabels(['Bas', 'Moyen', 'Haut'])

# Extend (flèches)
plt.colorbar(im, extend='both')  # Flèches haut et bas
plt.colorbar(im, extend='min')   # Flèche en bas
plt.colorbar(im, extend='max')   # Flèche en haut

# Colorbar pour plusieurs subplots
fig, axes = plt.subplots(2, 2)
for ax in axes.flat:
    im = ax.imshow(data, cmap='viridis')
fig.colorbar(im, ax=axes.ravel().tolist())

# Colorbar normalisée
from matplotlib.colors import Normalize
norm = Normalize(vmin=0, vmax=10)
sm = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
sm.set_array([])
plt.colorbar(sm)


[OK] AXES MULTIPLES & INSETS


# Inset axes (zoom)
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

fig, ax = plt.subplots()
ax.plot(x, y)

# Créer inset
axins = inset_axes(ax, width="40%", height="40%", loc='upper right')
axins.plot(x, y)
axins.set_xlim(2, 3)
axins.set_ylim(3, 5)

# Mark inset
from mpl_toolkits.axes_grid1.inset_locator import mark_inset
mark_inset(ax, axins, loc1=2, loc2=4, fc="none", ec="0.5")

# Axes divider
from mpl_toolkits.axes_grid1 import make_axes_locatable

fig, ax = plt.subplots()
im = ax.imshow(data)
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
plt.colorbar(im, cax=cax)

# Zoom region
from matplotlib.patches import Rectangle
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes

fig, ax = plt.subplots()
ax.plot(x, y)

axins = zoomed_inset_axes(ax, zoom=2, loc='upper left')
axins.plot(x, y)
axins.set_xlim(2, 3)
axins.set_ylim(3, 5)


[OK] LOGARITHMIC & SPECIAL SCALES


# Log scale
ax.set_xscale('log')
ax.set_yscale('log')

# Log-log plot
plt.loglog(x, y)

# Semi-log
plt.semilogx(x, y)  # X en log
plt.semilogy(x, y)  # Y en log

# Symlog (log avec zéro)
ax.set_yscale('symlog', linthresh=0.01)

# Logit scale
ax.set_yscale('logit')

# Custom scale
from matplotlib.scale import ScaleBase
class CustomScale(ScaleBase):
    name = 'custom'
    
    def get_transform(self):
        # Définir transformation
        pass

# Fonctions pour log scale
x_log = np.logspace(0, 2, 100)  # 10^0 à 10^2
y_log = np.log10(x)             # Log base 10
y_ln = np.log(x)                # Log naturel


[OK] POLAR PLOTS


# Créer axes polaires
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')

# Plot polaire
theta = np.linspace(0, 2*np.pi, 100)
r = np.abs(np.sin(2*theta))
ax.plot(theta, r)

# Scatter polaire
ax.scatter(theta, r, c=theta, cmap='hsv', alpha=0.75)

# Bar polaire
bars = ax.bar(theta, r, width=0.1, alpha=0.5)

# Configuration
ax.set_theta_zero_location('N')  # Zéro en haut
ax.set_theta_direction(-1)       # Sens horaire

# Limites radiales
ax.set_ylim(0, 1)
ax.set_rlabel_position(45)

# Grid
ax.grid(True)

# Rose des vents
directions = np.linspace(0, 2*np.pi, 8, endpoint=False)
values = [1, 2, 3, 4, 5, 4, 3, 2]
width = 2*np.pi / 8
ax.bar(directions, values, width=width, bottom=0.0)


[OK] STATISTIQUES & DISTRIBUTIONS


# Distribution normale
from scipy import stats

mu, sigma = 0, 1
x = np.linspace(mu - 4*sigma, mu + 4*sigma, 100)
y = stats.norm.pdf(x, mu, sigma)
plt.plot(x, y, label='PDF')
plt.fill_between(x, y, alpha=0.3)

# Histogram avec fit
data = np.random.randn(1000)
plt.hist(data, bins=30, density=True, alpha=0.7)
mu, sigma = data.mean(), data.std()
x = np.linspace(data.min(), data.max(), 100)
plt.plot(x, stats.norm.pdf(x, mu, sigma), 'r-', lw=2)

# Q-Q plot
from scipy.stats import probplot
fig, ax = plt.subplots()
probplot(data, dist="norm", plot=ax)

# Confidence intervals
mean = np.mean(data)
std_err = stats.sem(data)
ci = stats.t.interval(0.95, len(data)-1, loc=mean, scale=std_err)
plt.axhspan(ci[0], ci[1], alpha=0.2, color='gray')

# Kernel Density Estimation
from scipy.stats import gaussian_kde
density = gaussian_kde(data)
x = np.linspace(data.min(), data.max(), 200)
plt.plot(x, density(x))


[OK] SÉRIES TEMPORELLES


# Dates
import datetime as dt
import matplotlib.dates as mdates

dates = [dt.datetime(2024, 1, i) for i in range(1, 31)]
values = np.random.randn(30).cumsum()

fig, ax = plt.subplots()
ax.plot(dates, values)

# Formatter dates
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax.xaxis.set_major_locator(mdates.DayLocator(interval=5))
plt.xticks(rotation=45, ha='right')

# Auto format dates
fig.autofmt_xdate()

# Différents formats
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))  # Jan 01
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))  # 2024-01
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))  # 14:30

# Locators
ax.xaxis.set_major_locator(mdates.YearLocator())
ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_major_locator(mdates.WeekdayLocator())
ax.xaxis.set_major_locator(mdates.DayLocator())
ax.xaxis.set_major_locator(mdates.HourLocator())

# Timestamp to datetime
timestamps = [1609459200 + i*86400 for i in range(30)]
dates = [dt.datetime.fromtimestamp(ts) for ts in timestamps]

# Pandas integration
import pandas as pd
df = pd.DataFrame({'date': dates, 'value': values})
df.set_index('date').plot()


[OK] MULTIPLOTS & LAYOUTS


# Subplots simples
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes[0, 0].plot(x, y)
axes[0, 1].scatter(x, y)
axes[0, 2].bar(x, y)

# Flatten axes pour itérer
for ax, data in zip(axes.flat, datasets):
    ax.plot(data)

# Share axes
fig, axes = plt.subplots(3, 1, sharex=True, figsize=(10, 8))

# Constrained layout
fig, axes = plt.subplots(2, 2, constrained_layout=True)

# Tight layout
fig, axes = plt.subplots(2, 2)
plt.tight_layout()

# Custom spacing
fig, axes = plt.subplots(2, 2)
plt.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1,
                    hspace=0.4, wspace=0.4)

# GridSpec avancé
from matplotlib.gridspec import GridSpec

fig = plt.figure(figsize=(12, 8))
gs = GridSpec(3, 3, figure=fig, hspace=0.3, wspace=0.3)

ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :-1])
ax3 = fig.add_subplot(gs[1:, -1])
ax4 = fig.add_subplot(gs[2, 0])
ax5 = fig.add_subplot(gs[2, 1])


[OK] EXPORT & BACKENDS


# Backends disponibles
import matplotlib
print(matplotlib.get_backend())

# Changer backend
matplotlib.use('TkAgg')    # Interface graphique
matplotlib.use('Agg')      # Non-interactif (serveur)
matplotlib.use('Qt5Agg')   # Qt5
matplotlib.use('WebAgg')   # Navigateur web

# Sauvegarder haute résolution
plt.savefig('figure.png', dpi=300, bbox_inches='tight')

# PDF vectoriel
plt.savefig('figure.pdf', format='pdf')

# SVG pour web
plt.savefig('figure.svg', format='svg')

# EPS pour LaTeX
plt.savefig('figure.eps', format='eps')

# Plusieurs formats
for fmt in ['png', 'pdf', 'svg']:
    plt.savefig(f'figure.{fmt}')

# Métadonnées
plt.savefig('figure.png', metadata={'Author': 'John Doe',
                                     'Title': 'Mon Graphique',
                                     'Subject': 'Analyse de données'})

# Face color (fond)
plt.savefig('figure.png', facecolor='white')

# Edge color
plt.savefig('figure.png', edgecolor='black')


[OK] PERFORMANCE & OPTIMISATION


# Blitting (animations rapides)
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
line, = ax.plot([], [])

def init():
    line.set_data([], [])
    return line,

def update(frame):
    line.set_data(x[:frame], y[:frame])
    return line,

ani = FuncAnimation(fig, update, frames=len(x), init_func=init,
                    blit=True, interval=20)

# Rasterization (gros datasets)
ax.plot(huge_x, huge_y, rasterized=True)

# Sauvegarder avec rasterization
plt.savefig('figure.pdf', dpi=300, rasterized=True)

# Désactiver auto-redraw
plt.ioff()
# ... faire plots ...
plt.show()  # Afficher une fois

# Réutiliser figure
fig, ax = plt.subplots()
for data in datasets:
    ax.clear()
    ax.plot(data)
    plt.pause(0.1)

# Path simplification
ax.plot(x, y, path_effects=[path_effects.SimplifyPath(threshold=0.1)])


[OK] INTÉGRATION AVEC AUTRES LIBS


# Seaborn
import seaborn as sns
sns.set_theme()
sns.lineplot(data=df, x='x', y='y')

# Pandas
import pandas as pd
df = pd.DataFrame({'x': x, 'y': y})
df.plot(x='x', y='y')
df.plot.scatter(x='x', y='y')
df.plot.bar()
df.plot.hist()

# NumPy
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)

# SciPy
from scipy import signal
t = np.linspace(0, 1, 500)
sig = np.sin(2 * np.pi * 5 * t)
filtered = signal.medfilt(sig, kernel_size=11)
plt.plot(t, sig, label='Original')
plt.plot(t, filtered, label='Filtered')

# PIL/Pillow
from PIL import Image
img = Image.open('image.png')
plt.imshow(img)

# OpenCV
import cv2
img = cv2.imread('image.png')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)


[OK] LATEX & TYPOGRAPHIE


# Activer LaTeX
plt.rc('text', usetex=True)
plt.rc('font', family='serif')

# Formules mathématiques
plt.title(r'$\alpha > \beta)
plt.xlabel(r'$\sum_{i=1}^{\infty} x_i)
plt.ylabel(r'$\frac{d}{dx}f(x))

# Texte avec math
plt.text(0.5, 0.5, r'$E = mc^2, fontsize=20)

# Symboles grecs
# \alpha, \beta, \gamma, \delta, \epsilon, \theta, \lambda, \mu, \pi, \sigma, \omega

# Opérateurs
# \sum, \prod, \int, \partial, \nabla, \infty

# Fractions et racines
plt.text(0.5, 0.5, r'$\frac{a}{b})
plt.text(0.5, 0.5, r'$\sqrt{x})
plt.text(0.5, 0.5, r'$x^{2})
plt.text(0.5, 0.5, r'$x_{i})

# Matrices
plt.text(0.5, 0.5, r'$\begin{bmatrix} a & b \\ c & d \end{bmatrix})

# Désactiver LaTeX
plt.rc('text', usetex=False)

# Font properties
from matplotlib import font_manager
font_prop = font_manager.FontProperties(family='monospace', size=12, weight='bold')
plt.text(0.5, 0.5, 'Texte', fontproperties=font_prop)


[OK] TROUBLESHOOTING


# Backend issues
import matplotlib
matplotlib.use('TkAgg')  # Si problème d'affichage
import matplotlib.pyplot as plt

# Memory leaks (fermer figures)
plt.close('all')  # Fermer toutes
plt.close(fig)    # Fermer une figure spécifique
plt.close(1)      # Fermer figure numéro 1

# Warning UserWarning: tight_layout
plt.tight_layout()
# ou
fig.set_constrained_layout(True)

# LaTeX not found
plt.rc('text', usetex=False)  # Désactiver LaTeX

# Font not found
plt.rcParams['font.sans-serif'] = ['DejaVu Sans']

# Import error: No module named '_tkinter'
# Installer: apt-get install python3-tk (Linux)
# ou utiliser backend différent: matplotlib.use('Agg')

# Figure too big warning
plt.rcParams['figure.max_open_warning'] = 50

# Display not found (serveur)
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt


[OK] BONNES PRATIQUES


# [OK] Utiliser style OO (subplots) plutôt que pyplot
fig, ax = plt.subplots()
ax.plot(x, y)  # Plutôt que plt.plot(x, y)

# [OK] Toujours labelliser axes et ajouter légende
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_title('Titre')
ax.legend()

# [OK] Utiliser constrained_layout ou tight_layout
fig, ax = plt.subplots(constrained_layout=True)

# [OK] Spécifier figsize dès la création
fig, ax = plt.subplots(figsize=(10, 6))

# [OK] Fermer figures pour libérer mémoire
plt.close('all')

# [OK] Sauvegarder avant plt.show()
plt.savefig('figure.png')
plt.show()

# [OK] Utiliser context manager pour styles
with plt.style.context('seaborn-v0_8'):
    plt.plot(x, y)

# [X] Éviter pyplot pour scripts/production
# plt.plot(x, y)  # Mauvais
# ax.plot(x, y)   # Bon

# [X] Ne pas mélanger pyplot et OO
# plt.figure()
# ax = plt.subplot(111)
# ax.plot(x, y)
# plt.show()


[OK] EXEMPLES COMPLETS


# Exemple 1: Plot scientifique complet
fig, ax = plt.subplots(figsize=(10, 6), dpi=100)

x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

ax.plot(x, y1, 'b-', linewidth=2, label='sin(x)')
ax.plot(x, y2, 'r--', linewidth=2, label='cos(x)')

ax.set_xlabel('x', fontsize=14)
ax.set_ylabel('y', fontsize=14)
ax.set_title('Fonctions Trigonométriques', fontsize=16, fontweight='bold')

ax.grid(True, linestyle='--', alpha=0.5)
ax.legend(loc='upper right', fontsize=12)

ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)

plt.tight_layout()
plt.savefig('trig_functions.png', dpi=300, bbox_inches='tight')
plt.show()


# Exemple 2: Dashboard multi-plots
fig = plt.figure(figsize=(15, 10))
gs = GridSpec(3, 3, figure=fig, hspace=0.3, wspace=0.3)

# Line plot
ax1 = fig.add_subplot(gs[0, :])
ax1.plot(x, y, 'b-', linewidth=2)
ax1.set_title('Série Temporelle')
ax1.grid(True, alpha=0.3)

# Scatter
ax2 = fig.add_subplot(gs[1, 0])
ax2.scatter(x, y, c=y, cmap='viridis', alpha=0.6)
ax2.set_title('Scatter Plot')

# Histogram
ax3 = fig.add_subplot(gs[1, 1])
ax3.hist(y, bins=30, color='skyblue', edgecolor='black')
ax3.set_title('Distribution')

# Box plot
ax4 = fig.add_subplot(gs[1, 2])
ax4.boxplot([y1, y2, y3])
ax4.set_title('Box Plots')

# Heatmap
ax5 = fig.add_subplot(gs[2, :2])
data = np.random.rand(10, 10)
im = ax5.imshow(data, cmap='coolwarm', aspect='auto')
plt.colorbar(im, ax=ax5)
ax5.set_title('Heatmap')

# Pie
ax6 = fig.add_subplot(gs[2, 2])
sizes = [25, 35, 20, 20]
ax6.pie(sizes, labels=['A', 'B', 'C', 'D'], autopct='%1.1f%%')
ax6.set_title('Proportions')

plt.savefig('dashboard.png', dpi=150, bbox_inches='tight')
plt.show()


# Exemple 3: Animation interactive
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots(figsize=(10, 6))
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1.5, 1.5)
line, = ax.plot([], [], 'b-', linewidth=2)
point, = ax.plot([], [], 'ro', markersize=10)

def init():
    line.set_data([], [])
    point.set_data([], [])
    return line, point

def update(frame):
    x = np.linspace(0, frame, 100)
    y = np.sin(x)
    line.set_data(x, y)
    point.set_data([frame], [np.sin(frame)])
    return line, point

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True, interval=20)

plt.show()


[OK] RESSOURCES


# Documentation officielle: https://matplotlib.org/
# Gallery: https://matplotlib.org/stable/gallery/index.html
# Cheatsheets: https://github.com/matplotlib/cheatsheets
# Tutorials: https://matplotlib.org/stable/tutorials/index.html
# Seaborn (haut niveau): https://seaborn.pydata.org/
# Plotly (interactif): https://plotly.com/python/