diff options
Diffstat (limited to 'PVCM/cama/fr/ma1 np03 Manipulations.ipynb')
| -rw-r--r-- | PVCM/cama/fr/ma1 np03 Manipulations.ipynb | 823 |
1 files changed, 823 insertions, 0 deletions
diff --git a/PVCM/cama/fr/ma1 np03 Manipulations.ipynb b/PVCM/cama/fr/ma1 np03 Manipulations.ipynb new file mode 100644 index 0000000..b676e65 --- /dev/null +++ b/PVCM/cama/fr/ma1 np03 Manipulations.ipynb @@ -0,0 +1,823 @@ +{ + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "Les [manipulations d'un tableau](https://docs.scipy.org/doc/numpy-1.15.1/reference/routines.array-manipulation.html) comprennent :\n", + "\n", + "* la réorganisation du tableau (réindexation) \n", + "* l'aggrégation de 2 tableaux ou plus\n", + "* le découpage d'un tableau en 2 ou plus\n", + "\n", + "Avant de regarder ces points, regardons comment Numpy présente les\n", + "dimensions d'un tableau multidimensionnel avec la notion d'axes." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 4, 3)" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# An array of marks of 3 exams for 4 students in two subjects \n", + "# (therefore 6 marks per students or 12 per subjects)\n", + "\n", + "# stud.1 stud.2 stud.3 stud.4\n", + "marks = np.array([[[7,13,11], [7,7,13], [5,9,11], [7,17,15]], # subject 1\n", + " [[8,12,14], [8,12,12], [8,12,10], [12,16,12]]]) # subject 2\n", + "marks.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "## Les axes\n", + "\n", + "Un tableau a des axes qui correspondent aux axes d'un repère dans l'espace. L'ordre des axes\n", + "est celui de l'inclusion des crochets. En 2D un tableau de tableau est un tableau de lignes avec\n", + "chaque ligne qui est un tableau 1D de valeurs. L'ordre est donc lignes puis colonnes (contrairement à l'axes $(x,y)$ dans\n", + "l'espace). En 3D l'ordre est ligne, colonne, profondeur si on désire avoir une image, sinon c'est 0, 1 et 2.\n", + "\n", + "De très nombreuses opérations sur les tableaux se font suivant un des axes du tableau aussi il est important de \n", + "comprendre ce que sont les axes.\n", + "\n", + "Regardons l'exemple des notes ci-dessus. Les axes sont\n", + "\n", + "0. les matières\n", + "1. les étudiants \n", + "2. les examens" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "Faire la moyenne des valeurs suivant l'axe 1 revient à prendre les données suivant l'axe 1 et effectuer les calculs dessus, donc ici en sortir une moyenne." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 6.5, 11.5, 12.5],\n", + " [ 9. , 13. , 12. ]])" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "marks.mean(axis=1) # give means for each exam in each subject" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "Un autre facon de voir les axes est de les considérer comme des __axes de projection__. Si je projette un objet 3D\n", + "suivant l'axe des $y$, le résultat est un objet 2D en $(x,z)$. On a ainsi une réduction de dimension.\n", + "\n", + "Si je somme sur l'axe 0 un tableau de dimension (2,4,3) comme l'est notre tableau de notes, cela veut dire que je perds la dimension 0 et donc la dimension du résultat est (4,3).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(4, 3)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "marks.mean(axis=0).shape # mean along axis 0 (subjects) therefore this axis disapears" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "#### Quelques fonctions qui supportent les axes\n", + "\n", + "Toutes les fonctions qui s'appliquent à un ensemble de valeur pour produire un résultat\n", + "doivent pouvoir utiliser de concept d'axe (je ne les ai pas toutes\n", + "vérifiées mais n'hésitez pas à m'indiquer un contre-exemple). On a les fonctions mathématiques suivantes :\n", + "\n", + "* arithmétiques : `sum`, `prod`, `cumsum`, `cumprod` \n", + "* statistiques : `min`, `max`, `argmin`, `argmax`, `mean` (moyenne), `average` (moyenne pondérée), `std` (écart type), `var`, `median`, `percentile`, `quantile`\n", + "* autres : `gradiant`, `diff`, `fft`\n", + "\n", + "De plus il est possible de trier les valeurs d'un tableau suivant l'axe de son choix avec `sort`.\n", + "Par contre on ne peut les mélanger, avec [`shuffle`](https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.shuffle.html#numpy.random.shuffle), que suivant l'axe 0.\n", + "\n", + "#### Appliquer une fonction suivant un axe\n", + "\n", + "La fonction [`apply_along_axis`](https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.apply_along_axis.html)\n", + "permet d'appliquer une fonction 1D de son choix à un tableau suivant un axe. C'est l'axe qui va disparaître dans le résultat :" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-> [ 7 13 11] 6\n", + "-> [ 7 7 13] 6\n", + "-> [ 5 9 11] 6\n", + "-> [ 7 17 15] 10\n", + "-> [ 8 12 14] 6\n", + "-> [ 8 12 12] 4\n", + "-> [ 8 12 10] 4\n", + "-> [12 16 12] 4\n" + ] + }, + { + "data": { + "text/plain": [ + "array([[ 6, 6, 6, 10],\n", + " [ 6, 4, 4, 4]])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def diff_min_max(a):\n", + " print('->', a, a.max() - a.min())\n", + " return a.max() - a.min()\n", + "\n", + "np.apply_along_axis(diff_min_max, axis=-1, arr=marks) # -1 is the last axis, marks in our case" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "Question : c'est l'écart entre les notes de quoi ?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "#### Appliquer une fonction suivant plusieurs axes\n", + "\n", + "Certaines opérations peuvent prendre une liste d'axes et non un seul axe.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a.max \n", + " [17 16] \n", + "\n", + "a.max keepdim \n", + " [[[17]]\n", + "\n", + " [[16]]] \n", + "\n" + ] + } + ], + "source": [ + "print('a.max \\n', marks.max(axis=(1,2)), '\\n') \n", + "print('a.max keepdim \\n', marks.max(axis=(1,2), keepdims=True), '\\n') " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "Question : à quoi correspondent les 2 valeurs sorties ?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "Il est également peut utiliser la fonction [`apply_over_axes`](https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.apply_over_axes.html#numpy.apply_over_axes) pour lui indiquer quelle\n", + "fonction doit être appliquée suivant les axes donnés.\n", + "\n", + "Attention la fonction donnée en argument recevra l'ensemble du tableau et l'axe sur lequel elle doit\n", + "travailler, les axes étant donnés les uns après les autres et le tableau étant modifié à chaque étape." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Apply over axis 1\n", + "[[[ 7 13 11]\n", + " [ 7 7 13]\n", + " [ 5 9 11]\n", + " [ 7 17 15]]\n", + "\n", + " [[ 8 12 14]\n", + " [ 8 12 12]\n", + " [ 8 12 10]\n", + " [12 16 12]]] \n", + "\n", + "Apply over axis 2\n", + "[[[ 7 17 15]]\n", + "\n", + " [[12 16 14]]] \n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "array([[[17]],\n", + "\n", + " [[16]]])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def mymax(array, axis):\n", + " print('Apply over axis', axis)\n", + " print(array, '\\n')\n", + " return array.max(axis)\n", + "\n", + "np.apply_over_axes(mymax, marks, axes=(1,2))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "## Réorganisation d'un tableau\n", + "\n", + "On a déjà vu `reshape` pour changer la forme d'un tableau, `flatten` pour l'applatir en 1 dimension, regardons\n", + "d'autres fonctions de manipulation des tableaux.\n", + "\n", + "### Réordonner les axes\n", + "\n", + "#### `moveaxis` déplace un axe\n", + "\n", + "Dans notre exemple de notes, les 3 axes sont les matières, les étudiants et les examens.\n", + "La fonction `moveaxis` permet de déplacer un axe. Si ainsi je désire que les examens deviennent le premier axe\n", + "afin d'en faire ressortir les notes, je déplace l'axe 2 à la position 0 et les autres axes glissent pour faire de la place, l'axe 0 devient l'axe 1 et l'axe 1 devient l'axe 2 :" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "marks.shape = (2, 4, 3) \n", + "\n", + "b.shape = (3, 2, 4)\n" + ] + }, + { + "data": { + "text/plain": [ + "array([[[ 7, 7, 5, 7],\n", + " [ 8, 8, 8, 12]],\n", + "\n", + " [[13, 7, 9, 17],\n", + " [12, 12, 12, 16]],\n", + "\n", + " [[11, 13, 11, 15],\n", + " [14, 12, 10, 12]]])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print('marks.shape = ',marks.shape, '\\n')\n", + "b = np.moveaxis(marks, 2, 0) \n", + "print('b.shape = ', b.shape)\n", + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "Il est plus simple de voir ainsi que le premier examen a été difficile." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "#### `swapaxes` échange 2 axes\n", + "\n", + "Plutôt que d'insérer un axe à une nouvelle position et faire glisser les autres, on peut vouloir en échanger deux.\n", + "Voici comme avoir les notes pour chaque matière et chaque examen : " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[ 7, 7, 5, 7],\n", + " [13, 7, 9, 17],\n", + " [11, 13, 11, 15]],\n", + "\n", + " [[ 8, 8, 8, 12],\n", + " [12, 12, 12, 16],\n", + " [14, 12, 10, 12]]])" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "marks.swapaxes(1,2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "#### `transpose` pour tout faire\n", + "\n", + "Enfin `transpose` permet de réordonner tous les axes comme on veut, ainsi : `transpose((2,0,1))` met\n", + "\n", + "* l'axe 2 en place 0, \n", + "* l'axe 0 en place 1 \n", + "* l'axe 1 en place 2.\n", + "\n", + "#### Un apply over axis plus simple et plus rapide\n", + "\n", + "Malheureusement la fonction `apply_over_axis` n'est pas optimisée, aussi dans certaints il peut\n", + "être préférable de faire une boucle sur son tableau, ce qui veut dire mettre les axes qui vont rester au début et ceux sur lesquels on fait notre réduction à la fin :" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Means per students [10.833333333333334, 9.833333333333334, 9.166666666666666, 13.166666666666666]\n", + "24 µs ± 2.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", + "29.6 µs ± 1.23 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" + ] + } + ], + "source": [ + "print(\"Means per students\", [m.mean() for m in marks.transpose((1,0,2))])\n", + "\n", + "%timeit [m.mean() for m in marks.transpose((1,0,2))]\n", + "%timeit np.apply_over_axes(np.mean, marks, axes=(0,2))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "### Changer l'ordre des éléments d'un tableau\n", + "\n", + "On peut inverser les valeurs d'un tableau suivant un axe avec [`flip`](https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.flip.html#numpy.flip)\n", + "ce qui peut aussi être fait en l'indiquant au niveau des indices. Ainsi `np.flip(a, n)` est équivalent à \n", + "`a[:,:,..,::-1,:,..,:]` avec `::-1` en $n$-ième position.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[12, 13, 14, 15],\n", + " [16, 17, 18, 19],\n", + " [20, 21, 22, 23]],\n", + "\n", + " [[ 0, 1, 2, 3],\n", + " [ 4, 5, 6, 7],\n", + " [ 8, 9, 10, 11]]])" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = np.arange(24).reshape([2,3,4])\n", + "np.flip(a,0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "On peut faire glisser les valeurs suivant un axe avec `roll` en spécifiant de combien on les fait glisser :" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[ 4, 5, 6, 7],\n", + " [ 8, 9, 10, 11],\n", + " [ 0, 1, 2, 3]],\n", + "\n", + " [[16, 17, 18, 19],\n", + " [20, 21, 22, 23],\n", + " [12, 13, 14, 15]]])" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.roll(a, 2, axis=1) # roll elements by 2 along axis 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "La transposée s'applique aussi quelque soit la dimension. Par défaut elle inverse l'ordre des axes mais on peut \n", + "spécifier l'ordre voulu en sortie." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[ 0, 12],\n", + " [ 4, 16],\n", + " [ 8, 20]],\n", + "\n", + " [[ 1, 13],\n", + " [ 5, 17],\n", + " [ 9, 21]],\n", + "\n", + " [[ 2, 14],\n", + " [ 6, 18],\n", + " [10, 22]],\n", + "\n", + " [[ 3, 15],\n", + " [ 7, 19],\n", + " [11, 23]]])" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a.T" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[ 0, 4, 8],\n", + " [ 1, 5, 9],\n", + " [ 2, 6, 10],\n", + " [ 3, 7, 11]],\n", + "\n", + " [[12, 16, 20],\n", + " [13, 17, 21],\n", + " [14, 18, 22],\n", + " [15, 19, 23]]])" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.transpose(a, (0,2,1))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "## Agrégation\n", + "\n", + "### Concaténation\n", + "\n", + "La fonction de base est [`concatenate`](https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.concatenate.html) en indiquant l'axe choisi pour la concaténation. C'est à mon avis la méthode\n", + "la plus sûre et elle marche quelque soit la dimension.\n", + "\n", + "Cela étant on peut utiliser pour des tableaux 2D ou 3D :\n", + "\n", + "* `vstack` ou `row_stack` pour la concaténation vertical \n", + "* `hstack` ou `column_stack` pour la concaténation horizontal\n", + "* `dstack` pour la concaténation en profondeur (_deep_).\n", + "\n", + "Toutes ces fonctions prennent une liste de tableaux à concaténer comme argument. Bien sûr les tailles des tableaux doivent être compatibles." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[0. 0. 0.]\n", + " [0. 0. 0.]\n", + " [1. 1. 1.]\n", + " [1. 1. 1.]] \n", + "\n", + "[[0. 0. 0. 1. 1. 1.]\n", + " [0. 0. 0. 1. 1. 1.]]\n" + ] + } + ], + "source": [ + "a = np.zeros((2,3))\n", + "b = np.ones((2,3))\n", + "\n", + "print(np.concatenate((a,b), axis=0), '\\n') # same than vstack\n", + "print(np.hstack((a,b))) # same than concatenate with axis=1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "### Empilage\n", + "\n", + "A la différence de la concaténation, l'empilage ajoute une dimension.\n", + "Empiler est utile pour stocker un paquet de tableaux 2D, des images par exemple, dans un tableau 3D. \n", + "On utilise la fonction [`stack`](https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.stack.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[0., 0., 0.],\n", + " [0., 0., 0.]],\n", + "\n", + " [[1., 1., 1.],\n", + " [1., 1., 1.]]])" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c = np.stack((a,b)) # c[0] is a\n", + "c" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "Notez que `stack` a une option `axis` pour indiquer la direction dans laquelle on désire stocker les tableaux donnés." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "## Découpage\n", + "\n", + "La fonction inverse de la concaténation est le découpage avec [`split`](https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.split.html#numpy.split) qui demande comme arguments :\n", + "\n", + "* le tableau à découper\n", + "* en combien de morceaux ou à quels indices\n", + "* la direction (l'axe)\n", + "\n", + "Pour retrouver nos deux tableaux qui ont généré le résultat de la cellule précédante on coupe en 2 suivant l'axe 0. On peut aussi couper suivant un autre axe." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "split part 1\n", + " [[[0. 0. 0.]]\n", + "\n", + " [[1. 1. 1.]]] \n", + "\n", + "split part 2\n", + " [[[0. 0. 0.]]\n", + "\n", + " [[1. 1. 1.]]]\n" + ] + } + ], + "source": [ + "e,f = np.split(c, 2, 1) # splits in 2 along axis 1\n", + "print(\"split part 1\\n\", e, '\\n')\n", + "print(\"split part 2\\n\", f)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "Il existe aussi `hsplit`, `vsplit` et `dsplit` pour découper suivant les axes 0, 1 et 2.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "## From Python to Numpy\n", + "\n", + "Si vous désirez creuser et regarder de nombreux exemples, vous pouvez lire le livre de N. Rougier [From Python to Numpy](https://www.labri.fr/perso/nrougier/from-python-to-numpy/)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "lang": "fr" + }, + "source": [ + "\n", + "## Pandas aussi\n", + "\n", + "On retrouvera ces manipulations avec Pandas qui est le super tableur de Python. Il travaille aussi sur des \n", + "structures en forme de tableau mais sans la contrainte que toutes les valeurs soient du même type.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "variables": { + " PreviousNext(\"np02 Filtres.ipynb\", \"np04 Xarray.ipynb\")": " <br/><center><a href=\"np02 Filtres.ipynb\">np02 Filtres</a> ← <a href=\"http://python3.mooc.lrde.epita.fr/notebooks/Table%20des%20mati%C3%A8res.ipynb\" style=\"text-decoration:none\"> △ </a> → <a href=\"np04 Xarray.ipynb\">np04 Xarray</a></center><br/> " + } + }, + "source": [ + "{{ PreviousNext(\"np02 Filtres.ipynb\", \"np04 Xarray.ipynb\")}}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ] +}
\ No newline at end of file |
