VaultBy ENSAMIENParrainage
Notebook

2. Numpy (suite):

06-regex-numpy-broadcasting.ipynb

1. Expression régulière

Les expressions régulières (ou "regex") sont des chaînes de caractères utilisées pour rechercher, extraire, ou valider des motifs (patterns) dans du texte (ex. : email, numéro de téléphone, mot-clé...).

Python fournit le module re pour les manipuler (import re)

import re
  • Syntaxe des motifs (patterns)
Motif Signification Exemple(s) d’utilisation
. N’importe quel caractère sauf retour à la ligne a.c : correspond à abc, axc mais pas ac
^ Début de ligne ^Hello : correspond à Hello world si la ligne commence par Hello
$ Fin de ligne end$ : correspond à the end
[] Classe de caractères (ensemble autorisé) [0-9] : un chiffre, [aeiou] : une voyelle
\| OU logique (alternatives possibles) chat\|chien : correspond à chat ou chien
  • Motifs de base
Pattern Signification Exemple
. N'importe quel caractère (sauf retour ligne) a.c correspond à "abc", "a7c"
\d Un chiffre (0-9) \d\d correspond à "42"
\D Tout sauf un chiffre \D+ correspond à "abc"
\w Un caractère alphanumérique ou _ \w+ → "Nom123"
\W Tout sauf un caractère alphanumérique \W+ → "@#!"
\s Un espace, tabulation ou retour ligne \s+ → " "
\S Tout sauf un espace \S+ → "Texte"
  • Quantificateurs:
Pattern Signification Exemple
* 0 ou plusieurs ab* : "a", "abbb"
+ 1 ou plusieurs ab+ : "ab", "abbbb"
? 0 ou 1 fois ab? : "a", "ab"
{n} exactement n fois \d{4} : "2025"
{n,} au moins n fois \d{2,} : "1234"
{n,m} entre n et m fois \d{2,4} : "123", "2025"
  • Délimiteurs et classes de caractères
Pattern Signification Exemple
[...] Une des valeurs listées [abc] → "a", "b" ou "c"
[^...] Tout sauf les valeurs listées [^abc] → "d", "z"
[0-9] Chiffre de 0 à 9 [0-9]+ → "2025"
[a-zA-Z] Lettre majuscule ou minuscule [a-zA-Z]+ → "Python"
  • Début et fin de chaîne
Pattern Signification Exemple
^ Début de chaîne ^Bonjour → correspond à "Bonjour Alice"
$ Fin de chaîne 2025$ → correspond à "année 2025"
\b Limite de mot \bmot\b → correspond à "mot" mais pas "motif"
  • Exemples de motifs utiles
Objectif Regex
Nombre entier ^\d+$
Date (format jj/mm/aaaa) \d{2}/\d{2}/\d{4}
  • Fonctions principales de re
Fonction Rôle
re.search() Recherche un motif une seule fois dans une chaîne
re.match() Teste si le début de la chaîne correspond au motif
re.findall() Retourne toutes les correspondances
re.finditer() Idem que findall mais retourne un itérateur d'objets Match
re.sub() Remplace des parties de texte selon un motif
re.split() Coupe une chaîne selon un motif

Exemples

  • search()
# re.search() : Recherche un motif une seule fois dans une chaîne
import re

# Exemple 1 : présence d'un mot
texte = "Bienvenue au cours Python"
corresp = re.search(r"Python", texte)
print(corresp.group())  

# Exemple 2 : chiffre dans une phrase
corresp = re.search(r"\d+", "Il a 13 ans 15 ans")
print(corresp.group())  # 13

# Exemple 3 : Non chiffre dans une phrase
corresp = re.search(r"\D+", "Il a 13 ans")
print(corresp.group())  
Python
13
Il a 
  • match()
import re
# re.match() : Vérifie si le début de la chaîne correspond au motif.
# Exemple 1 : bon début
resultat = re.match(r"Bonjour", "Bonjour tout le monde")
print(resultat, "de type ", type(resultat)) 

print(resultat.group())  # .group() sert à récupérer le texte exact trouvé par la correspondance (match) : Bonjour

# Exemple 2 : ne matche pas car ce n’est pas au début
print(re.match(r"tout", "Bonjour tout le monde"))  # None
<re.Match object; span=(0, 7), match='Bonjour'> de type  <class 're.Match'>
Bonjour
None
  • findall()
# re.findall() : Retourne toutes les correspondances sous forme de liste.
# Exemple 1 : trouver tous les chiffres
nums = re.findall(r"\d+", "Année 2020, puis 2024, et maintenant 2025")
print(nums)  # ['2020', '2024', '2025']

# Exemple 2 : mots commençant par une majuscule
mots = re.findall(r"\b[A-Z]\w+", "Alice est avec C Bob et Oscar")
print(mots)  # ['Alice', 'Bob', 'Oscar']
['2020', '2024', '2025']
['Alice', 'Bob', 'Oscar']
  • re.finditer()
# re.finditer() : Comme findall, mais retourne des objets Match, donc plus d’info
# Exemple 1 : trouver les positions des chiffres
for m in re.finditer(r"\d+", "123 et 456"):
    print(f"Nombre : {m.group()}, position : {m.start()}")  
# Nombre : 123, position : 0
# Nombre : 456, position : 7

# Exemple 2 : toutes les majuscules avec leur position
texte = "Python Et C"
for match in re.finditer(r"[A-Z]", texte):
    print(match.group(), match.start())
# P 0
# E 7
# C 10
Nombre : 123, position : 0
Nombre : 456, position : 7
P 0
E 7
C 10
  • split()
# re.split()
# Exemple 1 : couper par espace ou virgule
print(re.split(r"[ ,]+", "Python, PHP Java,, C++")) 
# ['Python', 'PHP', 'Java', 'C++']

# Exemple 2 : couper à chaque chiffre
print(re.split(r"\d+", "abc123def456ghi")) # ['abc', 'def', 'ghi']
['Python', 'PHP', 'Java', 'C++']
['abc', 'def', 'ghi']
  • sub()
# re.sub(): Remplace les correspondances par une nouvelle valeur.
# Exemple 1 : anonymiser un email
texte = "Mon email est alice@gmail.com"
print(re.sub(r"\S+@\S+", "[EMAIL]", texte)) # Mon email est [EMAIL]

# Exemple 2 : remplacer tous les chiffres par '*'
print(re.sub(r"\d", "*", "Code 12345"))   # Code *****
Mon email est [EMAIL]
Code *****
  • fullmatch()
    La fonction re.fullmatch() vérifie si toute la chaîne correspond exactement au motif.
import re

motif = r"\d+"  # Un ou plusieurs chiffres

print(re.fullmatch(motif, "12345"))    #  OK → Match

print(re.fullmatch(motif, "123abc"))   #  NON → None
<re.Match object; span=(0, 5), match='12345'>
None

- Méthodes importantes

  • ravel() : pour applatir un tableau (qu'il ne fasse plus qu'une dimension)
  • squeeze() : quand une dimension est égale a 1, cette dimension disparait
  • concatenate() : assemble 2 tableaux ensemble selon un axes (existe aussi en hstack et vstack)
import numpy as np
A = np.zeros((2, 3)) # création d'un tableau de shape (2, 3)
print("-------------- A")
print(A)
-------------- A
[[0. 0. 0.]
 [0. 0. 0.]]
print("-------------- A.reshape()")
A = A.reshape((3, 2)) # redimensionne le tableau A (3 lignes, 2 colonnes)
print(A)
-------------- A.reshape()
[[0. 0.]
 [0. 0.]
 [0. 0.]]
print("-------------- A.ravel()")
X = A.ravel() # Aplatit le tableau A (une seule dimension)
print(X) 
-------------- A.ravel()
[0. 0. 0. 0. 0. 0.]
Y = np.array([[[1, 1, 1], [2, 4, 8]]]) 
print ("- Y : \n", Y) 

d1 = 0
print (f"\n- Y[{d1}] :\n {Y[d1]}")  

d2 = 1
print (f"\n- Y[{d1},{d2}] :\n {Y[d1,d2]}")  

d3 = 2
print (f"\n- Y[{d1},{d2},{d3}] :\n {Y[d1,d2,d3]}")  

print("\nShape de Y : ", Y.shape)   
- Y : 
 [[[1 1 1]
  [2 4 8]]]

- Y[0] :
 [[1 1 1]
 [2 4 8]]

- Y[0,1] :
 [2 4 8]

- Y[0,1,2] :
 8

Shape de Y :  (1, 2, 3)
print("-------------- squeeze() [Fr : presser]")
YP = np.squeeze(Y)  # élimine les dimensions "1" de Y.
print ("Apres squeezed() Y :\n ", YP) 
print("\nShape Y : ", YP.shape)
-------------- squeeze() [Fr : presser]
Apres squeezed() Y :
  [[1 1 1]
 [2 4 8]]

Shape Y :  (2, 3)
print("-------------- squeeze() [Fr : presser]")
YP = np.squeeze(YP)  # élimine les dimensions "1" de Y.
print ("Apres squeezed() Y :\n ", YP) 
print("\nShape Y : ", YP.shape)
-------------- squeeze() [Fr : presser]
Apres squeezed() Y :
  [[1 1 1]
 [2 4 8]]

Shape Y :  (2, 3)
A = np.zeros((2, 3)) # création d'un tableau de shape (2, 3)
B = np.ones((3, 3)) # création d'un tableau de shape (2, 3)

print("A :\n", A)
print("\nB :\n", B)
A :
 [[0. 0. 0.]
 [0. 0. 0.]]

B :
 [[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
np.concatenate((A, B), axis=0) # axe 0 : équivalent de np.vstack((A, B))
array([[0., 0., 0.],
       [0., 0., 0.],
       [1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])
A = np.zeros((2, 3)) # création d'un tableau de shape (2, 3)
B = np.ones((2, 4)) # création d'un tableau de shape (2, 3)
print("A :\n", A)
print("\nB :\n", B)
A :
 [[0. 0. 0.]
 [0. 0. 0.]]

B :
 [[1. 1. 1. 1.]
 [1. 1. 1. 1.]]
np.concatenate((A, B), axis=1) # axe 1 : équivalent de np.hstack((A, B))
array([[0., 0., 0., 1., 1., 1., 1.],
       [0., 0., 0., 1., 1., 1., 1.]])

Indexing et Slicing

Le fonctionnement est le meme que pour les listes

import numpy as np
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A)
[[1 2 3]
 [4 5 6]]
# Pour selectionner les blocs de la ligne (0-1) colonne (0-1)
A[0:2, 0:2]
array([[1, 2],
       [4, 5]])
A[0:2, 1:] = 11
print(A)
[[ 1 11 11]
 [ 4 11 11]]

Boolean Indexing

A = np.array([[7, 2, 3], [4, 5, 6]])
print(A)
[[7 2 3]
 [4 5 6]]
print(A<5) # masque booléen
[[False  True  True]
 [ True False False]]
print(A[A < 5]) # sous-ensemble filtré par le masque booléen
[2 3 4]
A[A<5] = 412 # convertit les valeurs sélectionnées.
print(A)
[[  7 412 412]
 [412   5   6]]

Fonctionnalités Mathématiques:

import numpy as np
- Méthodes de bases (les plus utiles) de la classe ndarray
A = np.array([[1, 2, 3], [4, 5, 6]])
print("------------- A:")
print(A)

print("\nsum :", A.sum()) # effectue la somme de tous les éléments du tableau
print("sum(axis=0) :", A.sum(axis=0)) # effectue la somme des colonnes (somme sur éléments des les lignes)
print("sum(axis=1) :",A.sum(axis=1)) # effectue la somme des lignes (somme sur les éléments des colonnes)
print("A.cumsum(axis=1) :\n", A.cumsum(axis=1)) # effectue la somme cumulée
print("A.cumsum(axis=0) :\n", A.cumsum(axis=0)) # effectue la somme cumulée
------------- A:
[[1 2 3]
 [4 5 6]]

sum : 21
sum(axis=0) : [5 7 9]
sum(axis=1) : [ 6 15]
A.cumsum(axis=1) :
 [[ 1  3  6]
 [ 4  9 15]]
A.cumsum(axis=0) :
 [[1 2 3]
 [5 7 9]]
A = np.array([[1, 2, 3], [4, 5, 6]])
print("------------- A:")
print(A)
print("A.prod() :", A.prod()) # effectue le produit
print("A.cumprod():",A.cumprod()) # effectue le produit cumulé
------------- A:
[[1 2 3]
 [4 5 6]]
A.prod() : 720
A.cumprod(): [  1   2   6  24 120 720]
A = np.array([[1, 2, 3], [4, 5, 6]])
print("------------- A:")
print(A)
print("A.min():",A.min()) # trouve le minimum du tableau
print("A.max():",A.max()) # trouve le maximum du tableau
------------- A:
[[1 2 3]
 [4 5 6]]
A.min(): 1
A.max(): 6
print("A.min():",A.min(axis=0)) # trouve le minimum / colonne du tableau 
A.min(): [1 2 3]
print("A.max():",A.max(axis=1)) # trouve le max / ligne du tableau
A.max(): [3 6]
print("A.mean():",A.mean()) # calcule la moyenne
print("A.std():",A.std()) # calcule l'ecart type,
print("A.var():",A.var()) # calcule la variance
A.mean(): 3.5
A.std(): 1.707825127659933
A.var(): 2.9166666666666665
print("A.mean():",A.mean(axis=0)) # calcule la moyenne / colonne
A.mean(): [2.5 3.5 4.5]
print("A.mean():",A.mean(axis=1)) # calcule la moyenne / ligne
A.mean(): [2. 5.]

Une méthode tres importante : la méthode argsort()

v = np.array([2, 9, 1, 0, 8, 7, 3]) # tableau aléatoire
print(v)
[2 9 1 0 8 7 3]
pos = v.argsort()
print(pos) # retourne les index pour trier le vecteur
[3 2 0 6 5 4 1]
vtr = v[pos]
print(vtr)
[0 1 2 3 7 8 9]
import time
import random
nbf = 1_000_000
a = np.array([random.randint(0,10) for i in range(nbf)])
b = a.copy()
deb = time.time()
a.sort()
fin = time.time()

print("temps sort() :",fin-deb)
temps sort() : 0.008445262908935547
deba = time.time()
pos = b.argsort()
vtr = b[pos]
fina = time.time()
print("temps argsort() :",fina-deba)
temps argsort() : 0.036273956298828125
A = np.random.randint(0, 10, [5, 5]) # tableau aléatoire
print(A)
[[0 4 6 2 4]
 [2 3 2 1 7]
 [8 2 6 3 0]
 [6 8 9 5 8]
 [4 1 5 3 3]]
print(A.argsort()) # retourne les index pour trier chaque ligne du tableau 
[[0 3 1 4 2]
 [3 0 2 1 4]
 [4 1 3 2 0]
 [3 0 1 4 2]
 [1 3 4 0 2]]
print(A[:,0].argsort()) # retourne les index pour trier la colonne 0 de A
[0 1 4 3 2]
A = A[A[:,0].argsort(), :] # trie les colonnes du tableau selon la colonne 0.
A
array([[0, 4, 6, 2, 4],
       [2, 3, 2, 1, 7],
       [4, 1, 5, 3, 3],
       [6, 8, 9, 5, 8],
       [8, 2, 6, 3, 0]])
- Statistics

Correlation de Pearson :

import numpy as np
B = np.random.randn(3, 3) # nombres aléatoires 3x3
print("B :\n",B)
B :
 [[-1.50431309 -0.47709981  0.37929429]
 [-1.37681216 -0.60380522  0.9000462 ]
 [ 0.46200969  0.06868264 -0.52351756]]
# retourne la matrice de corrélation de B
print("np.corrcoef(B):\n",np.corrcoef(B))
np.corrcoef(B):
 [[ 1.          0.97238515 -0.98587208]
 [ 0.97238515  1.         -0.99773884]
 [-0.98587208 -0.99773884  1.        ]]
# retourne la matrice de corrélation entre les lignes 0 et 1 ou 2 de B
print(np.corrcoef(B[0,:], B[1,:]))
print(np.corrcoef(B[0,:], B[2,:]))
[[1.         0.97238515]
 [0.97238515 1.        ]]
[[ 1.         -0.98587208]
 [-0.98587208  1.        ]]
# retourne la matrice de corrélation entre les colonnes 0 et 1 de B
print(np.corrcoef(B[:,0], B[:, 1]))
[[1.         0.10031005]
 [0.10031005 1.        ]]

np.unique() :

np.random.seed(0)
A = np.random.randint(0, 50, [5,5])
A
array([[44, 47,  0,  3,  3],
       [39,  9, 19, 21, 36],
       [23,  6, 24, 24, 12],
       [ 1, 38, 39, 23, 46],
       [24, 17, 37, 25, 13]])
np.unique(A)
array([ 0,  1,  3,  6,  9, 12, 13, 17, 19, 21, 23, 24, 25, 36, 37, 38, 39,
       44, 46, 47])
values, counts = np.unique(A, return_counts=True)

for i, j in zip(values[counts.argsort()], counts[counts.argsort()]):
    print(f'valeur {i:2} apparait {j}')
valeur  0 apparait 1
valeur 44 apparait 1
valeur 38 apparait 1
valeur 37 apparait 1
valeur 36 apparait 1
valeur 25 apparait 1
valeur 46 apparait 1
valeur 19 apparait 1
valeur 21 apparait 1
valeur 13 apparait 1
valeur 12 apparait 1
valeur  9 apparait 1
valeur  6 apparait 1
valeur  1 apparait 1
valeur 17 apparait 1
valeur 47 apparait 1
valeur 23 apparait 2
valeur  3 apparait 2
valeur 39 apparait 2
valeur 24 apparait 3

Calculs statistiques en présence de données manquates (NaN)

A = np.random.randn(5, 5)
A[0, 2] = np.nan # insere un NaN dans la matrice A
A[2, 4] = np.nan # insere un NaN dans la matrice A
print(A)
[[ 1.2206081  -1.33949555         nan -0.12346315  1.41437719]
 [-0.12405066  2.00815709  0.22988654  0.60489373  1.62715982]
 [ 1.59456053  0.23043417 -0.06491034 -0.96898025         nan]
 [-0.7827755  -0.44423283 -0.34518616 -0.88180055 -0.44265324]
 [-0.5409163  -1.32322737 -0.11279892  0.90734594  0.81526991]]
print(np.isnan(A))
[[False False  True False False]
 [False False False False False]
 [False False False False  True]
 [False False False False False]
 [False False False False False]]
nb_nan = np.isnan(A).sum()
print(nb_nan)
2
print('ratio NaN/zise:', (nb_nan/A.size)) # calcule la proportion de NaN dans A
ratio NaN/zise: 0.08
print('moyenne :', np.mean(A)) # calcule la moyenne de A
moyenne : nan
B = np.array([[1, 2, np.nan],
 [4, 5, 6]])

print(np.nanmean(B))        # 3.6
print(np.nanmean(B, 0))     # [2.5, 3.5, 6.]
print(np.nanmean(B, 1))     # [1.5, 5.0]
3.6
[2.5 3.5 6. ]
[1.5 5. ]
print('moyenne sans NaN:', np.nanmean(A)) # calcule la moyenne de A en ignorant les NaN
moyenne sans NaN: 0.13731313910742102

Valeurs propres et vecteurs propres

A = np.random.randint(0, 10, [3, 3])
print(A)
[[7 9 9]
 [3 6 7]
 [2 0 3]]
determ = np.linalg.det(A)
print('det =', determ) # calcule le determinant de A
det = 62.99999999999999
determ = np.around(determ)
print(determ)
63.0
# exemple :
import numpy as np
x = 1.1234567891234567
print(np.around(x, decimals=9))  # 1.1234567891
1.123456789
B = np.linalg.inv(A)
print('inv A:\n', B) # calcul l'inverse de A
inv A:
 [[ 0.28571429 -0.42857143  0.14285714]
 [ 0.07936508  0.04761905 -0.34920635]
 [-0.19047619  0.28571429  0.23809524]]
C = np.around(B, decimals=5)
print(C)
[[ 0.28571 -0.42857  0.14286]
 [ 0.07937  0.04762 -0.34921]
 [-0.19048  0.28571  0.2381 ]]
print(10*C[0,0])
2.8571
A = np.array([[4, -2],
              [1, 1]])
print(A)
[[ 4 -2]
 [ 1  1]]
val, vect = np.linalg.eig(A)

print("Valeurs propres :", val)
print("Vecteurs propres :\n", vect)
Valeurs propres : [3. 2.]
Vecteurs propres :
 [[0.89442719 0.70710678]
 [0.4472136  0.70710678]]
numVect = 1
print(A@vect[:,numVect]) 
print(val[numVect]*vect[:,numVect])
[1.41421356 1.41421356]
[1.41421356 1.41421356]

3. Broadcasting:

Technique permetant d’effectuer des opérations entre tableaux de formes différentes, sans copier les données, en étendant automatiquement les dimensions pour qu’elles soient compatibles

import numpy as np
A = np.array([np.arange(5*i, 5*i+3) for i in range(2)])
print(A)
[[0 1 2]
 [5 6 7]]
B = 3
print("A :\n",A)
print("A+B :")
print(A+B) # résultat 1
A :
 [[0 1 2]
 [5 6 7]]
A+B :
[[ 3  4  5]
 [ 8  9 10]]
C = np.array([[3],[4]])

print("A:\n", A)
print("C:\n", C)
print(C.shape)
A:
 [[0 1 2]
 [5 6 7]]
C:
 [[3]
 [4]]
(2, 1)
print("A+C:")
print(A+C)
A+C:
[[ 3  4  5]
 [ 9 10 11]]
L = np.array([13,6,2])
print(A)
print(L)
print(L.shape)
[[0 1 2]
 [5 6 7]]
[13  6  2]
(3,)
print(A+L)
[[13  7  4]
 [18 12  9]]
  • Attention (2,) ≠ (2,1)
z = np.array([[5],[9]])
w = np.array([5, 9])

print(z.shape)
print(w.shape)
(2, 1)
(2,)
print(A+z)
[[ 5  6  7]
 [14 15 16]]
print(A+w)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[36], line 1
----> 1 print(A+w)

ValueError: operands could not be broadcast together with shapes (2,3) (2,) 
  • solution
import numpy as np

a = np.array([1, 2, 3])  # shape = (3,)
a_col = a.reshape(-1, 1)  # shape = (3, 1)
print(a.shape)
print(a_col.shape)

# print(np.eye(3,2)+a_col)
# print(np.eye(3,2)+a)
(3,)
(3, 1)

    Type at least 2 letters. ↑ ↓ to move, Enter to open.