USMA1Q — Méthodes Numériques¶

Séance 2 — Fonctions, Listes & Traitement de données¶

Conservatoire National des Arts et Métiers

04 Mars 2026

nicholas-anton.collins-craft@enpc.fr

Un binder qui peut faire tourner le notebook qui contient les exercices se trouve ici :

https://mybinder.org/v2/gh/nickcollins-craft/USMA1Q-Methodes-Numeriques/HEAD

Plan du cours¶

Jour Sujet
03 mars Fondamentaux de la programmation en Python
04 mars Fonctions, listes approfondies, fichiers et graphiques
11 mars Matrices, précision et la résolution numérique des équations linéaires
18 mars Interpolation, la recherche des racines des fonctions et la résolution numérique des équations non linéaires
25 mars Intégration numérique
02 avril La résolution numérique des équations différentielles ordinaires et partielles
08 avril Examen

Plan de la séance (indicatif)¶

Horaire Sujet
0:00 – 1:00 Fonctions (def, return, docstrings, erreurs)
1:00 – 1:50 Listes approfondies (découpage, compréhensions)
1:50 – 2:00 ☕ Pause
2:00 – 2:40 Lecture de fichiers (open, split, CSV)
2:40 – 3:45 Premiers graphiques avec Matplotlib

1 · Fonctions¶

Pourquoi écrire des fonctions ?¶

Imaginez que vous calculez la moyenne d'une liste à cinq endroits différents dans votre script.
Si vous corrigez une erreur dans le calcul, vous devez la corriger cinq fois — et oublier l'une d'elles crée un bug silencieux.

Une fonction (function) résout ce problème :

  • Réutilisation — écrire une fois, appeler partout
  • Lisibilité — un nom explicite remplace dix lignes de calcul
  • Testabilité — on peut vérifier une fonction seule, indépendamment du reste

Règle d'or : si vous copiez-collez le même bloc de code deux fois, il est temps d'écrire une fonction.

Définir une fonction — def et return¶

def nom_de_la_fonction(parametre1, parametre2):
    # corps de la fonction — indenté de 4 espaces
    resultat = parametre1 + parametre2
    return resultat
  • def (définir) — mot-clé qui annonce la définition d'une fonction
  • Les paramètres (parameters) sont les variables d'entrée, listées entre parenthèses. Ils peuvent être de n'importe quel type
  • return (retourner) — envoie la valeur calculée à l'appelant et termine la fonction
  • Le corps (body) est indenté — comme pour if et for
def contrainte(force, aire):
    """Calcule la contrainte en MPa."""
    return force / aire

sigma = contrainte(10000, 78.5)   # appel avec les arguments
print(sigma)                      # → 127.3885…

La définition décrit ce que fait la fonction. L'appel (call) l'exécute.

Démonstration — fonctions simples¶

In [ ]:
# ── Fonctions simples ──────────────────────────────────────────────────────

def celsius_vers_kelvin(t_celsius):
    """Convertit une température de Celsius en Kelvin."""
    return t_celsius + 273.15

def taux_allongement(L0, Lf):
    """Calcule le taux d'allongement en pourcentage."""
    return ((Lf - L0) / L0) * 100

# Appels
print(celsius_vers_kelvin(850))          # → 1123.15 K
print(taux_allongement(50.0, 63.5))      # → 27.0 %

Retourner plusieurs valeurs¶

Une fonction peut renvoyer plusieurs valeurs séparées par une virgule — Python les regroupe dans un tuple (n-uplet).

def min_max(valeurs):
    """Retourne le minimum et le maximum d'une liste."""
    mini = valeurs[0]
    maxi = valeurs[0]
    for v in valeurs:
        if v < mini:
            mini = v
        if v > maxi:
            maxi = v
    return mini, maxi   # deux valeurs séparées par une virgule

m, M = min_max([245, 251, 238, 260, 249])
print("Min =", m, "Max =", M)   # → Min = 238  Max = 260

On peut récupérer chaque valeur dans sa propre variable en les séparant par une virgule à gauche du =.

Les docstrings (chaînes de documentation)¶

Une docstring est une chaîne de caractères placée juste après def, entre triples guillemets. Elle décrit ce que fait la fonction : ce qu'elle attend en entrée, ce qu'elle retourne. Ce n'est pas une partie obligatoire, mais c'est un bon pratique.

def moyenne(valeurs):
    """
    Calcule la moyenne arithmétique d'une liste de nombres.

    Paramètres
    ----------
    valeurs : list
        Liste de nombres (int ou float).

    Retourne
    --------
    float
        La moyenne de la liste.
    """
    total = 0
    for v in valeurs:
        total += v
    return total / len(valeurs)

La docstring est accessible avec help(moyenne) — votre IDE l'affiche aussi automatiquement.

Bonne pratique : une fonction sans docstring est une fonction à moitié finie si on veut faire un code durable.

⚠️ Oublier return — la fonction retourne None¶

Si une fonction ne contient pas de return, Python retourne silencieusement None (rien) — sans erreur. C'est une source fréquente de bugs difficiles à repérer.

def double(x):
    resultat = x * 2
    # oubli du return !

y = double(5)
print(y)          # → None  ← pas 10 !
print(y + 1)      # → TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

Règle : si vous voyez None là où vous attendez un nombre, cherchez un return manquant.

Lever une erreur — raise ValueError¶

Une bonne fonction vérifie ses entrées et signale clairement si quelque chose ne va pas.

  • raise (lever) — déclenche une exception (exception) et interrompt l'exécution
  • ValueError (erreur de valeur) — exception standard pour une valeur incorrecte
def racine_carree(x):
    """Calcule √x. Lève ValueError si x est négatif."""
    if x < 0:
        raise ValueError("x doit être positif, reçu : " + str(x))
    return x ** 0.5

print(racine_carree(9))    # → 3.0
print(racine_carree(-4))   # → ValueError: x doit être positif, reçu : -4

Un message d'erreur explicite vaut mieux qu'un résultat silencieusement faux (NaN ou un nombre aberrant).

In [ ]:
# ── Démonstration : fonction robuste ──────────────────────────────────────
import math


def contrainte_engineering(force, diametre):
    """
    Calcule la contrainte d'ingénierie (MPa) pour un cylindre.

    Paramètres
    ----------
    force    : float  Force en N
    diametre : float  Diamètre en mm

    Retourne
    --------
    float  Contrainte en MPa
    """
    if diametre <= 0:
        raise ValueError("Le diamètre doit être strictement positif, reçu : " + str(diametre))
    if force < 0:
        raise ValueError("La force ne peut pas être négative, reçu : " + str(force))
    aire = math.pi * (diametre / 2) ** 2   # mm²
    return force / aire                     # N/mm² = MPa

print(contrainte_engineering(15000, 10.0))   # → 190.99 MPa
# print(contrainte_engineering(15000, -5))   # → ValueError (décommenter pour tester)

À vous de faire les exercices 1.1, 1.2 et 1.3 !¶

2 · Listes approfondies¶

Découpage (slicing) — liste[début:fin:pas]¶

Le découpage extrait une sous-liste sans modifier l'originale.

mesures = [245, 251, 238, 260, 249, 253, 247]
#           0     1    2    3    4    5    6

mesures[1:4]    # → [251, 238, 260]   indice 1 inclus, 4 exclus
mesures[:3]     # → [245, 251, 238]   du début jusqu'à 3 (exclus)
mesures[4:]     # → [249, 253, 247]   de 4 jusqu'à la fin
mesures[::2]    # → [245, 238, 249, 247]  un élément sur deux (pas=2)
mesures[::-1]   # → [247, 253, 249, 260, 238, 251, 245]  ordre inversé

⚠️ Le découpage crée une copie indépendante. Modifier la sous-liste ne modifie pas l'originale.

Compréhension de liste (list comprehension)¶

La compréhension de liste est une façon concise de construire une nouvelle liste à partir d'une liste existante.

Forme générale :

nouvelle_liste = [expression for element in liste_source]

Avec filtre (filter) optionnel :

nouvelle_liste = [expression for element in liste_source if condition]

Exemples :

mesures = [245, 251, 238, 260, 249]

# Convertir chaque valeur HB en MPa (approximation : HB × 3.45)
en_mpa = [v * 3.45 for v in mesures]
# → [845.25, 866.95, 821.1, 897.0, 860.05]

# Ne garder que les valeurs supérieures à 248
elevees = [v for v in mesures if v > 248]
# → [251, 260, 249]

Compréhension vs boucle for¶

Les deux formes produisent le même résultat :

# Avec boucle — explicite, facile à lire pour les débutants
carres = []
for v in mesures:
    carres.append(v ** 2)

# Avec compréhension — plus concis, idiomatique en Python
carres = [v ** 2 for v in mesures]

Quand utiliser quoi ?

Situation Forme recommandée
Transformation simple d'une liste Compréhension
Logique complexe, plusieurs étapes Boucle for
Accumulation avec état (total, max…) Boucle for
In [ ]:
# ── Démonstration : compréhensions de listes ──────────────────────────────

limites_elastiques = [320, 415, 285, 510, 370, 295, 480, 440, 350, 395]  # MPa

# Conversion en kN/cm²  (1 MPa = 0.1 kN/cm²)
en_kn_cm2 = [v * 0.1 for v in limites_elastiques]
print("En kN/cm² :", en_kn_cm2)

# Filtrer : valeurs ≥ 400 MPa (aciers haute résistance)
haute_resistance = [v for v in limites_elastiques if v >= 400]
print("≥ 400 MPa :", haute_resistance)

# Pourcentage qui passe
taux = len(haute_resistance) / len(limites_elastiques) * 100
print("Taux de conformité :", round(taux), "%")

À vous de faire les exercices 2.1 et 2.2 !¶

☕ Pause — 10 minutes¶

3 · Lecture de fichiers¶

Ouvrir un fichier — open() et le gestionnaire with¶

open(chemin, mode) (ouvrir) ouvre un fichier et retourne un objet fichier (file object).

# Sans gestionnaire — il FAUT fermer le fichier manuellement
f = open("donnees.csv", "r")   # "r" = lecture (read)
# ... lire ...
f.close()   # close() = fermer — si on oublie, le fichier reste verrouillé !

Mieux : utiliser le gestionnaire de contexte with (avec) — le fichier est automatiquement fermé à la sortie du bloc, même en cas d'erreur.

with open("donnees.csv", "r") as f:
    # tout ce qui lit le fichier va ici
    contenu = f.read()   # read() = lire tout le fichier d'un coup
# f est automatiquement fermé ici

Bonne pratique : utilisez toujours with open(...) — c'est la forme idiomatique et sûre.

Lire ligne par ligne — readline(), strip(), split()¶

Pour un CSV, on lit une ligne à la fois et on la découpe :

with open("donnees.csv", "r") as f:
    for ligne in f:                   # itérer sur les lignes
        ligne = ligne.strip()         # strip() — supprime \n et espaces en bout
        if ligne.startswith("#"):     # startswith() — ignorer les commentaires
            continue
        morceaux = ligne.split(",")   # split(",") — découpe sur la virgule
        # morceaux est une liste de chaînes : ["0.002", "380.0"]
        deformation = float(morceaux[0])   # float() — convertir chaîne en nombre
        contrainte  = float(morceaux[1])
Méthode Signification Exemple
strip() supprimer espace/\n en bout de chaîne "0.002\n" → "0.002"
split(",") découper la chaîne sur la virgule "0.002,380" → ["0.002", "380"]
float() convertir une chaîne en décimal "380.0" → 380.0
startswith() vérifier si la chaîne commence par… "# commentaire" → True

⚠️ Pièges fréquents lors de la lecture de fichiers¶

1. Oublier strip() avant split()

ligne = "0.002,380.0\n"
morceaux = ligne.split(",")   # → ["0.002", "380.0\n"]  ← \n dans le dernier !
float(morceaux[1])            # → ValueError (impossible de convertir "380.0\n")

# Correction :
morceaux = ligne.strip().split(",")   # → ["0.002", "380.0"]  ✓

2. Ne pas sauter l'en-tête

# La première ligne du CSV est : "deformation,contrainte"
# float("deformation") → ValueError !

# Correction : ignorer les lignes qui ne commencent pas par un chiffre
if ligne[0].isdigit():
    # traiter la ligne

3. Ouvrir sans with et oublier close()
Le fichier reste verrouillé — impossible de le modifier ou le supprimer tant que Python tourne.

À vous de faire les exercices 3.1 et 3.2 !¶

4 · Premiers graphiques avec Matplotlib¶

Importer et tracer une première courbe¶

import matplotlib.pyplot as plt   # convention universelle : alias plt

Structure minimale d'un graphique :

plt.figure(figsize=(8, 5))   # figure() — créer une figure ; figsize = taille en pouces
plt.plot(x, y)               # plot() — tracer une courbe
plt.xlabel("...")            # xlabel() — étiquette de l'axe x
plt.ylabel("...")            # ylabel() — étiquette de l'axe y
plt.title("...")             # title()  — titre du graphique
plt.grid(True)               # grid()   — afficher la grille
plt.tight_layout()           # ajuster les marges automatiquement
plt.show()                   # show()   — afficher le graphique

plt.plot(x, y) relie les points dans l'ordre — c'est une courbe.
Si x n'est pas croissant, la courbe aura des zigzags inattendus.

Démonstration — courbe contrainte–déformation¶

In [ ]:
# ── Courbe contrainte–déformation — caoutchouc naturel souple ────────────
import matplotlib.pyplot as plt

# Données — caoutchouc naturel souple
deformations = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.5, 1.8, 2.0, 2.5, 3.0, 3.5, 4.0]
contraintes  = [0.000, 0.152, 0.267, 0.363, 0.447, 0.525, 0.598, 0.702,
                0.802, 0.867, 1.025, 1.181, 1.335, 1.488]   # MPa

plt.figure(figsize=(8, 5))
plt.plot(deformations, contraintes,
         color='steelblue',    # couleur de la courbe
         linewidth=2,          # épaisseur du trait
         label='Caoutchouc naturel')   # label = étiquette pour la légende

plt.xlabel("Déformation ε (sans unité)", fontsize=13)
plt.ylabel("Contrainte σ (MPa)",         fontsize=13)
plt.title("Courbe contrainte–déformation — caoutchouc naturel souple", fontsize=14)
plt.legend(fontsize=12)        # legend() — afficher la légende
plt.grid(True)
plt.tight_layout()
plt.show()

Paramètres de mise en forme¶

Paramètre Valeurs exemples Effet
color 'steelblue', '#E07B39', 'k' couleur de la courbe
linewidth 1, 2, 3 épaisseur
linestyle '-', '--', ':', '-.' style du trait
marker 'o', 's', '^', '+' marqueur sur chaque point
label 'Acier' texte dans la légende
alpha 0.0–1.0 transparence
plt.plot(x, y, color='k', linestyle='--', marker='o', markersize=4, label='Essai 1')
plt.plot(x, z, color='steelblue', linestyle='-', label='Essai 2')
plt.legend()

Pour comparer plusieurs courbes, variez à la fois la couleur et le style de trait — les lecteurs daltoniensne verront peut-être pas la différence de couleur seule.

Nuage de points — plt.scatter()¶

plt.scatter(x, y) (nuage de points) affiche chaque paire (x, y) comme un point isolé, sans les relier.
Utile pour visualiser des données expérimentales sans supposer de tendance entre les points.

# Dureté vs résistance à la traction pour différents alliages
durete = [170, 210, 240, 275, 310, 350, 390]
Rm     = [530, 650, 750, 860, 980, 1100, 1220]

plt.figure(figsize=(7, 5))
plt.scatter(durete, Rm,
            color='tomato',   # tomato = rouge-orangé
            s=60,             # s = taille des marqueurs
            edgecolors='k',   # edgecolors = couleur du contour
            linewidths=0.8)
plt.xlabel("Dureté HB",            fontsize=12)
plt.ylabel("Résistance Rm (MPa)",  fontsize=12)
plt.title("Corrélation dureté–Rm", fontsize=13)
plt.grid(True)
plt.tight_layout()
plt.show()

Bonnes pratiques de visualisation¶

Un graphique publiable respecte ces règles :

  1. Axes étiquetés avec les unités — "σ (MPa)", jamais juste "sigma"
  2. Texte lisible — fontsize=10 (ou plus) minimum ; essayez d'avoir au moins la même taille que le texte du document
  3. Légende si plusieurs courbes — avec label= dans plot() et plt.legend()
  4. Palette daltonisme-amicale — évitez les rouges et verts autant que possible
  5. Différencier les courbes par couleur et style de trait ('-', '--', ':') — pas couleur seule
  6. Titre informatif — "Courbe σ–ε — acier 316L", si un titre est démandé
  7. plt.tight_layout() — évite les étiquettes coupées en export
In [ ]:
# ── Nuage de points : dureté vs résistance ────────────────────────────────
import matplotlib.pyplot as plt

durete = [170, 210, 240, 275, 310, 350, 390]
Rm     = [530, 650, 750, 860, 980, 1100, 1220]
noms   = ['E24', 'E28', 'S355', 'S460', 'HLE 690', 'HLE 890', 'HLE 1100']

plt.figure(figsize=(7, 5))
plt.scatter(durete, Rm, color='tomato', s=70, edgecolors='k', linewidths=0.8, zorder=3)

# Étiqueter chaque point
for i, nom in enumerate(noms):
    plt.annotate(nom, (durete[i], Rm[i]),
                 textcoords="offset points", xytext=(6, 4), fontsize=9)

plt.xlabel("Dureté HB",            fontsize=12)
plt.ylabel("Résistance Rm (MPa)",  fontsize=12)
plt.title("Corrélation dureté–Rm — aciers structuraux", fontsize=13)
plt.grid(True)
plt.tight_layout()
plt.show()

À vous de faire les exercices 4.1 et 4.2 !¶

Points clés de la séance¶

Concept Ce qu'il faut retenir
Fonctions (def/return) Écrire une fois, appeler partout ; toujours inclure une docstring
return manquant La fonction retourne silencieusement None — source de bugs
raise ValueError Signaler explicitement une entrée invalide
Découpage (liste[a:b:pas]) Extrait une sous-liste indépendante
Compréhension de liste [expr for x in liste if cond] — concis pour transformer/filtrer
with open(...) Fermeture automatique du fichier — toujours utiliser cette forme
strip() → split() Supprimer \n avant de découper — ordre important
plt.plot() / plt.scatter() Courbe reliée vs points isolés ; étiqueter axes avec unités