From 01b37c0e49ec0b747bb8edfda0d8688ec24e64fb Mon Sep 17 00:00:00 2001 From: Honaminto Date: Thu, 30 Jul 2026 00:42:17 -0400 Subject: [PATCH 1/4] Update python_by_example.md --- lectures/python_by_example.md | 59 +++++++++++++++++------------------ 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/lectures/python_by_example.md b/lectures/python_by_example.md index 61f885a..b0b4455 100644 --- a/lectures/python_by_example.md +++ b/lectures/python_by_example.md @@ -11,9 +11,9 @@ translation: title: Un exemple introductif headings: Overview: Vue d'ensemble - 'The Task: Plotting a White Noise Process': "La tâche\_: tracer un processus de bruit blanc" + 'The Task: Plotting a White Noise Process': "Objectif\_: simuler et représenter un processus de bruit blanc" Version 1: Version 1 - Version 1::Imports: Importations + Version 1::Imports: Importation de bibliothèques Version 1::Imports::Why So Many Imports?: "Pourquoi autant d'importations\_?" Version 1::Imports::Packages: Packages Version 1::Imports::Subpackages: Sous-packages @@ -45,18 +45,18 @@ translation: ## Vue d'ensemble -Nous sommes maintenant prêts à commencer à apprendre le langage Python lui-même. +Nous sommes maintenant prêts à aborder le langage Python proprement dit. Dans ce cours, nous allons écrire puis décortiquer de petits programmes Python. L'objectif est de vous présenter la syntaxe de base de Python et ses structures de données. -Des concepts plus approfondis seront abordés dans les cours ultérieurs. +Les notions plus avancées seront abordées dans les chapitres suivants. Vous devriez avoir lu le {doc}`cours ` sur la prise en main de Python avant de commencer celui-ci. -## La tâche : tracer un processus de bruit blanc +## Objectif : simuler et représenter un processus de bruit blanc Supposons que nous voulions simuler et tracer le processus de bruit blanc $\epsilon_0, \epsilon_1, \ldots, \epsilon_T$, où chaque tirage $\epsilon_t$ est un tirage indépendant d'une loi normale centrée réduite. @@ -91,12 +91,12 @@ plt.show() Décomposons ce programme et voyons comment il fonctionne. (import)= -### Importations +### Importation de bibliothèques Les deux premières lignes du programme importent des fonctionnalités provenant de bibliothèques de code externes. -La première ligne importe {doc}`NumPy `, un package Python privilégié pour des tâches telles que +La première ligne importe {doc}`NumPy `, une bibliothèque Python très utilisée pour des tâches telles que * le travail avec des tableaux (vecteurs et matrices) * les fonctions mathématiques courantes comme `cos` et `sqrt` @@ -184,12 +184,12 @@ sqrt(4) C'est également correct. -L'avantage est qu'il y a moins à taper si nous utilisons `sqrt` souvent dans notre code. +Cette écriture permet d'alléger le code lorsque la fonction `sqrt` est utilisée fréquemment. L'inconvénient est que, dans un long programme, ces deux lignes pourraient être séparées par de nombreuses autres lignes. -Il est alors plus difficile pour les lecteurs de savoir d'où vient `sqrt`, s'ils le souhaitent. +En revanche, dans un programme long, il peut devenir plus difficile de déterminer de quelle bibliothèque provient `sqrt` ### Tirages aléatoires @@ -207,7 +207,7 @@ dans `ϵ_values`. Les deux lignes suivantes génèrent le graphique. -Nous pouvons et allons examiner ci-dessous diverses façons de configurer et d'améliorer ce graphique. +Nous verrons plus loin différentes façons de configurer et d'améliorer ce graphique. ## Implémentations alternatives @@ -216,7 +216,7 @@ Essayons d'écrire quelques versions alternatives de {ref}`notre premier program Les programmes ci-dessous sont moins efficaces que l'original, et donc quelque peu artificiels. -Mais ils nous aident à illustrer une syntaxe et une sémantique Python importantes dans un cadre familier. +Ils permettent néanmoins d'illustrer, dans un contexte familier, plusieurs éléments importants de la syntaxe et du fonctionnement de Python. ### Une version avec une boucle for @@ -253,7 +253,7 @@ En bref, Considérons l'instruction `ϵ_values = []`, qui crée une liste vide. -Les listes sont une structure de données native de Python utilisée pour regrouper une collection d'objets. +Une liste est une structure de données native de Python qui permet de regrouper plusieurs objets. Les éléments des listes sont ordonnés, et les doublons sont autorisés dans les listes. @@ -279,7 +279,7 @@ x Ici `append()` est ce qu'on appelle une **méthode**, c'est-à-dire une fonction « attachée à » un objet --- dans ce cas, la liste `x`. -Nous apprendrons tout sur les méthodes {doc}`plus tard `, mais juste pour vous donner une idée, +Nous étudierons les méthodes plus en détail dans un {doc}`chapitre ultérieur `. Pour le moment, retenons simplement que : * Les objets Python tels que les listes, les chaînes de caractères, etc. ont tous des méthodes utilisées pour manipuler les données contenues dans l'objet. * Les objets chaînes de caractères ont des [méthodes de chaîne](https://docs.python.org/3/library/stdtypes.html#string-methods), les objets listes ont des [méthodes de liste](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists), etc. @@ -323,9 +323,9 @@ for i in range(ts_length): Python exécute les deux lignes indentées `ts_length` fois avant de passer à la suite. -Ces deux lignes sont appelées un **bloc de code**, car elles constituent le « bloc » de code sur lequel nous bouclons. +Ces deux lignes constituent un **bloc de code**, c'est-à-dire l'ensemble des instructions répétées à chaque itération. -Contrairement à la plupart des autres langages, Python connaît l'étendue du bloc de code *uniquement grâce à l'indentation*. +Contrairement à la plupart des autres langages, Python détermine les limites du bloc de code *uniquement à partir de l'indentation*. Dans notre programme, l'indentation diminue après la ligne `ϵ_values.append(e)`, indiquant à Python que cette ligne marque la limite inférieure du bloc de code. @@ -366,7 +366,7 @@ Ainsi, contrairement à la plupart des autres langages, les espaces blancs dans Une fois que vous vous y êtes habitué, c'est une bonne chose : cela * force une indentation propre et cohérente, améliorant la lisibilité -* supprime l'encombrement, comme les accolades ou les instructions de fin utilisées dans d'autres langages +* évite certains éléments syntaxiques supplémentaires, comme les accolades ou les mots-clés de fin utilisés dans d'autres langages. D'un autre côté, cela demande un peu de soin pour être fait correctement, alors veuillez retenir : @@ -400,15 +400,16 @@ plt.plot(ϵ_values) plt.show() ``` -Une boucle while continuera d'exécuter le bloc de code délimité par l'indentation jusqu'à ce que la condition (```i < ts_length```) soit satisfaite. +Une boucle while exécute le bloc de code délimité par l'indentation tant que la condition (```i < ts_length```) est vraie. + Dans ce cas, le programme continuera d'ajouter des valeurs à la liste ```ϵ_values``` jusqu'à ce que ```i``` soit égal à ```ts_length``` : ```{code-cell} python3 -i == ts_length #la condition de fin de la boucle while +i == ts_length # la condition de fin de la boucle while ``` -Notez que +Notez que * le bloc de code de la boucle `while` est à nouveau délimité uniquement par l'indentation. * l'instruction `i = i + 1` peut être remplacée par `i += 1`. @@ -448,11 +449,10 @@ plt.show() L'instruction `b = np.empty(T+1)` alloue de l'espace mémoire pour `T+1` nombres (à virgule flottante). -Ces nombres sont remplis par la boucle `for`. +Ces nombres sont remplis au moyen de la boucle `for`. Allouer la mémoire au départ est plus efficace que d'utiliser une liste Python et -`append`, car cette dernière doit demander de manière répétée de l'espace de stockage au -système d'exploitation. +`append`, car l'utilisation répétée de cette dernière peut nécessiter de nouvelles allocations de mémoire. Remarquez que nous avons ajouté une légende au graphique --- une fonctionnalité que l'on vous demandera d'utiliser dans les exercices. @@ -484,7 +484,7 @@ import numpy as np import matplotlib.pyplot as plt ``` -Posez $T=200$ et $\alpha = 0.9$. +On pose $T = 200$ et $\alpha = 0.9$. ```{exercise-end} ``` @@ -612,8 +612,7 @@ plt.show() :label: pbe_ex4 ``` -Un aspect important de pratiquement tous les langages de programmation est le branchement et -les conditions. +Les branchements conditionnels constituent un élément essentiel de presque tous les langages de programmation. En Python, les conditions sont généralement implémentées avec la syntaxe if--else. @@ -706,8 +705,8 @@ import numpy as np Vos indices sont les suivants : * Si $U$ est une variable aléatoire uniforme bivariée sur le carré unité $(0, 1)^2$, alors la probabilité que $U$ se trouve dans un sous-ensemble $B$ de $(0,1)^2$ est égale à l'aire de $B$. -* Si $U_1,\ldots,U_n$ sont des copies IID de $U$, alors, à mesure que $n$ devient grand, la fraction qui tombe dans $B$ converge vers la probabilité d'atterrir dans $B$. -* Pour un cercle, $area = \pi * radius^2$. +* Si $U_1,\ldots,U_n$ sont des copies IID de $U$, alors, lorsque $n$ devient suffisamment grand, la fraction qui tombe dans $B$ converge vers la probabilité que U appartienne à $B$. +* Pour un cercle, $aire = \pi * rayon^2$. ``` ```{exercise-end} @@ -731,7 +730,7 @@ $\pi = A / r^2$. Résumé : si nous pouvons estimer l'aire d'un cercle de diamètre 1, alors diviser par $r^2 = (1/2)^2 = 1/4$ donne une estimation de $\pi$. -Nous estimons l'aire en échantillonnant des uniformes bivariées et en regardant la +Nous estimons l'aire en générant des observations d'une loi uniforme bidimensionnelle et en regardant la fraction qui tombe dans le cercle. ```{code-cell} python3 @@ -755,8 +754,8 @@ for i in range(n): area_estimate = count / n -print(area_estimate * 4) # division par radius**2 +print(area_estimate * 4) # division par rayon**2 ``` ```{solution-end} -``` \ No newline at end of file +``` From 796c4423d397860c3d3a9b6e21ff4109d04de4d5 Mon Sep 17 00:00:00 2001 From: Honaminto Date: Thu, 30 Jul 2026 00:46:58 -0400 Subject: [PATCH 2/4] [python_by_example] Corrections de style et de terminologie From 7b74aa91c6256f38bf3f1d38671df7ad0a28b8d6 Mon Sep 17 00:00:00 2001 From: Honaminto Date: Sat, 1 Aug 2026 03:11:54 -0400 Subject: [PATCH 3/4] Update numpy.md --- lectures/numpy.md | 163 ++++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 84 deletions(-) diff --git a/lectures/numpy.md b/lectures/numpy.md index 6768bbd..1d6d25b 100644 --- a/lectures/numpy.md +++ b/lectures/numpy.md @@ -1,4 +1,4 @@ ---- + --- jupytext: text_representation: extension: .md @@ -46,10 +46,10 @@ translation: ``` ```{epigraph} -« Soyons clairs : le travail de la science n'a rien à voir avec le consensus. Le consensus est l'affaire de la politique. La science, au contraire, ne requiert qu'un seul chercheur qui se trouve avoir raison, ce qui signifie qu'il ou elle dispose de résultats vérifiables par référence au monde réel. En science, le consensus n'a aucune importance. Ce qui importe, ce sont des résultats reproductibles. » -- Michael Crichton +« Soyons clairs : le travail de la science n'a rien à voir avec le consensus. Le consensus est l'affaire de la politique. La science, au contraire, ne requiert qu'un seul chercheur qui se trouve avoir raison, ce qui signifie qu'il ou elle dispose de résultats vérifiables par référence au monde réel. En science, le consensus n'a aucune importance. Ce qui importe, ce sont des résultats reproductibles. » -- Michael Crichton ``` -En plus de ce qui est inclus dans Anaconda, ce cours nécessitera les bibliothèques suivantes : +En plus de ce qui est inclus dans Anaconda, ce cours nécessitera les bibliothèques suivantes : ```{code-cell} ipython3 :tags: [hide-output] @@ -59,14 +59,14 @@ En plus de ce qui est inclus dans Anaconda, ce cours nécessitera les bibliothè ## Vue d'ensemble -[NumPy](https://en.wikipedia.org/wiki/NumPy) est une bibliothèque de premier ordre pour la programmation numérique +[NumPy](https://en.wikipedia.org/wiki/NumPy) est une bibliothèque de référence pour le calcul numérique en Python * Largement utilisée dans le monde universitaire, la finance et l'industrie. -* Mature, rapide, stable et en développement continu. +* Mature, rapide, stable et continuellement améliorée. -Nous avons déjà vu du code impliquant NumPy dans les cours précédents. +Nous avons déjà rencontré du code utilisant NumPy dans les chapitres précédents. -Dans ce cours, nous allons entamer une discussion plus systématique de +Dans ce chapitre, nous allons étudier de manière plus systématique 1. les tableaux NumPy et 1. les opérations fondamentales de traitement de tableaux fournies par NumPy. @@ -74,7 +74,7 @@ Dans ce cours, nous allons entamer une discussion plus systématique de (Pour une référence alternative, consultez [la documentation officielle de NumPy](https://numpy.org/doc/stable/reference/).) -Nous utiliserons les importations suivantes. +Nous utiliserons les instructions d'importation suivantes. ```{code-cell} python3 import numpy as np @@ -93,12 +93,12 @@ from matplotlib import cm ```{index} single: NumPy; Arrays ``` -Le problème essentiel que NumPy résout est le traitement rapide de tableaux. +L'un des principaux objectifs de NumPy est de permettre un traitement rapide des tableaux. La structure la plus importante que NumPy définit est un type de données tableau, formellement appelé [numpy.ndarray](https://numpy.org/doc/stable/reference/arrays.ndarray.html). -Les tableaux NumPy alimentent une très grande proportion de l'écosystème scientifique de Python. +Les tableaux NumPy constituent la base d'une grande partie de l'écosystème scientifique Python. ### Notions de base @@ -113,16 +113,16 @@ a type(a) ``` -Les tableaux NumPy ressemblent quelque peu aux listes natives de Python, sauf que +Les tableaux NumPy ressemblent à certains égards aux listes Python, à deux différences importantes près : -* Les données *doivent être homogènes* (tous les éléments du même type). +* Les données *doivent être homogènes* : tous les éléments doivent avoir le même type. * Ces types doivent être l'un des [types de données](https://numpy.org/doc/stable/reference/arrays.dtypes.html) (`dtypes`) fournis par NumPy. -Les plus importants de ces dtypes sont : +Parmi les types de données les plus couramment utilisés, on trouve : -* float64 : nombre à virgule flottante sur 64 bits -* int64 : entier sur 64 bits -* bool : True ou False sur 8 bits +* float64 : nombre à virgule flottante sur 64 bits +* int64 : entier sur 64 bits +* bool : True ou False sur 8 bits Il existe également des dtypes pour représenter les nombres complexes, les entiers non signés, etc. @@ -133,7 +133,7 @@ a = np.zeros(3) type(a[0]) ``` -Si nous voulons utiliser des entiers, nous pouvons le spécifier comme suit : +Si nous voulons utiliser des entiers, nous pouvons le spécifier comme suit : ```{code-cell} python3 a = np.zeros(3, dtype=int) @@ -152,16 +152,16 @@ Considérons l'affectation suivante z = np.zeros(10) ``` -Ici `z` est un tableau **plat** --- ni vecteur ligne ni vecteur colonne. +Ici, `z` est un tableau **unidimensionnel** : ce n'est ni un vecteur ligne ni un vecteur colonne. ```{code-cell} python3 z.shape ``` -Ici, le tuple de forme n'a qu'un seul élément, qui est la longueur du tableau +Ici, le tuple représentant la forme du tableau ne contient qu'un seul élément : sa longueur (les tuples à un seul élément se terminent par une virgule). -Pour lui donner une dimension supplémentaire, nous pouvons modifier l'attribut `shape` +Pour transformer ce tableau en tableau bidimensionnel, nous pouvons modifier son attribut `shape` ```{code-cell} python3 z.shape = (10, 1) # Convertit le tableau plat en vecteur colonne (bidimensionnel) @@ -174,7 +174,7 @@ z.shape = (2, 2) # Tableau bidimensionnel z ``` -Dans le dernier cas, pour créer le tableau 2x2, nous pourrions aussi passer un tuple à la fonction `zeros()`, comme +Dans le dernier exemple, nous aurions également pu créer directement le tableau $2 \times 2$ en passant un tuple à la fonction `zeros()`, comme dans `z = np.zeros((2, 2))`. @@ -189,7 +189,7 @@ Comme nous l'avons vu, la fonction `np.zeros` crée un tableau de zéros. Vous pouvez probablement deviner ce que crée `np.ones`. -Une fonction apparentée est `np.empty`, qui crée des tableaux en mémoire qui peuvent ensuite être remplis de données +La fonction `np.empty` crée un tableau sans initialiser ses valeurs, afin qu'il puisse être rempli ultérieurement ```{code-cell} python3 z = np.empty(3) @@ -200,7 +200,7 @@ Les nombres que vous voyez ici sont des valeurs parasites. (Python alloue 3 morceaux contigus de mémoire de 64 bits, et le contenu existant de ces emplacements mémoire est interprété comme des valeurs `float64`) -Pour établir une grille de nombres régulièrement espacés, utilisez `np.linspace` +Pour générer une suite de nombres régulièrement espacés sur un intervalle, utilisez `np.linspace` ```{code-cell} python3 z = np.linspace(2, 4, 5) # De 2 à 4, avec 5 éléments @@ -234,7 +234,7 @@ z = np.array([[1, 2], [3, 4]]) # Tableau 2D à partir d'une liste de lis z ``` -Voir aussi `np.asarray`, qui remplit une fonction similaire, mais ne fait pas +La fonction `np.asarray` joue un rôle similaire, mais ne fait pas de copie distincte des données déjà présentes dans un tableau NumPy. Pour lire les données d'un tableau à partir d'un fichier texte contenant des données numériques, utilisez `np.loadtxt` --- voir [la documentation](https://numpy.org/doc/stable/reference/routines.io.html) pour plus de détails. @@ -246,7 +246,7 @@ Pour lire les données d'un tableau à partir d'un fichier texte contenant des d ```{index} single: NumPy; Arrays (Indexing) ``` -Pour un tableau plat, l'indexation est la même que pour les séquences Python : +Pour un tableau unidimensionnel, l'indexation fonctionne comme pour les autres séquences Python : ```{code-cell} python3 z = np.linspace(1, 2, 5) @@ -265,7 +265,7 @@ z[0:2] # Deux éléments, en commençant par l'élément 0 z[-1] ``` -Pour les tableaux 2D, la syntaxe d'indexation est la suivante : +Pour les tableaux 2D, la syntaxe d'indexation est la suivante : ```{code-cell} python3 z = np.array([[1, 2], [3, 4]]) @@ -282,7 +282,7 @@ z[0, 1] Et ainsi de suite. -Les colonnes et les lignes peuvent être extraites comme suit +On peut extraire les lignes et les colonnes de la manière suivante ```{code-cell} python3 z[0, :] @@ -292,7 +292,7 @@ z[0, :] z[:, 1] ``` -Les tableaux NumPy d'entiers peuvent aussi être utilisés pour extraire des éléments +Un tableau d'indices entiers peut également servir à sélectionner plusieurs éléments ```{code-cell} python3 z = np.linspace(2, 4, 5) @@ -304,7 +304,7 @@ indices = np.array((0, 2, 3)) z[indices] ``` -Enfin, un tableau de `dtype bool` peut être utilisé pour extraire des éléments +Enfin, un tableau booléen peut être utilisé comme masque pour sélectionner des éléments ```{code-cell} python3 z @@ -321,7 +321,7 @@ z[d] Nous verrons ci-dessous pourquoi cela est utile. -Une remarque en passant : tous les éléments d'un tableau peuvent être fixés à un même nombre en utilisant la notation de tranche +Remarquons également que l'on peut attribuer une même valeur à tous les éléments d'un tableau à l'aide d'une tranche ```{code-cell} python3 z = np.empty(3) @@ -338,7 +338,7 @@ z ```{index} single: NumPy; Arrays (Methods) ``` -Les tableaux disposent de méthodes utiles, toutes soigneusement optimisées +Les tableaux NumPy disposent de nombreuses méthodes utiles et optimisées ```{code-cell} python3 a = np.array((4, 3, 2, 1)) @@ -387,7 +387,7 @@ a.shape = (2, 2) a.T # Équivalent à a.transpose() ``` -Une autre méthode qui vaut la peine d'être connue est `searchsorted()`. +Une autre méthode utile à connaître est `searchsorted()`. Si `z` est un tableau non décroissant, alors `z.searchsorted(a)` renvoie l'indice du premier élément de `z` qui est `>= a` @@ -425,7 +425,7 @@ Nous pouvons ajouter un scalaire à chaque élément comme suit a + 10 ``` -La multiplication scalaire est similaire +La multiplication par un scalaire fonctionne de la même manière ```{code-cell} python3 a * 10 @@ -456,10 +456,7 @@ En particulier, `A * B` n'est *pas* le produit matriciel, c'est un produit élé ```{index} single: NumPy; Matrix Multiplication ``` -```{index} single: NumPy; Matrix Multiplication -``` - -Nous utilisons le symbole `@` pour la multiplication matricielle, comme suit : +Nous utilisons le symbole `@` pour la multiplication matricielle, comme suit : ```{code-cell} python3 A = np.ones((2, 2)) @@ -468,7 +465,7 @@ A @ B ``` La syntaxe fonctionne avec des tableaux plats --- NumPy devine intelligemment ce que vous -voulez : +voulez : ```{code-cell} python3 A @ (0, 1) @@ -487,10 +484,10 @@ Comme nous effectuons une post-multiplication, le tuple est traité comme un vec (Cette section prolonge une excellente discussion du broadcasting fournie par [Jake VanderPlas](https://jakevdp.github.io/PythonDataScienceHandbook/02.05-computation-on-arrays-broadcasting.html).) ```{note} -Le broadcasting est un aspect très important de NumPy. En même temps, le broadcasting avancé est relativement complexe et certains des détails ci-dessous peuvent être survolés en première lecture. +Le broadcasting est un aspect très important de NumPy. Cependant, les formes avancées de broadcasting sont relativement complexes et certains des détails ci-dessous peuvent être survolés en première lecture. ``` -Dans les opérations élément par élément, les tableaux peuvent ne pas avoir la même forme. +Lors d'une opération élément par élément, les tableaux n'ont pas nécessairement la même forme. Lorsque cela se produit, NumPy étendra automatiquement les tableaux à la même forme chaque fois que possible. @@ -518,7 +515,7 @@ b = np.array([3, 6, 9]) a + b ``` -Voici une représentation visuelle de cette opération de broadcasting : +Voici une représentation visuelle de cette opération de broadcasting : ```{code-cell} python3 --- @@ -635,7 +632,7 @@ ax.text(5, 7.0, '+', size=12, ha='center', va='center') ax.text(10.5, 7.0, '=', size=12, ha='center', va='center'); ``` -Qu'en est-il de `b -> (3, 1)` ? +Qu'en est-il de `b -> (3, 1)` ? Dans ce cas, NumPy étendra automatiquement `b -> (3, 1)` en `b -> (3, 3)`. @@ -647,7 +644,7 @@ b.shape = (3, 1) a + b ``` -Voici une représentation visuelle de cette opération de broadcasting : +Voici une représentation visuelle de cette opération de broadcasting : ```{code-cell} python3 --- @@ -716,7 +713,7 @@ b.shape = (3, 1) a + b ``` -Voici une représentation visuelle de cette opération de broadcasting : +Voici une représentation visuelle de cette opération de broadcasting : ```{code-cell} python3 --- @@ -790,7 +787,7 @@ a + b La `ValueError` nous indique que les opérandes n'ont pas pu être broadcastées ensemble. -Voici une représentation visuelle pour montrer pourquoi ce broadcasting ne peut pas être exécuté : +La représentation suivante montre pourquoi cette opération de broadcasting est impossible : ```{code-cell} python3 --- @@ -835,22 +832,22 @@ C'est parce que, lorsque `b` est étendu de `b -> (3,)` à `b -> (3, 3)`, NumPy Les choses deviennent encore plus délicates lorsque nous passons à des dimensions supérieures. -Pour nous aider, nous pouvons utiliser la liste de règles suivante : +Pour nous aider, nous pouvons utiliser la liste de règles suivante : * *Étape 1 :* Lorsque les dimensions de deux tableaux ne correspondent pas, NumPy étend celui qui a le moins de dimensions en ajoutant une ou plusieurs dimensions à gauche des dimensions existantes. - - Par exemple, si `a -> (3, 3)` et `b -> (3,)`, alors le broadcasting ajoutera une dimension à gauche de sorte que `b -> (1, 3)` ; - - Si `a -> (2, 2, 2)` et `b -> (2, 2)`, alors le broadcasting ajoutera une dimension à gauche de sorte que `b -> (1, 2, 2)` ; + - Par exemple, si `a -> (3, 3)` et `b -> (3,)`, alors le broadcasting ajoutera une dimension à gauche de sorte que `b -> (1, 3)` ; + - Si `a -> (2, 2, 2)` et `b -> (2, 2)`, alors le broadcasting ajoutera une dimension à gauche de sorte que `b -> (1, 2, 2)` ; - Si `a -> (3, 2, 2)` et `b -> (2,)`, alors le broadcasting ajoutera deux dimensions à gauche de sorte que `b -> (1, 1, 2)` (on peut aussi voir ce processus comme un passage par l'*Étape 1* deux fois). * *Étape 2 :* Lorsque les deux tableaux ont la même dimension mais des formes différentes, NumPy tentera d'étendre les dimensions où l'indice de forme est 1. - - Par exemple, si `a -> (1, 3)` et `b -> (3, 1)`, alors le broadcasting étendra les dimensions de forme 1 dans `a` et `b` de sorte que `a -> (3, 3)` et `b -> (3, 3)` ; - - Si `a -> (2, 2, 2)` et `b -> (1, 2, 2)`, alors le broadcasting étendra la première dimension de `b` de sorte que `b -> (2, 2, 2)` ; + - Par exemple, si `a -> (1, 3)` et `b -> (3, 1)`, alors le broadcasting étendra les dimensions de forme 1 dans `a` et `b` de sorte que `a -> (3, 3)` et `b -> (3, 3)` ; + - Si `a -> (2, 2, 2)` et `b -> (1, 2, 2)`, alors le broadcasting étendra la première dimension de `b` de sorte que `b -> (2, 2, 2)` ; - Si `a -> (3, 2, 2)` et `b -> (1, 1, 2)`, alors le broadcasting étendra `b` sur toutes les dimensions de forme 1 de sorte que `b -> (3, 2, 2)`. * *Étape 3 :* Après les étapes 1 et 2, si les deux tableaux ne correspondent toujours pas, une `ValueError` sera levée. Par exemple, supposons `a -> (2, 2, 3)` et `b -> (2, 2)` - - Par l'*Étape 1*, `b` sera étendu en `b -> (1, 2, 2)` ; - - Par l'*Étape 2*, `b` sera étendu en `b -> (2, 2, 2)` ; + - Par l'*Étape 1*, `b` sera étendu en `b -> (1, 2, 2)` ; + - Par l'*Étape 2*, `b` sera étendu en `b -> (2, 2, 2)` ; - Nous pouvons voir qu'ils ne correspondent pas l'un à l'autre après les deux premières étapes. Ainsi, une `ValueError` sera levée @@ -859,10 +856,10 @@ Pour nous aider, nous pouvons utiliser la liste de règles suivante : Les tableaux NumPy sont des types de données mutables, comme les listes Python. -En d'autres termes, leur contenu peut être modifié (muté) en mémoire après l'initialisation. +En d'autres termes, leur contenu peut être modifié en mémoire après l'initialisation. C'est pratique mais, combiné au modèle de nommage et de référence de Python, -cela peut conduire à des erreurs chez les débutants en NumPy. +cela peut être source d'erreurs pour les personnes qui découvrent NumPy. Dans cette section, nous passons en revue quelques points clés. @@ -883,7 +880,7 @@ a[-1] = 0 # Change le dernier élément en 0 a ``` -La mutabilité conduit au comportement suivant (qui peut être choquant pour les programmeurs MATLAB...) +La mutabilité entraîne le comportement suivant, qui peut surprendre les utilisateurs de MATLAB. ```{code-cell} python3 rng = np.random.default_rng() @@ -902,9 +899,9 @@ Ce qui s'est passé, c'est que nous avons modifié `a` en modifiant `b`. Le nom `b` est lié à `a` et devient simplement une autre référence au tableau (le modèle d'affectation de Python est décrit plus en détail {doc}`plus loin dans le cours `). -Il a donc des droits égaux pour apporter des modifications à ce tableau. +Toute modification effectuée par l'intermédiaire de `b` affecte donc également le tableau accessible par `a`. -C'est en fait le comportement par défaut le plus sensé ! +Ce comportement est généralement préférable, car il évite les copies inutiles. Cela signifie que nous ne faisons circuler que des pointeurs vers les données, plutôt que de faire des copies. @@ -959,7 +956,7 @@ z = np.array([1, 2, 3]) np.sin(z) ``` -Cela élimine le besoin de boucles explicites élément par élément telles que +Cela évite d'écrire explicitement une boucle parcourant les éléments un à un, comme dans l'exemple suivant ```{code-cell} python3 n = len(z) @@ -973,7 +970,7 @@ Parce qu'elles agissent élément par élément sur les tableaux, ces fonctions Dans le jargon de NumPy, elles sont aussi appelées **ufuncs**, ou **fonctions universelles**. Comme nous l'avons vu ci-dessus, les opérations arithmétiques usuelles (`+`, `*`, etc.) fonctionnent -également élément par élément, et combiner celles-ci avec les ufuncs donne un très large ensemble de fonctions rapides opérant élément par élément. +également élément par élément. En combinant ces opérations aux ufuncs, on dispose d'un vaste ensemble d'opérations vectorisées et efficaces. ```{code-cell} python3 z @@ -983,7 +980,7 @@ z (1 / np.sqrt(2 * np.pi)) * np.exp(- 0.5 * z**2) ``` -Toutes les fonctions définies par l'utilisateur n'agissent pas élément par élément. +Les fonctions définies par l'utilisateur ne sont pas nécessairement vectorisées. Par exemple, passer un tableau NumPy à la fonction `f` définie ci-dessous provoque une `ValueError` @@ -992,7 +989,7 @@ def f(x): return 1 if x > 0 else 0 ``` -La fonction NumPy `np.where` fournit une alternative vectorisée : +La fonction NumPy `np.where` fournit une alternative vectorisée : ```{code-cell} python3 x = rng.standard_normal(4) @@ -1061,7 +1058,7 @@ b z[b] ``` -Bien sûr, nous pouvons --- et faisons souvent --- effectuer ceci en une seule étape +En pratique, cette sélection est souvent réalisée en une seule étape ```{code-cell} python3 z[z > 3] @@ -1099,7 +1096,7 @@ np.linalg.inv(A) # Calcule l'inverse ```{index} single: Python; SciPy ``` -Une grande partie de ces fonctionnalités est aussi disponible dans [SciPy](https://scipy.org/), une collection de modules qui sont construits par-dessus NumPy. +Une grande partie de ces fonctionnalités est aussi disponible dans [SciPy](https://scipy.org/), une collection de modules reposant sur NumPy. Nous couvrirons les versions SciPy plus en détail {doc}`bientôt `. @@ -1112,7 +1109,7 @@ Pour une liste complète de ce qui est disponible dans NumPy, consultez [cette d NumPy tente d'implémenter le multithreading dans une grande partie de son code compilé. -Examinons un exemple pour voir cela en action. +Observons ce comportement à l'aide d'un exemple. Le morceau de code suivant calcule les valeurs propres d'un grand nombre de matrices générées aléatoirement. @@ -1128,7 +1125,7 @@ for i in range(n): ``` Maintenant, examinons la sortie du moniteur système htop sur notre machine pendant que -ce code s'exécute : +ce code s'exécute : ```{figure} /_static/lecture_specific/parallelization/htop_parallel_npmat.png :scale: 80 @@ -1160,7 +1157,7 @@ p(x) = a_0 + a_1 x + a_2 x^2 + \cdots a_N x^N = \sum_{n=0}^N a_n x^n {ref}`Plus tôt `, vous avez écrit une fonction simple `p(x, coeff)` pour évaluer {eq}`np_polynom` sans tenir compte de l'efficacité. -Écrivez maintenant une nouvelle fonction qui effectue le même travail, mais qui utilise des tableaux NumPy et des opérations sur tableaux pour ses calculs, plutôt qu'une quelconque forme de boucle Python. +Écrivez maintenant une nouvelle fonction qui effectue le même travail, mais qui utilise des tableaux NumPy et des opérations sur tableaux pour ses calculs, sans utiliser de boucle Python explicite. (Une telle fonctionnalité est déjà implémentée sous la forme de `np.poly1d`, mais pour les besoins de l'exercice, n'utilisez pas cette classe) @@ -1175,7 +1172,7 @@ Utilisez `np.cumprod()` :class: dropdown ``` -Ce code fait le travail +La fonction suivante répond à la question ```{code-cell} python3 def p(x, coef): @@ -1213,12 +1210,12 @@ Nous souhaitons générer une variable aléatoire discrète $x$ telle que $\math En d'autres termes, `x` prend des valeurs dans `range(len(q))` et `x = i` avec probabilité `q[i]`. -L'algorithme standard (de transformation inverse) est le suivant : +L'algorithme standard (de transformation inverse) est le suivant : * Divisez l'intervalle unité $[0, 1]$ en $n$ sous-intervalles $I_0, I_1, \ldots, I_{n-1}$ tels que la longueur de $I_i$ soit $q_i$. -* Tirez une variable aléatoire uniforme $U$ sur $[0, 1]$ et renvoyez le $i$ tel que $U \in I_i$. +* Effectuez un tirage $U$ selon la loi uniforme sur $[0, 1]$ et renvoyez le $i$ tel que $U \in I_i$. -La probabilité de tirer $i$ est la longueur de $I_i$, qui est égale à $q_i$. +La probabilité d'obtenir la valeur $i$ est égale à la longueur de l'intervalle $I_i$, qui est égale à $q_i$. Nous pouvons implémenter l'algorithme comme suit @@ -1234,10 +1231,10 @@ def sample(q): a = a + q[i] ``` -Si vous ne voyez pas comment cela fonctionne, essayez de suivre le flux pour un exemple simple, tel que `q = [0.25, 0.75]` +Si vous ne voyez pas comment cela fonctionne, essayez de suivre pas à pas l'exécution de l'algorithme sur un exemple simple, tel que `q = [0.25, 0.75]` Il est utile de dessiner les intervalles sur papier. -Votre exercice consiste à l'accélérer en utilisant NumPy, en évitant les boucles explicites +L'objectif de l'exercice est d'en proposer une version plus rapide à l'aide de NumPy, en évitant les boucles explicites ```{hint} :class: dropdown @@ -1260,7 +1257,7 @@ Si vous le pouvez, écrivez la méthode de sorte que `draw(k)` renvoie `k` tirag :class: dropdown ``` -Voici notre première tentative de solution : +Voici notre première tentative de solution : ```{code-cell} python3 from numpy import cumsum @@ -1288,8 +1285,7 @@ class DiscreteRV: return self.Q.searchsorted(self.rng.uniform(0, 1, size=k)) ``` -La logique n'est pas évidente, mais si vous prenez votre temps et la lisez lentement, -vous comprendrez. +La logique peut sembler difficile au premier abord. Prenez le temps d'examiner chaque étape du code. Il y a cependant un problème ici. @@ -1304,12 +1300,11 @@ d.q = (0.5, 0.5) Le problème est que `Q` ne change pas en conséquence, et `Q` correspond aux données utilisées dans la méthode `draw`. -Pour gérer cela, une option consiste à calculer `Q` chaque fois que la méthode draw -est appelée. +Une première solution consisterait à recalculer `Q` à chaque appel de la méthode `draw`. Mais c'est inefficace par rapport à un calcul unique de `Q`. -Une meilleure option consiste à utiliser des descripteurs. +Une solution plus efficace consiste à utiliser un descripteur Python. Une solution de la [bibliothèque quantecon](https://github.com/QuantEcon/QuantEcon.py/tree/main/quantecon) utilisant des descripteurs et se comportant comme nous le souhaitons peut être trouvée @@ -1324,7 +1319,7 @@ utilisant des descripteurs et se comportant comme nous le souhaitons peut être Rappelez-vous notre {ref}`discussion précédente ` sur la fonction de répartition empirique. -Votre tâche consiste à +L'objectif de cet exercice est de 1. Rendre la méthode `__call__` plus efficace en utilisant NumPy. 1. Ajouter une méthode qui trace la FDE sur $[a, b]$, où $a$ et $b$ sont des paramètres de la méthode. @@ -1336,8 +1331,8 @@ Votre tâche consiste à Un exemple de solution est donné ci-dessous. -En substance, nous avons simplement repris [ce code](https://github.com/QuantEcon/QuantEcon.py/blob/main/quantecon/ecdf.py) -de QuantEcon et ajouté une méthode de tracé +La solution ci-dessous adapte [ce code](https://github.com/QuantEcon/QuantEcon.py/blob/main/quantecon/ecdf.py) +de QuantEcon en lui ajoutant une méthode de tracé ```{code-cell} python3 """ @@ -1430,7 +1425,7 @@ Rappelez-vous que le [broadcasting](broadcasting) dans NumPy peut nous aider à Dans cet exercice, essayez d'utiliser des boucles `for` pour reproduire le résultat des opérations de broadcasting suivantes. -**Partie 1** : Essayez de reproduire cet exemple simple en utilisant des boucles `for` et comparez vos résultats avec l'opération de broadcasting ci-dessous. +**Partie 1** : Essayez de reproduire cet exemple simple en utilisant des boucles `for` et comparez vos résultats avec l'opération de broadcasting ci-dessous. ```{code-cell} python3 @@ -1449,7 +1444,7 @@ tags: [hide-output] print(A) ``` -**Partie 2** : Passez à la reproduction du résultat de l'opération de broadcasting suivante. Parallèlement, comparez les vitesses du broadcasting et de la boucle `for` que vous implémentez. +**Partie 2** : Passez à la reproduction du résultat de l'opération de broadcasting suivante. Parallèlement, comparez les vitesses du broadcasting et de la boucle `for` que vous implémentez. Pour cette partie de l'exercice, vous pouvez utiliser les fonctions `tic`/`toc` de la bibliothèque `quantecon` pour chronométrer l'exécution. @@ -1552,4 +1547,4 @@ print(np.array_equal(B, D)) ``` ```{solution-end} -``` \ No newline at end of file +``` From 164c9fb205d87d89952426eee56fcad8ecc04784 Mon Sep 17 00:00:00 2001 From: Honaminto Date: Sat, 1 Aug 2026 04:25:17 -0400 Subject: [PATCH 4/4] main.md --- lectures/numpy.md | 161 ++++++++++++++++++++++++---------------------- 1 file changed, 83 insertions(+), 78 deletions(-) diff --git a/lectures/numpy.md b/lectures/numpy.md index 1d6d25b..b6c5be5 100644 --- a/lectures/numpy.md +++ b/lectures/numpy.md @@ -1,4 +1,4 @@ - --- +--- jupytext: text_representation: extension: .md @@ -46,10 +46,10 @@ translation: ``` ```{epigraph} -« Soyons clairs : le travail de la science n'a rien à voir avec le consensus. Le consensus est l'affaire de la politique. La science, au contraire, ne requiert qu'un seul chercheur qui se trouve avoir raison, ce qui signifie qu'il ou elle dispose de résultats vérifiables par référence au monde réel. En science, le consensus n'a aucune importance. Ce qui importe, ce sont des résultats reproductibles. » -- Michael Crichton +« Soyons clairs : le travail de la science n'a rien à voir avec le consensus. Le consensus est l'affaire de la politique. La science, au contraire, ne requiert qu'un seul chercheur qui se trouve avoir raison, ce qui signifie qu'il ou elle dispose de résultats vérifiables par référence au monde réel. En science, le consensus n'a aucune importance. Ce qui importe, ce sont des résultats reproductibles. » -- Michael Crichton ``` -En plus de ce qui est inclus dans Anaconda, ce cours nécessitera les bibliothèques suivantes : +En plus de ce qui est inclus dans Anaconda, ce cours nécessitera les bibliothèques suivantes : ```{code-cell} ipython3 :tags: [hide-output] @@ -59,14 +59,14 @@ En plus de ce qui est inclus dans Anaconda, ce cours nécessitera les bibliothè ## Vue d'ensemble -[NumPy](https://en.wikipedia.org/wiki/NumPy) est une bibliothèque de référence pour le calcul numérique en Python +[NumPy](https://en.wikipedia.org/wiki/NumPy) est une bibliothèque de premier ordre pour la programmation numérique * Largement utilisée dans le monde universitaire, la finance et l'industrie. -* Mature, rapide, stable et continuellement améliorée. +* Mature, rapide, stable et en développement continu. -Nous avons déjà rencontré du code utilisant NumPy dans les chapitres précédents. +Nous avons déjà vu du code impliquant NumPy dans les cours précédents. -Dans ce chapitre, nous allons étudier de manière plus systématique +Dans ce cours, nous allons entamer une discussion plus systématique de 1. les tableaux NumPy et 1. les opérations fondamentales de traitement de tableaux fournies par NumPy. @@ -74,7 +74,7 @@ Dans ce chapitre, nous allons étudier de manière plus systématique (Pour une référence alternative, consultez [la documentation officielle de NumPy](https://numpy.org/doc/stable/reference/).) -Nous utiliserons les instructions d'importation suivantes. +Nous utiliserons les importations suivantes. ```{code-cell} python3 import numpy as np @@ -93,12 +93,12 @@ from matplotlib import cm ```{index} single: NumPy; Arrays ``` -L'un des principaux objectifs de NumPy est de permettre un traitement rapide des tableaux. +Le problème essentiel que NumPy résout est le traitement rapide de tableaux. La structure la plus importante que NumPy définit est un type de données tableau, formellement appelé [numpy.ndarray](https://numpy.org/doc/stable/reference/arrays.ndarray.html). -Les tableaux NumPy constituent la base d'une grande partie de l'écosystème scientifique Python. +Les tableaux NumPy alimentent une très grande proportion de l'écosystème scientifique de Python. ### Notions de base @@ -113,16 +113,16 @@ a type(a) ``` -Les tableaux NumPy ressemblent à certains égards aux listes Python, à deux différences importantes près : +Les tableaux NumPy ressemblent quelque peu aux listes natives de Python, sauf que -* Les données *doivent être homogènes* : tous les éléments doivent avoir le même type. +* Les données *doivent être homogènes* (tous les éléments du même type). * Ces types doivent être l'un des [types de données](https://numpy.org/doc/stable/reference/arrays.dtypes.html) (`dtypes`) fournis par NumPy. -Parmi les types de données les plus couramment utilisés, on trouve : +Les plus importants de ces dtypes sont : -* float64 : nombre à virgule flottante sur 64 bits -* int64 : entier sur 64 bits -* bool : True ou False sur 8 bits +* float64 : nombre à virgule flottante sur 64 bits +* int64 : entier sur 64 bits +* bool : True ou False sur 8 bits Il existe également des dtypes pour représenter les nombres complexes, les entiers non signés, etc. @@ -133,7 +133,7 @@ a = np.zeros(3) type(a[0]) ``` -Si nous voulons utiliser des entiers, nous pouvons le spécifier comme suit : +Si nous voulons utiliser des entiers, nous pouvons le spécifier comme suit : ```{code-cell} python3 a = np.zeros(3, dtype=int) @@ -152,16 +152,16 @@ Considérons l'affectation suivante z = np.zeros(10) ``` -Ici, `z` est un tableau **unidimensionnel** : ce n'est ni un vecteur ligne ni un vecteur colonne. +Ici `z` est un tableau **plat** --- ni vecteur ligne ni vecteur colonne. ```{code-cell} python3 z.shape ``` -Ici, le tuple représentant la forme du tableau ne contient qu'un seul élément : sa longueur +Ici, le tuple de forme n'a qu'un seul élément, qui est la longueur du tableau (les tuples à un seul élément se terminent par une virgule). -Pour transformer ce tableau en tableau bidimensionnel, nous pouvons modifier son attribut `shape` +Pour lui donner une dimension supplémentaire, nous pouvons modifier l'attribut `shape` ```{code-cell} python3 z.shape = (10, 1) # Convertit le tableau plat en vecteur colonne (bidimensionnel) @@ -174,7 +174,7 @@ z.shape = (2, 2) # Tableau bidimensionnel z ``` -Dans le dernier exemple, nous aurions également pu créer directement le tableau $2 \times 2$ en passant un tuple à la fonction `zeros()`, comme +Dans le dernier cas, pour créer le tableau 2x2, nous pourrions aussi passer un tuple à la fonction `zeros()`, comme dans `z = np.zeros((2, 2))`. @@ -189,7 +189,7 @@ Comme nous l'avons vu, la fonction `np.zeros` crée un tableau de zéros. Vous pouvez probablement deviner ce que crée `np.ones`. -La fonction `np.empty` crée un tableau sans initialiser ses valeurs, afin qu'il puisse être rempli ultérieurement +Une fonction apparentée est `np.empty`, qui crée des tableaux en mémoire qui peuvent ensuite être remplis de données ```{code-cell} python3 z = np.empty(3) @@ -200,7 +200,7 @@ Les nombres que vous voyez ici sont des valeurs parasites. (Python alloue 3 morceaux contigus de mémoire de 64 bits, et le contenu existant de ces emplacements mémoire est interprété comme des valeurs `float64`) -Pour générer une suite de nombres régulièrement espacés sur un intervalle, utilisez `np.linspace` +Pour établir une grille de nombres régulièrement espacés, utilisez `np.linspace` ```{code-cell} python3 z = np.linspace(2, 4, 5) # De 2 à 4, avec 5 éléments @@ -234,7 +234,7 @@ z = np.array([[1, 2], [3, 4]]) # Tableau 2D à partir d'une liste de lis z ``` -La fonction `np.asarray` joue un rôle similaire, mais ne fait pas +Voir aussi `np.asarray`, qui remplit une fonction similaire, mais ne fait pas de copie distincte des données déjà présentes dans un tableau NumPy. Pour lire les données d'un tableau à partir d'un fichier texte contenant des données numériques, utilisez `np.loadtxt` --- voir [la documentation](https://numpy.org/doc/stable/reference/routines.io.html) pour plus de détails. @@ -246,7 +246,7 @@ Pour lire les données d'un tableau à partir d'un fichier texte contenant des d ```{index} single: NumPy; Arrays (Indexing) ``` -Pour un tableau unidimensionnel, l'indexation fonctionne comme pour les autres séquences Python : +Pour un tableau plat, l'indexation est la même que pour les séquences Python : ```{code-cell} python3 z = np.linspace(1, 2, 5) @@ -265,7 +265,7 @@ z[0:2] # Deux éléments, en commençant par l'élément 0 z[-1] ``` -Pour les tableaux 2D, la syntaxe d'indexation est la suivante : +Pour les tableaux 2D, la syntaxe d'indexation est la suivante : ```{code-cell} python3 z = np.array([[1, 2], [3, 4]]) @@ -282,7 +282,7 @@ z[0, 1] Et ainsi de suite. -On peut extraire les lignes et les colonnes de la manière suivante +Les colonnes et les lignes peuvent être extraites comme suit ```{code-cell} python3 z[0, :] @@ -292,7 +292,7 @@ z[0, :] z[:, 1] ``` -Un tableau d'indices entiers peut également servir à sélectionner plusieurs éléments +Les tableaux NumPy d'entiers peuvent aussi être utilisés pour extraire des éléments ```{code-cell} python3 z = np.linspace(2, 4, 5) @@ -304,7 +304,7 @@ indices = np.array((0, 2, 3)) z[indices] ``` -Enfin, un tableau booléen peut être utilisé comme masque pour sélectionner des éléments +Enfin, un tableau de `dtype bool` peut être utilisé pour extraire des éléments ```{code-cell} python3 z @@ -321,7 +321,7 @@ z[d] Nous verrons ci-dessous pourquoi cela est utile. -Remarquons également que l'on peut attribuer une même valeur à tous les éléments d'un tableau à l'aide d'une tranche +Une remarque en passant : tous les éléments d'un tableau peuvent être fixés à un même nombre en utilisant la notation de tranche ```{code-cell} python3 z = np.empty(3) @@ -338,7 +338,7 @@ z ```{index} single: NumPy; Arrays (Methods) ``` -Les tableaux NumPy disposent de nombreuses méthodes utiles et optimisées +Les tableaux disposent de méthodes utiles, toutes soigneusement optimisées ```{code-cell} python3 a = np.array((4, 3, 2, 1)) @@ -387,7 +387,7 @@ a.shape = (2, 2) a.T # Équivalent à a.transpose() ``` -Une autre méthode utile à connaître est `searchsorted()`. +Une autre méthode qui vaut la peine d'être connue est `searchsorted()`. Si `z` est un tableau non décroissant, alors `z.searchsorted(a)` renvoie l'indice du premier élément de `z` qui est `>= a` @@ -425,7 +425,7 @@ Nous pouvons ajouter un scalaire à chaque élément comme suit a + 10 ``` -La multiplication par un scalaire fonctionne de la même manière +La multiplication scalaire est similaire ```{code-cell} python3 a * 10 @@ -456,7 +456,10 @@ En particulier, `A * B` n'est *pas* le produit matriciel, c'est un produit élé ```{index} single: NumPy; Matrix Multiplication ``` -Nous utilisons le symbole `@` pour la multiplication matricielle, comme suit : +```{index} single: NumPy; Matrix Multiplication +``` + +Nous utilisons le symbole `@` pour la multiplication matricielle, comme suit : ```{code-cell} python3 A = np.ones((2, 2)) @@ -465,7 +468,7 @@ A @ B ``` La syntaxe fonctionne avec des tableaux plats --- NumPy devine intelligemment ce que vous -voulez : +voulez : ```{code-cell} python3 A @ (0, 1) @@ -484,10 +487,10 @@ Comme nous effectuons une post-multiplication, le tuple est traité comme un vec (Cette section prolonge une excellente discussion du broadcasting fournie par [Jake VanderPlas](https://jakevdp.github.io/PythonDataScienceHandbook/02.05-computation-on-arrays-broadcasting.html).) ```{note} -Le broadcasting est un aspect très important de NumPy. Cependant, les formes avancées de broadcasting sont relativement complexes et certains des détails ci-dessous peuvent être survolés en première lecture. +Le broadcasting est un aspect très important de NumPy. En même temps, le broadcasting avancé est relativement complexe et certains des détails ci-dessous peuvent être survolés en première lecture. ``` -Lors d'une opération élément par élément, les tableaux n'ont pas nécessairement la même forme. +Dans les opérations élément par élément, les tableaux peuvent ne pas avoir la même forme. Lorsque cela se produit, NumPy étendra automatiquement les tableaux à la même forme chaque fois que possible. @@ -515,7 +518,7 @@ b = np.array([3, 6, 9]) a + b ``` -Voici une représentation visuelle de cette opération de broadcasting : +Voici une représentation visuelle de cette opération de broadcasting : ```{code-cell} python3 --- @@ -632,7 +635,7 @@ ax.text(5, 7.0, '+', size=12, ha='center', va='center') ax.text(10.5, 7.0, '=', size=12, ha='center', va='center'); ``` -Qu'en est-il de `b -> (3, 1)` ? +Qu'en est-il de `b -> (3, 1)` ? Dans ce cas, NumPy étendra automatiquement `b -> (3, 1)` en `b -> (3, 3)`. @@ -644,7 +647,7 @@ b.shape = (3, 1) a + b ``` -Voici une représentation visuelle de cette opération de broadcasting : +Voici une représentation visuelle de cette opération de broadcasting : ```{code-cell} python3 --- @@ -713,7 +716,7 @@ b.shape = (3, 1) a + b ``` -Voici une représentation visuelle de cette opération de broadcasting : +Voici une représentation visuelle de cette opération de broadcasting : ```{code-cell} python3 --- @@ -787,7 +790,7 @@ a + b La `ValueError` nous indique que les opérandes n'ont pas pu être broadcastées ensemble. -La représentation suivante montre pourquoi cette opération de broadcasting est impossible : +Voici une représentation visuelle pour montrer pourquoi ce broadcasting ne peut pas être exécuté : ```{code-cell} python3 --- @@ -832,22 +835,22 @@ C'est parce que, lorsque `b` est étendu de `b -> (3,)` à `b -> (3, 3)`, NumPy Les choses deviennent encore plus délicates lorsque nous passons à des dimensions supérieures. -Pour nous aider, nous pouvons utiliser la liste de règles suivante : +Pour nous aider, nous pouvons utiliser la liste de règles suivante : * *Étape 1 :* Lorsque les dimensions de deux tableaux ne correspondent pas, NumPy étend celui qui a le moins de dimensions en ajoutant une ou plusieurs dimensions à gauche des dimensions existantes. - - Par exemple, si `a -> (3, 3)` et `b -> (3,)`, alors le broadcasting ajoutera une dimension à gauche de sorte que `b -> (1, 3)` ; - - Si `a -> (2, 2, 2)` et `b -> (2, 2)`, alors le broadcasting ajoutera une dimension à gauche de sorte que `b -> (1, 2, 2)` ; + - Par exemple, si `a -> (3, 3)` et `b -> (3,)`, alors le broadcasting ajoutera une dimension à gauche de sorte que `b -> (1, 3)` ; + - Si `a -> (2, 2, 2)` et `b -> (2, 2)`, alors le broadcasting ajoutera une dimension à gauche de sorte que `b -> (1, 2, 2)` ; - Si `a -> (3, 2, 2)` et `b -> (2,)`, alors le broadcasting ajoutera deux dimensions à gauche de sorte que `b -> (1, 1, 2)` (on peut aussi voir ce processus comme un passage par l'*Étape 1* deux fois). * *Étape 2 :* Lorsque les deux tableaux ont la même dimension mais des formes différentes, NumPy tentera d'étendre les dimensions où l'indice de forme est 1. - - Par exemple, si `a -> (1, 3)` et `b -> (3, 1)`, alors le broadcasting étendra les dimensions de forme 1 dans `a` et `b` de sorte que `a -> (3, 3)` et `b -> (3, 3)` ; - - Si `a -> (2, 2, 2)` et `b -> (1, 2, 2)`, alors le broadcasting étendra la première dimension de `b` de sorte que `b -> (2, 2, 2)` ; + - Par exemple, si `a -> (1, 3)` et `b -> (3, 1)`, alors le broadcasting étendra les dimensions de forme 1 dans `a` et `b` de sorte que `a -> (3, 3)` et `b -> (3, 3)` ; + - Si `a -> (2, 2, 2)` et `b -> (1, 2, 2)`, alors le broadcasting étendra la première dimension de `b` de sorte que `b -> (2, 2, 2)` ; - Si `a -> (3, 2, 2)` et `b -> (1, 1, 2)`, alors le broadcasting étendra `b` sur toutes les dimensions de forme 1 de sorte que `b -> (3, 2, 2)`. * *Étape 3 :* Après les étapes 1 et 2, si les deux tableaux ne correspondent toujours pas, une `ValueError` sera levée. Par exemple, supposons `a -> (2, 2, 3)` et `b -> (2, 2)` - - Par l'*Étape 1*, `b` sera étendu en `b -> (1, 2, 2)` ; - - Par l'*Étape 2*, `b` sera étendu en `b -> (2, 2, 2)` ; + - Par l'*Étape 1*, `b` sera étendu en `b -> (1, 2, 2)` ; + - Par l'*Étape 2*, `b` sera étendu en `b -> (2, 2, 2)` ; - Nous pouvons voir qu'ils ne correspondent pas l'un à l'autre après les deux premières étapes. Ainsi, une `ValueError` sera levée @@ -856,10 +859,10 @@ Pour nous aider, nous pouvons utiliser la liste de règles suivante : Les tableaux NumPy sont des types de données mutables, comme les listes Python. -En d'autres termes, leur contenu peut être modifié en mémoire après l'initialisation. +En d'autres termes, leur contenu peut être modifié (muté) en mémoire après l'initialisation. C'est pratique mais, combiné au modèle de nommage et de référence de Python, -cela peut être source d'erreurs pour les personnes qui découvrent NumPy. +cela peut conduire à des erreurs chez les débutants en NumPy. Dans cette section, nous passons en revue quelques points clés. @@ -880,7 +883,7 @@ a[-1] = 0 # Change le dernier élément en 0 a ``` -La mutabilité entraîne le comportement suivant, qui peut surprendre les utilisateurs de MATLAB. +La mutabilité conduit au comportement suivant (qui peut être choquant pour les programmeurs MATLAB...) ```{code-cell} python3 rng = np.random.default_rng() @@ -899,9 +902,9 @@ Ce qui s'est passé, c'est que nous avons modifié `a` en modifiant `b`. Le nom `b` est lié à `a` et devient simplement une autre référence au tableau (le modèle d'affectation de Python est décrit plus en détail {doc}`plus loin dans le cours `). -Toute modification effectuée par l'intermédiaire de `b` affecte donc également le tableau accessible par `a`. +Il a donc des droits égaux pour apporter des modifications à ce tableau. -Ce comportement est généralement préférable, car il évite les copies inutiles. +C'est en fait le comportement par défaut le plus sensé ! Cela signifie que nous ne faisons circuler que des pointeurs vers les données, plutôt que de faire des copies. @@ -956,7 +959,7 @@ z = np.array([1, 2, 3]) np.sin(z) ``` -Cela évite d'écrire explicitement une boucle parcourant les éléments un à un, comme dans l'exemple suivant +Cela élimine le besoin de boucles explicites élément par élément telles que ```{code-cell} python3 n = len(z) @@ -970,7 +973,7 @@ Parce qu'elles agissent élément par élément sur les tableaux, ces fonctions Dans le jargon de NumPy, elles sont aussi appelées **ufuncs**, ou **fonctions universelles**. Comme nous l'avons vu ci-dessus, les opérations arithmétiques usuelles (`+`, `*`, etc.) fonctionnent -également élément par élément. En combinant ces opérations aux ufuncs, on dispose d'un vaste ensemble d'opérations vectorisées et efficaces. +également élément par élément, et combiner celles-ci avec les ufuncs donne un très large ensemble de fonctions rapides opérant élément par élément. ```{code-cell} python3 z @@ -980,7 +983,7 @@ z (1 / np.sqrt(2 * np.pi)) * np.exp(- 0.5 * z**2) ``` -Les fonctions définies par l'utilisateur ne sont pas nécessairement vectorisées. +Toutes les fonctions définies par l'utilisateur n'agissent pas élément par élément. Par exemple, passer un tableau NumPy à la fonction `f` définie ci-dessous provoque une `ValueError` @@ -989,7 +992,7 @@ def f(x): return 1 if x > 0 else 0 ``` -La fonction NumPy `np.where` fournit une alternative vectorisée : +La fonction NumPy `np.where` fournit une alternative vectorisée : ```{code-cell} python3 x = rng.standard_normal(4) @@ -1058,7 +1061,7 @@ b z[b] ``` -En pratique, cette sélection est souvent réalisée en une seule étape +Bien sûr, nous pouvons --- et faisons souvent --- effectuer ceci en une seule étape ```{code-cell} python3 z[z > 3] @@ -1096,7 +1099,7 @@ np.linalg.inv(A) # Calcule l'inverse ```{index} single: Python; SciPy ``` -Une grande partie de ces fonctionnalités est aussi disponible dans [SciPy](https://scipy.org/), une collection de modules reposant sur NumPy. +Une grande partie de ces fonctionnalités est aussi disponible dans [SciPy](https://scipy.org/), une collection de modules qui sont construits par-dessus NumPy. Nous couvrirons les versions SciPy plus en détail {doc}`bientôt `. @@ -1109,7 +1112,7 @@ Pour une liste complète de ce qui est disponible dans NumPy, consultez [cette d NumPy tente d'implémenter le multithreading dans une grande partie de son code compilé. -Observons ce comportement à l'aide d'un exemple. +Examinons un exemple pour voir cela en action. Le morceau de code suivant calcule les valeurs propres d'un grand nombre de matrices générées aléatoirement. @@ -1125,7 +1128,7 @@ for i in range(n): ``` Maintenant, examinons la sortie du moniteur système htop sur notre machine pendant que -ce code s'exécute : +ce code s'exécute : ```{figure} /_static/lecture_specific/parallelization/htop_parallel_npmat.png :scale: 80 @@ -1157,7 +1160,7 @@ p(x) = a_0 + a_1 x + a_2 x^2 + \cdots a_N x^N = \sum_{n=0}^N a_n x^n {ref}`Plus tôt `, vous avez écrit une fonction simple `p(x, coeff)` pour évaluer {eq}`np_polynom` sans tenir compte de l'efficacité. -Écrivez maintenant une nouvelle fonction qui effectue le même travail, mais qui utilise des tableaux NumPy et des opérations sur tableaux pour ses calculs, sans utiliser de boucle Python explicite. +Écrivez maintenant une nouvelle fonction qui effectue le même travail, mais qui utilise des tableaux NumPy et des opérations sur tableaux pour ses calculs, plutôt qu'une quelconque forme de boucle Python. (Une telle fonctionnalité est déjà implémentée sous la forme de `np.poly1d`, mais pour les besoins de l'exercice, n'utilisez pas cette classe) @@ -1172,7 +1175,7 @@ Utilisez `np.cumprod()` :class: dropdown ``` -La fonction suivante répond à la question +Ce code fait le travail ```{code-cell} python3 def p(x, coef): @@ -1210,12 +1213,12 @@ Nous souhaitons générer une variable aléatoire discrète $x$ telle que $\math En d'autres termes, `x` prend des valeurs dans `range(len(q))` et `x = i` avec probabilité `q[i]`. -L'algorithme standard (de transformation inverse) est le suivant : +L'algorithme standard (de transformation inverse) est le suivant : * Divisez l'intervalle unité $[0, 1]$ en $n$ sous-intervalles $I_0, I_1, \ldots, I_{n-1}$ tels que la longueur de $I_i$ soit $q_i$. -* Effectuez un tirage $U$ selon la loi uniforme sur $[0, 1]$ et renvoyez le $i$ tel que $U \in I_i$. +* Tirez une variable aléatoire uniforme $U$ sur $[0, 1]$ et renvoyez le $i$ tel que $U \in I_i$. -La probabilité d'obtenir la valeur $i$ est égale à la longueur de l'intervalle $I_i$, qui est égale à $q_i$. +La probabilité de tirer $i$ est la longueur de $I_i$, qui est égale à $q_i$. Nous pouvons implémenter l'algorithme comme suit @@ -1231,10 +1234,10 @@ def sample(q): a = a + q[i] ``` -Si vous ne voyez pas comment cela fonctionne, essayez de suivre pas à pas l'exécution de l'algorithme sur un exemple simple, tel que `q = [0.25, 0.75]` +Si vous ne voyez pas comment cela fonctionne, essayez de suivre le flux pour un exemple simple, tel que `q = [0.25, 0.75]` Il est utile de dessiner les intervalles sur papier. -L'objectif de l'exercice est d'en proposer une version plus rapide à l'aide de NumPy, en évitant les boucles explicites +Votre exercice consiste à l'accélérer en utilisant NumPy, en évitant les boucles explicites ```{hint} :class: dropdown @@ -1257,7 +1260,7 @@ Si vous le pouvez, écrivez la méthode de sorte que `draw(k)` renvoie `k` tirag :class: dropdown ``` -Voici notre première tentative de solution : +Voici notre première tentative de solution : ```{code-cell} python3 from numpy import cumsum @@ -1285,7 +1288,8 @@ class DiscreteRV: return self.Q.searchsorted(self.rng.uniform(0, 1, size=k)) ``` -La logique peut sembler difficile au premier abord. Prenez le temps d'examiner chaque étape du code. +La logique n'est pas évidente, mais si vous prenez votre temps et la lisez lentement, +vous comprendrez. Il y a cependant un problème ici. @@ -1300,11 +1304,12 @@ d.q = (0.5, 0.5) Le problème est que `Q` ne change pas en conséquence, et `Q` correspond aux données utilisées dans la méthode `draw`. -Une première solution consisterait à recalculer `Q` à chaque appel de la méthode `draw`. +Pour gérer cela, une option consiste à calculer `Q` chaque fois que la méthode draw +est appelée. Mais c'est inefficace par rapport à un calcul unique de `Q`. -Une solution plus efficace consiste à utiliser un descripteur Python. +Une meilleure option consiste à utiliser des descripteurs. Une solution de la [bibliothèque quantecon](https://github.com/QuantEcon/QuantEcon.py/tree/main/quantecon) utilisant des descripteurs et se comportant comme nous le souhaitons peut être trouvée @@ -1319,7 +1324,7 @@ utilisant des descripteurs et se comportant comme nous le souhaitons peut être Rappelez-vous notre {ref}`discussion précédente ` sur la fonction de répartition empirique. -L'objectif de cet exercice est de +Votre tâche consiste à 1. Rendre la méthode `__call__` plus efficace en utilisant NumPy. 1. Ajouter une méthode qui trace la FDE sur $[a, b]$, où $a$ et $b$ sont des paramètres de la méthode. @@ -1331,8 +1336,8 @@ L'objectif de cet exercice est de Un exemple de solution est donné ci-dessous. -La solution ci-dessous adapte [ce code](https://github.com/QuantEcon/QuantEcon.py/blob/main/quantecon/ecdf.py) -de QuantEcon en lui ajoutant une méthode de tracé +En substance, nous avons simplement repris [ce code](https://github.com/QuantEcon/QuantEcon.py/blob/main/quantecon/ecdf.py) +de QuantEcon et ajouté une méthode de tracé ```{code-cell} python3 """ @@ -1425,7 +1430,7 @@ Rappelez-vous que le [broadcasting](broadcasting) dans NumPy peut nous aider à Dans cet exercice, essayez d'utiliser des boucles `for` pour reproduire le résultat des opérations de broadcasting suivantes. -**Partie 1** : Essayez de reproduire cet exemple simple en utilisant des boucles `for` et comparez vos résultats avec l'opération de broadcasting ci-dessous. +**Partie 1** : Essayez de reproduire cet exemple simple en utilisant des boucles `for` et comparez vos résultats avec l'opération de broadcasting ci-dessous. ```{code-cell} python3 @@ -1444,7 +1449,7 @@ tags: [hide-output] print(A) ``` -**Partie 2** : Passez à la reproduction du résultat de l'opération de broadcasting suivante. Parallèlement, comparez les vitesses du broadcasting et de la boucle `for` que vous implémentez. +**Partie 2** : Passez à la reproduction du résultat de l'opération de broadcasting suivante. Parallèlement, comparez les vitesses du broadcasting et de la boucle `for` que vous implémentez. Pour cette partie de l'exercice, vous pouvez utiliser les fonctions `tic`/`toc` de la bibliothèque `quantecon` pour chronométrer l'exécution.