---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
kernelspec:
  display_name: Python 3 (ipykernel)
  language: python
  name: python3
---

+++ {"nbgrader": {"grade": false, "grade_id": "cell-63247b06949c9806", "locked": true, "schema_version": 3, "solution": false}}

# Manipuler des tableaux

Dans cette feuille, vous allez apprendre à effectuer quelques manipulations simples sur
les tableaux, comme nous l'avions fait au premier semestre avec les `vector` de C++. En
Python, de tels tableaux peuvent être représentés par les `array` de la bibliothèque
`NumPy` (usuellement abrégée en `np`) :

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-8c751ead7518dfea
  locked: true
  schema_version: 3
  solution: false
---
import numpy as np
```

+++ {"nbgrader": {"schema_version": 3, "solution": false, "grade": false, "locked": true, "task": false, "grade_id": "cell-1063495ca725c5db"}}

## Tableaux à deux dimensions

+++ {"nbgrader": {"grade": false, "grade_id": "cell-9a29dafa51e1a013", "locked": true, "schema_version": 3, "solution": false}}

Voilà un tableau à deux dimensions avec deux lignes et quatre colonnes:

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-67fbacbcf94193b3
  locked: true
  schema_version: 3
  solution: false
---
T = np.array([[1, 2, 3, 4], 
              [5, 6, 7, 8]])
```

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-845dffdd69e40937
  locked: true
  schema_version: 3
  solution: false
---
T
```

Vous noterez que l'on a construit le tableau avec la fonction `np.array` à partir d'une
liste de deux listes donnant respectivement le contenu de la première et la seconde ligne
du tableau. Puis on a affecté le tableau obtenu à la variable `T`.

+++ {"nbgrader": {"grade": false, "grade_id": "cell-47cf516abf13b964", "locked": true, "schema_version": 3, "solution": false}}

On peut retrouver les tailles de ce tableau avec:

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-3cfd6fa0623e6f98
  locked: true
  schema_version: 3
  solution: false
---
T.shape
```

+++ {"nbgrader": {"schema_version": 3, "solution": false, "grade": false, "locked": true, "task": false, "grade_id": "cell-c8f6590b61b5b276"}}

Vous vous rappellez que les `vector` de C++ sont intrinsèquement des tableaux à une
dimension, et que l'on émule des tableaux à deux dimensions avec des tableaux de
tableaux. Ici, en revanche, les tableaux `array` de numpy permettent de construire
explicitement des tableaux à deux dimensions.

+++ {"nbgrader": {"grade": false, "grade_id": "cell-3080232c4e2fb2df", "locked": true, "schema_version": 3, "solution": false}}

### Exercice

1. En vous inspirant de l'exemple ci-dessus, construisez un tableau à trois lignes et
   trois colonnes, contenant les entiers de 1 à 9 de gauche à droite et de haut en bas
   comme dans la figure suivante:

   ```
   1 2 3
   4 5 6
   7 8 9
   ```

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-1b95ffffb22a38df
  locked: false
  schema_version: 3
  solution: true
  task: false
---
### BEGIN SOLUTION
T2 = np.array([[1,2,3],
               [4,5,6],
               [7,8,9]])
### END SOLUTION
T2
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-f23c45dc4abb2118", "locked": true, "schema_version": 3, "solution": false}}

Nous testons la forme du tableau:

```{code-cell} ipython3
---
nbgrader:
  grade: true
  grade_id: cell-3dcbc34e7cdb8449
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
assert T2.shape == (3,3)
```

+++ {"nbgrader": {"schema_version": 3, "solution": false, "grade": false, "locked": true, "task": false, "grade_id": "cell-96fc71e105b68b41"}}

ainsi que son contenu :

```{code-cell} ipython3
---
nbgrader:
  grade: true
  grade_id: cell-b2598336ebcb56a2
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
assert [ T2[i,j] for i in range(3) for j in range(3) ] == [1, 2, 3, 4, 5, 6, 7, 8, 9]
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-21d9a01c8d293ab4", "locked": true, "schema_version": 3, "solution": false}}

Voici comment accéder au contenu d'une case individuelle du tableau :

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-9ca4451347ad1cf3
  locked: true
  schema_version: 3
  solution: false
---
T2[1,2]
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-0c42459abbd31832", "locked": true, "schema_version": 3, "solution": false}}

Cette case est en deuxième ligne et troisième colonne: en effet, comme en C++, les lignes
et colonnes sont numérotées à partir de 0.

+++ {"nbgrader": {"grade": false, "grade_id": "cell-4aaf4afc9dab0881", "locked": true, "schema_version": 3, "solution": false}}

Si l'on veut extraire toute une ligne, ou toute une colonne, on remplace la coordonnée
que l'on veut laisser libre par `:`. Ainsi, dans l'exemple suivant, on extrait la
deuxième colonne (donc d'indice $1$) en laissant libre l'indice de la ligne, et en fixant
l'indice de la colonne à `1`:

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-6c91767340cf600d
  locked: true
  schema_version: 3
  solution: false
---
T2[:,1]
```

+++ {"nbgrader": {"schema_version": 3, "solution": false, "grade": false, "locked": true, "task": false, "grade_id": "cell-7d229a134ec6e867"}}

Extrayez la deuxième ligne du tableau et affectez-la à la variable `li` dont vous
afficherez le contenu :

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-0abb1396ab91dd95
  locked: false
  schema_version: 3
  solution: true
  task: false
---
### BEGIN SOLUTION
li = T2[1,:]
li
### END SOLUTION
```

```{code-cell} ipython3
---
nbgrader:
  grade: true
  grade_id: cell-e22d3374bf68c443
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
assert isinstance(li, np.ndarray)
assert li.shape == (3,)
assert list(li) == [4,5,6]
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-990adcd36c40f8df", "locked": true, "schema_version": 3, "solution": false}}

## Tableaux à trois dimensions et plus

Pour le moment, nous avons utilisé des tableaux à deux dimensions. Ultérieurement,
notamment pour représenter des images, nous aurons besoin de tableaux de plus grande
dimension: un seul nombre ne suffit en effet pas pour représenter un pixel.

`Numpy` permet de représenter des tableaux de toute dimension. Voici un tableau de
dimension 3 :

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-34c03e0c6027d356
  locked: true
  schema_version: 3
  solution: false
---
T3D = np.array([[[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9]],
                [[10,11,12], [13,14,15], [16,17,18]],
                [[19,20,21], [22,23,24], [25,26,27]]
                ])
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-796c1a5acbab1a2b", "locked": true, "schema_version": 3, "solution": false}}

On peut le voir comme un tableau à trois couches :

:::{figure} media/fig_tab.svg
:width: 30%
:alt: représentation du tableau à trois dimensions
:::

% TODO 2024-2025: ajouter les axes i,j,k sur la figure

Pour accéder à une case du tableau on utilise `T[i,j,k]`, où `i` est le numéro de la
ligne, `j` le numéro de la colonne et `k` le numéro de la couche contenant la case.

Comme pour les listes, on peut extraire des sous-tableaux avec les opérateurs de découpe
de tranches (*slicing*).

:::{admonition} Rappel: tranches (*slices*)
:class: hint dropdown

Lorsque `L` est une liste, l'opération `L[start:stop:step]` permet d'extraire une
*tranche* de `L`, liste contenant tous les éléments de `L` d'indice entre `start`
(inclus) et `stop` (exclus) par pas de `step`. Par défaut, `start` vaut `0`, `stop` vaut
`len(L)`, et `step` vaut `1`. Ainsi, `L[:3]` contient les trois premiers éléments de `L`
(d'indice `i<3`) tandis que `L[3:]` contient les éléments suivants (d'indice `3<=i`).

Plus généralement, cette opération s'applique à la plupart des objets indexés par des
entiers.
:::

On peut ainsi extraire ces couches comme suit :

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-574e34b7ddc4509d
  locked: true
  schema_version: 3
  solution: false
---
T3D[:,:,0]
```

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-dd11d154ece1aef6
  locked: true
  schema_version: 3
  solution: false
---
T3D[:,:,1]
```

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-a96fbf7480f42e12
  locked: true
  schema_version: 3
  solution: false
---
T3D[:,:,2]
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-414edd834755639f", "locked": true, "schema_version": 3, "solution": false}}

### Exercices

Extrayez la première colonne de la deuxième couche de `T3D` et stockez-la dans la
variable `C`:

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-81c4d71ee7b1a54d
  locked: false
  schema_version: 3
  solution: true
  task: false
---
### BEGIN SOLUTION
C = T3D[:,0,1]
### END SOLUTION
C
```

+++ {"nbgrader": {"schema_version": 3, "solution": false, "grade": false, "locked": true, "task": false, "grade_id": "cell-868c3302aea64357"}}

Notez que c'est un tableau à une dimension, donc noté en ligne !

```{code-cell} ipython3
---
nbgrader:
  grade: true
  grade_id: cell-995993eb1b58947e
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
assert list(C) == [2, 11, 20]
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-414edd834755639g", "locked": true, "schema_version": 3, "solution": false}}

Maintenant, extrayez un tableau contenant la première colonne de chacune des trois
couches de `T3D` et stockez le dans la variable `C`. Notez que l'on souhaite que ces
colonnes soient bien représentées par des colonnes dans `C` !

:::{todo}
Ajouter une indication: Cela se fait en un seul appel à T[...,...,...]. Pour chacune des
trois coordonnées (numéro de ligne, de colonne, de couche) décidez si vous souhaitez la
fixer ou la laisser libre.
:::

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-abdcb2e9f73d6dfe
  locked: false
  schema_version: 3
  solution: true
  task: false
---
### BEGIN SOLUTION
C = T3D[:,0,:]
### END SOLUTION
C
```

```{code-cell} ipython3
---
nbgrader:
  grade: true
  grade_id: cell-3227c3665bd8802d
  locked: true
  points: 1
  schema_version: 3
  solution: false
  task: false
---
for i in range(3):
    assert np.array_equal(T3D[:,0,i], C[:,i])
```

+++ {"nbgrader": {"grade": false, "grade_id": "cell-72625c162aacdd71", "locked": true, "schema_version": 3, "solution": false}}

## Statistiques simples sur les tableaux

+++ {"nbgrader": {"grade": false, "grade_id": "cell-202df26286a5b3a0", "locked": true, "schema_version": 3, "solution": false}}

Numpy permet de faire des statistiques simples sur les tableaux. Revenons à notre tableau
`T` :

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-e979baa940e6f47e
  locked: true
  schema_version: 3
  solution: false
---
T
```

+++ {"nbgrader": {"schema_version": 3, "solution": false, "grade": false, "locked": true, "task": false, "grade_id": "cell-29729cfe62afb790"}}

Calculez à la main :

- la moyenne de chaque colonne de `T`;
- la moyenne de chaque ligne de `T`;
- la moyenne de tous les éléments du tableau `T`.

+++ {"nbgrader": {"schema_version": 3, "solution": false, "grade": false, "locked": true, "task": false, "grade_id": "cell-00194d77bb863b7b"}}

Comparez vos résultats avec ceux des calculs suivants. Que calcule chaque commande
ci-dessous?

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-1d9fbab6c508d984
  locked: true
  schema_version: 3
  solution: false
---
T.mean(axis=0)
```

REPONDEZ ICI

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-899594980a692e52
  locked: true
  schema_version: 3
  solution: false
---
T.mean(axis=1)
```

REPONDEZ ICI

```{code-cell} ipython3
---
nbgrader:
  grade: false
  grade_id: cell-e979baa940e6f47d
  locked: true
  schema_version: 3
  solution: false
---
T.mean()
```

REPONDEZ ICI

+++ {"nbgrader": {"schema_version": 3, "solution": false, "grade": false, "locked": true, "task": false, "grade_id": "cell-7fc37d52040f3366"}}

## Conclusion

Voilà, vous avez vu tous les éléments de manipulation des tableaux `NumPy` dont nous
aurons besoin aujourd'hui.
