{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "eddab7fb",
   "metadata": {},
   "source": [
    "# Traitement du Signal - TDM1 : NumPy\n",
    "\n",
    "---\n",
    "**AVANT DE COMMENCER, TRES IMPORTANT !!**\n",
    "\n",
    "Durant ce semestre, vous aurez des TDM à rendre.\n",
    "\n",
    "Vous aurez donc dans ces fichiers de TDM des cellules de codes à compléter, comme celle-ci :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8ef86337",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bcc17021",
   "metadata": {},
   "source": [
    "Ou des cellules texte à compléter (commençant par **_REPONSE_**) :"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5205e23",
   "metadata": {},
   "source": [
    "**_REPONSE :_** _Ici, vous écrivez votre réponse._"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "687f90de",
   "metadata": {},
   "source": [
    "Vérifiez bien avant de rendre un TDM que toutes les cellules (codes et texte) soient bien **complétées**."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21b868af",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "L'objectif de ce premier TDM va être de prendre en main le package python NumPy. Cette librairie sert à manipuler efficacement des tableaux de données, contenant des entiers, des nombres décimaux (float), des nombres complexes (complex), ou autre. De plus ces données peuvent être codées différement (mais on verra ça plus tard). On peut manipuler des tableaux à plusieurs dimensions, mais dans le contexte de ce cours, on utilisera uniquement des tableaux à une seule dimension (ce qui représente un signal). De ce fait, on peut se poser une question: pourquoi ne pas utiliser une liste Python pour manipuler les signaux ? A travers ce TDM, nous répondrons donc à cette question.\n",
    "\n",
    "Cette librairie est incontournable dans le domaine et va être utilisée durant tout le semestre, donc prenez bien le temps de lire la documentation : https://numpy.org/doc/2.3/user/index.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f9eabb79",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import de la librairie NumPy\n",
    "import numpy as np\n",
    "\n",
    "# Import de la librairie time, qui permettra de mesurer le temps d'exécution de certaines commandes\n",
    "import time"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0792d760",
   "metadata": {},
   "source": [
    "Lorsque vous utiliszz une fonction NumPy, je vous invite fortement à aller lire la documentation officielle de NumPu, comme précisé dessus, mais vous pouvez également utiliser la fonction *help* afin d'avoir la documentation directement dans votre Notebook"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e476e16",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function print in module builtins:\n",
      "\n",
      "print(*args, sep=' ', end='\\n', file=None, flush=False)\n",
      "    Prints the values to a stream, or to sys.stdout by default.\n",
      "\n",
      "    sep\n",
      "      string inserted between values, default a space.\n",
      "    end\n",
      "      string appended after the last value, default a newline.\n",
      "    file\n",
      "      a file-like object (stream); defaults to the current sys.stdout.\n",
      "    flush\n",
      "      whether to forcibly flush the stream.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Exemple de documentation pour la fonction print\n",
    "help(print)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81ea5a9e",
   "metadata": {},
   "source": [
    "\n",
    "Je vous recommande **FORTEMENT** :\n",
    "- de prendre des notes durant ce TDM sur les fonctions NumPy vues, afin de créer un document qui vous sera utile durant tout le semestre : une cheat sheet\n",
    "- de vous partager le clavier si vous êtes en binôme ! Il faut que tout le monde code !\n",
    "- de ne pas utiliser d'IA pour faire le TDM à votre place (ChatGPT, Gemini, Claude, ou encore pire, Grok...). Vous aurez un examen pratique en fin de semestre, sans possibilité de les consulter, donc autant apprendre en TDM\n",
    "\n",
    "Ce TDM va être répétitif: pour chaque exercice, je vous demande de le réaliser avec:\n",
    "- un tableau sous forme de liste Python (ex: $[1, 2, 3, 4]$)\n",
    "- un tableau NumPy (ex: $np.array([1, 2, 3, 4])$)\n",
    "\n",
    "A travers les différents exercices, vous vous rendrez compte normalement que même si vous maîtrisez bien les listes Python (enfin, vous croyez bien les maîtriser...), un tableau NumPy est très souvent beaucoup plus optimisé, en terme d'efficacité, de temps d'exécution, et de lisibilité de code !\n",
    "\n",
    "De ce fait, avant de partir la tête dans le guidon afin de finir le TDM au plus vite, lisez la documentation pour trouver la fonction la plus adaptée. Très souvent, une ou deux lignes de code suffisent !"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b50b4fac",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Ex 1 : La base de la base de NumPy\n",
    "\n",
    "### De la liste au tableau\n",
    "\n",
    "Commençons tout d'abord par créer un tableau NumPy. Etant donné qu'une liste est un tableau d'une seule dimension, on peut d'abord créer un tableau à partir d'une liste python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5a71fd14",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Une première liste Python\n",
    "\n",
    "L = [2, 5, 1, 8, 3]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50ecac58",
   "metadata": {},
   "source": [
    "Convertissez la liste $L$ précédemment créée, en un tableau NumPy affectée à la variable $T$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e0a5d6db",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "T : [2 5 1 8 3]\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Conversion de la liste L en un tableau Numpy T\n",
    "T = np.array(L)\n",
    "print(f\"T : {T}\") # Le print affiche le tableau comme si c'était une liste. Ce n'est pas un bug, mais une surcouche permettant d'afficher proprement les données du tableau"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a283ff41",
   "metadata": {},
   "source": [
    "Affichez maintenant le type de données de $T$ (pour vérifier que c'est bien un tableau NumPy), ainsi que le type de données de ces éléments"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "075eadbc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Type de données de T : <class 'numpy.ndarray'>\n",
      "Type de données des éléments de T : int64\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage du type de données de T et du type de données de ces éléments\n",
    "print(f\"Type de données de T : {type(T)}\")\n",
    "print(f\"Type de données des éléments de T : {T.dtype}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e650a98a",
   "metadata": {},
   "source": [
    "En effet, contrairement à une liste, qui peut inclure n'importe quel type de données (en réalité, les éléments de la liste sont des pointeurs vers des objets Python), les éléments d'un tableau NumPy ont un type de données. Ici, vous devriez avoir un type de données *int64*, à savoir des entiers 64 bits. Cela veut dire que si vous ajoutez un élément qui ne convient pas au type de données prévu, le type de données de tous les éléments du tableau changera. \n",
    "\n",
    "Pour illustrer ces précédentes lignes, répétez les mêmes opérations avec les listes $L_2$, $L_3$, et $L_4$. Convertissez les listes en tableaux NumPy, et affichez le type de données des éléments des tableaux"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "790f6fa1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Type de données des éléments de T2 : float64\n",
      "Type de données des éléments de T3 : complex128\n",
      "Type de données des éléments de T4 : <U21\n"
     ]
    }
   ],
   "source": [
    "L2 = [2, 5, 1, 8, 3, 7.5]\n",
    "L3 = [2, 5, 1, 8, 3, 3j]\n",
    "L4 = [2, 5, 1, 8, 3, 'a']\n",
    "\n",
    "# A COMPLETER\n",
    "# Conversion des listes en tableaux et affichage des types de données des éléments des tableaux\n",
    "T2 = np.array(L2)\n",
    "T3 = np.array(L3)\n",
    "T4 = np.array(L4)\n",
    "print(f\"Type de données des éléments de T2 : {T2.dtype}\")\n",
    "print(f\"Type de données des éléments de T3 : {T3.dtype}\")\n",
    "print(f\"Type de données des éléments de T4 : {T4.dtype}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b344bcd",
   "metadata": {},
   "source": [
    "Affichez ensuite la taille de $T$, et son nombre de dimensions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d88b767e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Taille de T : (5,)\n",
      "Nombre de dimensions de T : 1\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage de la taille et du nombre de dimensions de T\n",
    "print(f\"Taille de T : {T.shape}\") # Comme le tableau est d'une seule dimension, on peut également utiliser len(T), mais c'est moins adapté\n",
    "print(f\"Nombre de dimensions de T : {T.ndim}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53825fdb",
   "metadata": {},
   "source": [
    "### Construction d'un tableau\n",
    "\n",
    "Même si on peut facilement convertir une liste en tableau, il est souvent préférable de construire le tableau directement avec NumPy. Pour cela, on va maintenant construire des tableaux, tout d'abord sous forme de liste Python, puis sous forme de tableau NumPy. \n",
    "\n",
    "**Attention: Je ne veux voir aucune conversion Liste -> Numpy et Numpy -> Liste, c'est tout l'intérêt de ce TDM !**\n",
    "\n",
    "Construisez tout d'abord un tableau nul (rempli de zéros), de taille 50"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7f0641ad",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Construction d'un tableau nul de taille 50\n",
    "\n",
    "# Avec une liste\n",
    "L = []\n",
    "for i in range(50):\n",
    "    L.append(0)\n",
    "    \n",
    "# Avec une liste (mais mieux optimisé)\n",
    "L = [0] * 50\n",
    "\n",
    "# Avec un tableau NumPy\n",
    "T = np.zeros((50))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "787e8b37",
   "metadata": {},
   "source": [
    "Même exercice, mais avec un tableau de taille 10 000 000, remplis de uns. Et on va chronométrer tout ça."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "c8b62118",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Construction de L (non optimisé): 5.6847333908081055 s\n",
      "Construction de L (optimisé): 0.21520066261291504 s\n",
      "Construction de T: 0.17491412162780762 s\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Construction d'un tableau remplis de uns, de taille 10 000 000\n",
    "taille = 100000000\n",
    "# Avec une liste\n",
    "t_init = time.time()\n",
    "L = []\n",
    "for i in range(taille):\n",
    "    L.append(1)\n",
    "t_end = time.time()\n",
    "print(f\"Construction de L (non optimisé): {t_end-t_init} s\")\n",
    "\n",
    "# Avec une liste (optimisé)\n",
    "t_init = time.time()\n",
    "L = [1] * taille\n",
    "t_end = time.time()\n",
    "print(f\"Construction de L (optimisé): {t_end-t_init} s\")\n",
    "\n",
    "# Avec un tableau NumPy\n",
    "t_init = time.time()\n",
    "L = np.ones((taille))\n",
    "t_end = time.time()\n",
    "print(f\"Construction de T: {t_end-t_init} s\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "604c3b2b",
   "metadata": {},
   "source": [
    "Un nouveau tableau à construire: la suite des nombres allant de 0 à 149, donc de taille 150 ($A = [0, 1, 2, ..., 149]$)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "8f6bd2d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Construction du tableau représentant la suite allant de 0 à 149\n",
    "\n",
    "# Avec une liste\n",
    "L = []\n",
    "for i in range(150):\n",
    "    L.append(i)\n",
    "\n",
    "# Avec une liste (optimisé)\n",
    "L = [i for i in range(150)]\n",
    "\n",
    "# Avec un tableau NumPy\n",
    "T = np.arange(150)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2620c9de",
   "metadata": {},
   "source": [
    "Construisez ensuite une liste et un tableau avec la suite des nombres allant de 1000 à 50, avec un pas de -10 ($B = [1000, 990, 980, ..., 50]$)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "6dcf049a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Construction du tableau représentant la suite allant de 1000 à 50, avec un pas de -10\n",
    "\n",
    "# Avec une liste\n",
    "L = []\n",
    "for i in range(1000, 40, -10):\n",
    "    L.append(i)\n",
    "\n",
    "# Avec une liste (optimisé)\n",
    "L = [i for i in range(1000, 40, -10)]\n",
    "\n",
    "# Avec un tableau NumPy\n",
    "T = np.arange(1000, 40, -10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61fde770",
   "metadata": {},
   "source": [
    "Pour finir, une dernière construction de liste et tableau contenant précisément 145 valeurs, allant de 68 à 1923, avec un pas régulier.\n",
    "\n",
    "*Note : Pour le tableau NumPy, il y a une fonction NumPy conçue précisément pour ce cas de problème, sans besoin de calculer le pas...*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "866f7e7e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Construction du tableau de 145 valeurs, de 68 à 1923, avec un pas régulier\n",
    "\n",
    "# Avec une liste\n",
    "L = []\n",
    "pas = (1923-68) / (145-1) # On est obligé de calculer le pas si on manipule avec des listes\n",
    "for i in range(145):\n",
    "    L.append(68 + i * pas)\n",
    "\n",
    "# Avec une liste (optimisé)\n",
    "L = [68 + i* pas for i in range(145)]\n",
    "\n",
    "# Avec un tableau NumPy\n",
    "T = np.linspace(68, 1923, 145)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab99f4f1",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Ex 2 : Tableau + Elément = Tableau ?\n",
    "\n",
    "Pour cet exercice, je vous construis la liste et le tableau qu'on va utiliser. Et on va faire des opérations basiques dessus. Voilà..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "8a7c7b7d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# La liste et le tableau à manipuler pour ce nouvel exercice\n",
    "L = [2,5,8,3,9,1,7,45,3,69,2,8,7,66]\n",
    "T = np.array(L)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1cfc06bf",
   "metadata": {},
   "source": [
    "Incrémentez de 2 chaque élement des listes/tableaux"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "b42ce518",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Incrémentation de 2 de L et T\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = L.copy()\n",
    "for i in range(len(L)):\n",
    "    _L[i] = L[i] + 2\n",
    "\n",
    "# Avec L (optimisé)\n",
    "_L = [l+2 for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = T+2 # Les opérateurs mathématiques élémentaires sont applicables sur un tableau élément par élément"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7dd6cfff",
   "metadata": {},
   "source": [
    "Divisez maintenant tous les éléments des listes/tableaux par 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "74762a4b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Division par 5 de L et T\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = L.copy()\n",
    "for i in range(len(L)):\n",
    "    _L[i] = L[i] / 5\n",
    "\n",
    "# Avec L (optimisé)\n",
    "_L = [l/5 for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = T/5"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62bbf5c6",
   "metadata": {},
   "source": [
    "Calculez maintenant la racine carrée de tous les éléments des listes/tableaux"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "30c52c14",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Racine carré des éléments de L et T\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "import math\n",
    "_L = L.copy()\n",
    "for i in range(len(L)):\n",
    "    _L[i] = math.sqrt(L[i])\n",
    "\n",
    "# Avec L (optimisé)\n",
    "_L = [math.sqrt(l) for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = np.sqrt(T)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5037164d",
   "metadata": {},
   "source": [
    "Pour finir, calculez la valeur absolue de tous les éléments des listes/tableaux"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "98e955b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Valeur absolue des éléments de L et T\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "import math\n",
    "_L = L.copy()\n",
    "for i in range(len(L)):\n",
    "    _L[i] = abs(L[i])\n",
    "\n",
    "# Avec L (optimisé)\n",
    "_L = [abs(l) for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = np.abs(T)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e880170d",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Ex 3 : Extraire et combiner\n",
    "\n",
    "Durant le semestre, on va beaucoup manipuler des signaux... Rien de très étonnant... Dans cette manipulation, il est possible qu'on ait besoin de :\n",
    "- Récupérer un extrait d'un signal, une partie\n",
    "- Concaténer plusieurs signaux pour en faire un grand signal\n",
    "\n",
    "Avec cet exercice, on va découvrir comment faire ces opérations efficacement"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "b289bf10",
   "metadata": {},
   "outputs": [],
   "source": [
    "# La liste et le tableau à manipuler pour ce nouvel exercice\n",
    "L = [2,5,8,3,9,1,7,45,3,69,2,8,7,66]\n",
    "T = np.array(L)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c445836a",
   "metadata": {},
   "source": [
    "Affichez le premier élément des listes/tableaux"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "4e31d2f5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage de la première valeur de L et T\n",
    "\n",
    "# Avec L\n",
    "print(L[0])\n",
    "\n",
    "# Avec T\n",
    "print(T[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f83efaef",
   "metadata": {},
   "source": [
    "Même question, mais avec le dernier élément"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "ae08aa30",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "66\n",
      "66\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage de la dernière valeur de L et T\n",
    "\n",
    "# Avec L\n",
    "print(L[-1]) # Ou print(L[len(L)-1])\n",
    "\n",
    "# Avec T\n",
    "print(T[-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d70181dc",
   "metadata": {},
   "source": [
    "Récupérez et affichez ensuite les éléments des listes/tableaux entre les positions 5 et 9 incluses (ce qui donnera $[9,1,7,45,3]$)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "712b7505",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[9, 1, 7, 45, 3]\n",
      "[ 9  1  7 45  3]\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage des éléments entre les positions 5 et 9 incluses de L et T\n",
    "\n",
    "# Avec L\n",
    "print(L[4:9]) # Ou print(L[len(L)-1])\n",
    "\n",
    "# Avec T\n",
    "print(T[4:9])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b849400",
   "metadata": {},
   "source": [
    "Affichez maintenant un élément sur deux des listes/tableaux (index 0, 2, 4, etc.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "09d8f969",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2, 8, 9, 7, 3, 2, 7]\n",
      "[2 8 9 7 3 2 7]\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage d'un élément sur deux de L et T\n",
    "\n",
    "# Avec L\n",
    "print(L[::2])\n",
    "\n",
    "# Avec T\n",
    "print(T[::2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f7a68e2e",
   "metadata": {},
   "source": [
    "**ATTENTION :** Lorsque vous extrayez un sous-tableau NumPy, vous faites en réalité une vue du tableau, et non une copie modifiée. Cela veut dire que si vous modifiez le sous-tableau en question, vous modifiez également le tableau original. Un exemple ci-dessous:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "498dd684",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tableau tab : [1 2 3 4 5 6]\n",
      "Sous-tableau sub_tab : [2 3 4]\n",
      "Sous-tableau sub_tab après modification : [       2 10000000        4]\n",
      "Tableau tab après modification : [       1        2 10000000        4        5        6]\n"
     ]
    }
   ],
   "source": [
    "# Exemple de modification de vue d'un tableau NumPy\n",
    "\n",
    "tab = np.array([1,2,3,4,5,6])\n",
    "sub_tab = tab[1:4]\n",
    "print(f\"Tableau tab : {tab}\")\n",
    "print(f\"Sous-tableau sub_tab : {sub_tab}\")\n",
    "\n",
    "sub_tab[1] = 10000000 # On modifie une valeur du sous-tableau\n",
    "print(f\"Sous-tableau sub_tab après modification : {sub_tab}\")\n",
    "print(f\"Tableau tab après modification : {tab}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6e848a0",
   "metadata": {},
   "source": [
    "Recopiez le code ci-dessous et modifiez la ligne d'extraction (*sub_tab = tab[1:4]*) afin que la modification de *sub_tab* n'entraîne pas la modification de *tab*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d2a3706",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tableau tab : [1 2 3 4 5 6]\n",
      "Sous-tableau sub_tab : [2 3 4]\n",
      "Sous-tableau sub_tab après modification : [       2 10000000        4]\n",
      "Tableau tab après modification : [1 2 3 4 5 6]\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Modification du sous-tableau sans modifier le tableau original\n",
    "\n",
    "tab = np.array([1,2,3,4,5,6])\n",
    "sub_tab = tab.copy()[1:4] # ou tab[1:4].copy()\n",
    "print(f\"Tableau tab : {tab}\")\n",
    "print(f\"Sous-tableau sub_tab : {sub_tab}\")\n",
    "\n",
    "sub_tab[1] = 10000000 \n",
    "print(f\"Sous-tableau sub_tab après modification : {sub_tab}\")\n",
    "print(f\"Tableau tab après modification : {tab}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7724eb41",
   "metadata": {},
   "source": [
    "Créez ensuite les liste et tableau $L_{inv}$ et $T_{inv}$, à savoir les liste et tableau $L$ et $T$ mais à l'envers\n",
    "\n",
    "*Note : vous pouvez réaliser ces opérations avec de l'indexation, comme précédemment, ou alors pour le tableau NumPy avec une fonction spécifique*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "0fd83619",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER   \n",
    "# Inversion de L et T\n",
    "\n",
    "# Avec L\n",
    "L_inv = L[::-1]\n",
    "\n",
    "# Avec T\n",
    "# Méthode 1 : indexation, comme NumPy\n",
    "T_inv = T[::-1]\n",
    "\n",
    "# Méthode 2 : avec la fonction np.flip\n",
    "T_inv = np.flip(T)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19a2ff11",
   "metadata": {},
   "source": [
    "On rajoute maintenant des listes/tableaux pour la suite"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "7d2941af",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Chouette, de nouveaux éléments !\n",
    "\n",
    "L2 = [4,9,63,5,7,88,7,4,1]\n",
    "T2 = np.array(L2)\n",
    "\n",
    "L3 = [96,97,98,99]\n",
    "T3 = np.array(L3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b441d0d",
   "metadata": {},
   "source": [
    "Concaténez les listes/tableaux $L$ et $L_2$, puis $T$ et $T_2$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "c7b83773",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Concaténation de L et L2, et T et T2\n",
    "\n",
    "# Avec L et L2\n",
    "_L = L+L2\n",
    "\n",
    "# Avec T et T2\n",
    "_T = np.concatenate([T,T2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0578afaf",
   "metadata": {},
   "source": [
    "Pour finir, concaténez les listes/tableaux $L$, $L_2$ et $L_3$, puis $T$, $T_2$ et $T_3$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "a9f61dc4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Concaténation de L, L2, L3 ; puis T, T2, T3\n",
    "\n",
    "# Avec L, L2, L3\n",
    "_L = L+L2+L3\n",
    "\n",
    "# Avec T, T2, T3\n",
    "_T = np.concatenate([T,T2,T3])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f9d4ad0",
   "metadata": {},
   "source": [
    "**Note importante :** Il se peut ici que vous ayez utilisé la fonction *np.append*. Je ne dis pas que ce que vous avez fait n'est pas correct, mais cela montre en partie que vous utilisez alors un tableau NumPy comme une liste, alors que ce n'est vraiment pas la mentalité recherchée. \n",
    "\n",
    "Alors pourquoi ce n'est pas bien ? \n",
    "- Lorsque vous ajoutez un élément dans une liste avec la fonction *append*, la liste s'aggrandit avec un nouveau pointeur, mais l'objet reste le même.\n",
    "- Lorsque vous ajoutez un élément dans un tableau NumPy, vous construisez en réalité un nouveau tableau NumPy, car les **tableaux ont une taille fixe**. \n",
    "\n",
    "De ce fait, si vous avez plusieurs éléments à ajouter à un tableau, mieux vaut faire une liste, convertir en tableau NumPy, et concaténer au tableau original, ou alors trouver la bonne fonction NumPy qui permet de créer le bon tableau à ajouter. Mais je ne veux pas voir une multitude d'appels de la fonction *np.append*."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b53e7d1d",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Ex 4 : Elle est où la valeur, elle est où ?\n",
    "\n",
    "Sur nos signaux (donc tableaux), nous aurons besoin également d'extraire de l'information utile, comme par exemple la valeur maximum, mais également la position où elle se situe. Ici, on va voir ensemble quelques fonctionnalités utiles de NumPy qui vont dans ce sens."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "f355ac62",
   "metadata": {},
   "outputs": [],
   "source": [
    "# La liste et le tableau à manipuler pour ce nouvel exercice\n",
    "L = [20,5,9,7,8,66,3,14,5,32,8,77]\n",
    "T = np.array(L)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba842958",
   "metadata": {},
   "source": [
    "Affichez la valeur maximale des listes/tableaux"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "9d746d5b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "77\n",
      "77\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage de la valeur maximale de L et T\n",
    "\n",
    "# Avec L\n",
    "print(max(L))\n",
    "\n",
    "# Avec T\n",
    "print(np.max(T))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0e16fce",
   "metadata": {},
   "source": [
    "Affichez maintenant **la position** de la valeur maximale des listes/tableaux"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "eb5fcf79",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "11\n",
      "11\n",
      "11\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage de la position de la valeur maximale de L et T\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "indice_max = -1\n",
    "valeur_max = -math.inf\n",
    "for i in range(len(L)):\n",
    "    if L[i] > valeur_max:\n",
    "        valeur_max = L[i]\n",
    "        indice_max = i\n",
    "print(indice_max)\n",
    "\n",
    "# Avec L (optimisé)\n",
    "print(L.index(max(L))) # Pas si optimisé car ça parcourt deux fois la liste : une pour le max, et une pour argmax\n",
    "\n",
    "# Avec T\n",
    "print(np.argmax(T))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e828ffb8",
   "metadata": {},
   "source": [
    "On va répéter la même opération cette fois, mais avec une liste et tableau beaucoup plus gros (10 000 000 de valeurs), et chronométrer la recherche"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "9c4d1789",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "11246\n",
      "Affichage de la position de la valeur max de L_big (non optimisé): 0.5198063850402832 s\n",
      "11246\n",
      "Affichage de la position de la valeur max de L_big (optimisé): 0.10569953918457031 s\n",
      "11246\n",
      "Affichage de la position de la valeur max de T: 0.0010950565338134766 s\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage de la position de la valeur maximale de L et T\n",
    "\n",
    "T_big = np.random.randint(0,15000,(10000000))\n",
    "L_big = T_big.tolist()\n",
    "\n",
    "# Avec L_big (non optimisé)\n",
    "t_init = time.time()\n",
    "indice_max = -1\n",
    "valeur_max = -math.inf\n",
    "for i in range(len(L_big)):\n",
    "    if L_big[i] > valeur_max:\n",
    "        valeur_max = L_big[i]\n",
    "        indice_max = i\n",
    "print(indice_max)\n",
    "t_end = time.time()\n",
    "print(f\"Affichage de la position de la valeur max de L_big (non optimisé): {t_end-t_init} s\")\n",
    "\n",
    "# Avec L_big (optimisé)\n",
    "t_init = time.time()\n",
    "print(L_big.index(max(L_big))) # Pas si optimisé car ça parcourt deux fois la liste : une pour le max, et une pour argmax\n",
    "t_end = time.time()\n",
    "print(f\"Affichage de la position de la valeur max de L_big (optimisé): {t_end-t_init} s\")\n",
    "\n",
    "# Avec T_big\n",
    "t_init = time.time()\n",
    "print(np.argmax(T_big))\n",
    "t_end = time.time()\n",
    "print(f\"Affichage de la position de la valeur max de T: {t_end-t_init} s\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15126ae7",
   "metadata": {},
   "source": [
    "On peut reprendre nos listes et tableaux $L$ et $T$ pour la suite :D\n",
    "\n",
    "Retrouvez maintenant les indices des valeurs strictement supérieures à 10 dans nos listes/tableaux"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "73fd8d2d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 5, 7, 9, 11]\n",
      "[0, 5, 7, 9, 11]\n",
      "[[ 0]\n",
      " [ 5]\n",
      " [ 7]\n",
      " [ 9]\n",
      " [11]]\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Affichage de la position desvaleurs supérieures à 10 de L et T\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "indices_val_sup = []\n",
    "for i in range(len(L)):\n",
    "    if L[i] > 10:\n",
    "        indices_val_sup.append(i)\n",
    "print(indices_val_sup)\n",
    "\n",
    "# Avec L (optimisé)\n",
    "print([i for i,l in enumerate(L) if l>10])\n",
    "\n",
    "# Avec T\n",
    "print(np.argwhere(T>10))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b1f3a0e",
   "metadata": {},
   "source": [
    "*Note : Comme vous avez pu le remarquer, si vous avez bien utilisé la fonction np.argwhere, le retour de la fonction n'est pas un tableau, mais une liste de tableau. Ici, la liste ne comprend qu'un tableau, car le tableau original analysé n'a qu'une seule dimension. Si vous utilisez la fonction pour analyser un tableau à N dimensions, vous aurez une liste de N tableaux*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84c27792",
   "metadata": {},
   "source": [
    "Pour finir cet exercice, recensez toutes les valeurs différentes des listes/tableaux.\n",
    "\n",
    "*ex: pour la listes [1,2,1], les valeurs différentes à retrouver sont [1,2]*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "b5548833",
   "metadata": {},
   "outputs": [],
   "source": [
    "T = np.random.randint(0, 100, (75))\n",
    "L = T.tolist()\n",
    "\n",
    "# A COMPLETER\n",
    "# Récupération des valeurs différentes des listes/tableaux\n",
    "\n",
    "# Avec L\n",
    "L_val = []\n",
    "for element in L:\n",
    "    if element not in L_val:\n",
    "        L_val.append(element)\n",
    "        \n",
    "# Avec L (optimisé)\n",
    "L_val = list(set(L)) # Ici, on utilise une astuce en passant par un ensemble (set), qui n'autorise pas les valeurs doublons, puis on convertit cet ensemble par une liste. Ca fonctionne, mais c'est pas foufou...\n",
    "\n",
    "# Avec T\n",
    "T_val = np.unique(T)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8488c520",
   "metadata": {},
   "source": [
    "Refaites la même opération, mais en récupérant également le nombre d'occurences de chaque valeur"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "e2af89e6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Récupération des valeurs différentes des listes/tableaux, ainsi que leur nombre d'occurences\n",
    "\n",
    "# Avec L\n",
    "# On passe ici par un dictionnaire, plus adapté dans notre contexte\n",
    "L_inventaire = {}\n",
    "for element in L:\n",
    "    if element not in L_inventaire.keys():\n",
    "        L_inventaire[element] = 1\n",
    "    else:\n",
    "        L_inventaire[element]+=1\n",
    "L_val = L_inventaire.keys()\n",
    "L_occurences = L_inventaire.values()\n",
    "\n",
    "# Avec T\n",
    "T_val, T_occurences = np.unique(T, return_counts=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "891f9541",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Ex 5 : Tableau + Tableau = Tableau ?\n",
    "\n",
    "Un signal, c'est modifiable, c'est tout l'intérêt de cet EC. De ce fait, on va être amené à faire des calculs entre plusieurs signaux."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "0217d8ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Les listes et tableaux à manipuler pour ce nouvel exercice\n",
    "T1 = np.random.randint(0,10,(25))\n",
    "T2 = np.random.randint(0,10,(25))\n",
    "L1 = T1.tolist()\n",
    "L2 = T2.tolist()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ffb41fe",
   "metadata": {},
   "source": [
    "Calculez la somme des éléments des listes/tableaux (donc par exemple, la somme de tous les éléments de $L_1$, qui donnera en sortie un scalaire)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "3cfa12ae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "101\n",
      "101\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Somme des éléments L1 et T1\n",
    "\n",
    "# Avec L1\n",
    "print(sum(L1))\n",
    "\n",
    "# Avec T1\n",
    "print(np.sum(T1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a76fbb59",
   "metadata": {},
   "source": [
    "Calculez maintenant la moyenne des listes/tableaux"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "89b9d4e8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.04\n",
      "4.04\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Moyenne de L1 et T1\n",
    "\n",
    "# Avec L1\n",
    "print(sum(L1)/len(L1)) # Il n'y a pas de fonction mean disponible\n",
    "\n",
    "# Avec T1\n",
    "print(np.mean(T1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f87989a",
   "metadata": {},
   "source": [
    "Plus complexe: passons au produit scalaire de deux vecteurs. Effectuez le produit scalaire sur deux vecteurs identiques (liste ou tableau)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "8772ba61",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "601\n",
      "601\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Produit scalaire entre L1 et L1, puis T1 et T1\n",
    "\n",
    "# Avec L1 et L1\n",
    "print(sum([i*j for i,j in zip(L1,L1)]))\n",
    "\n",
    "# Avec T1 et T1\n",
    "print(np.dot(T1,T1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb3d513b",
   "metadata": {},
   "source": [
    "Répétez l'opération, mais avec 2 vecteurs différents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "7fc89b38",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "497\n",
      "497\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Produit scalaire entre L1 et L2, puis T1 et T2\n",
    "\n",
    "# Avec L1 et L2\n",
    "print(sum([i*j for i,j in zip(L1,L2)]))\n",
    "\n",
    "# Avec T1 et T2\n",
    "print(np.dot(T1,T2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c0ed06d",
   "metadata": {},
   "source": [
    "Faites la multiplication des 2 listes $L_1$ et $L_2$, élément par élément, puis avec les tableaux $T_1$ et $T_2$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "165bd7c2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[6, 0, 0, 12, 12, 40, 35, 30, 16, 63, 40, 54, 16, 0, 49, 1, 0, 18, 1, 63, 0, 3, 21, 10, 7]\n",
      "[ 6  0  0 12 12 40 35 30 16 63 40 54 16  0 49  1  0 18  1 63  0  3 21 10\n",
      "  7]\n"
     ]
    }
   ],
   "source": [
    "# A COMPLETER\n",
    "# Multiplication élément par élément entre L1 et L2, puis T1 et T2\n",
    "\n",
    "# Avec L1 et L2\n",
    "print([i*j for i,j in zip(L1,L2)])\n",
    "\n",
    "# Avec T1 et T2\n",
    "print(T1*T2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6b3391a",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Ex 6 : Opérations logiques\n",
    "\n",
    "Les modifications, c'est bien, mais c'est mieux si on peut choisir les éléments qu'on souhaite modifier, donc effectuer des modifications conditionnelles. Cela permet d'éviter d'écrire des *if, else*, et donc de gagner en clarté de code.\n",
    "\n",
    "Avant de faire ces modifications conditionnelles, il nous faut d'abord créer ce qu'on appelle des masques, à savoir des listes/tableaux de même dimensions que ceux étudiés, mais contenant uniquement des booléens (*True* ou *False*). Chaque élément du masque associé au tableau indique donc si la condition étudiée est validée par l'élement du tableau."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "bae24725",
   "metadata": {},
   "outputs": [],
   "source": [
    "# La liste et le tableau à manipuler pour ce nouvel exercice\n",
    "L = [20,5,9,7,8,66,3,14,5,32,8,77]\n",
    "T = np.array(L)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "595a05df",
   "metadata": {},
   "source": [
    "Créez un vecteur indiquant pour chaque valeur du vecteur original (L et T) si elle est supérieure strictement à 10\n",
    "\n",
    "*Par exemple, si $L = [5, 20, 9, 10, 67]$, le vecteur créé sera $L\\_sup\\_10 = [False, True, False, False, True]$*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "56567915",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Création du vecteur indiquant toutes les valeurs de L,T supérieures strictement à 10\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = []\n",
    "for l in L:\n",
    "    if l>10:\n",
    "        _L.append(True)\n",
    "    else:\n",
    "        _L.append(False)\n",
    "        \n",
    "# Avec L (optimisé)\n",
    "_L = [l>10 for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = T>10"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e6491cc",
   "metadata": {},
   "source": [
    "Répétez la même opération, mais indiquant pour chaque valeur du vecteur original si elle est inférieure ou égale à 15"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "d1e1a84e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Création du vecteur indiquant toutes les valeurs de L,T inférieures ou égales à 10\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = []\n",
    "for l in L:\n",
    "    if l<=15:\n",
    "        _L.append(True)\n",
    "    else:\n",
    "        _L.append(False)\n",
    "        \n",
    "# Avec L (optimisé)\n",
    "_L = [l<=15 for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = T<=15"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b6bab37",
   "metadata": {},
   "source": [
    "Construisez le vecteur indiquant pour chaque valeur du vecteur original si elle est supérieure ou égale à 20, ou égale à 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "3bea090b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Création du vecteur indiquant toutes les valeurs de L,T supérieures ou égales à 20 ou égales à 5\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = []\n",
    "for l in L:\n",
    "    if l>=20 or l==5:\n",
    "        _L.append(True)\n",
    "    else:\n",
    "        _L.append(False)\n",
    "        \n",
    "# Avec L (optimisé)\n",
    "_L = [l>=15 or l==5 for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = np.logical_or(T>=15,T==5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50d48581",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Ex 7 : Modifier un vecteur conditionnellement\n",
    "\n",
    "Bon, c'est cool les masques, mais ça ne sert pas vraiment à grand chose si on n'en fait rien. On peut maintenant s'attaquer aux modifications conditionnelles. Les tableaux, associés à leurs masques, peuvent alors être modifiés partiellement (à savoir uniquement les valeurs qui remplissent ou non la condition étudiée).\n",
    "\n",
    "Avant d'utiliser les masques, on va voir quelques fonctions utiles de NumPy qui calculent elles-mêmes le masque à utiliser."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "189ed7be",
   "metadata": {},
   "outputs": [],
   "source": [
    "# La liste et le tableau à manipuler pour ce nouvel exercice\n",
    "T = np.random.rand(20)*20\n",
    "L = T.tolist()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3409f020",
   "metadata": {},
   "source": [
    "Modifiez les listes/tableaux pour limiter les valeurs entre 5 et 15\n",
    "\n",
    "*(c'est-à-dire que toutes les valeurs en-dessous de 5 passent à 5, et toutes les valeurs au-dessus de 15 passent à 15)*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "61db505b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Limitation des valeurs de L et T entre 10 et 15\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = []\n",
    "for l in L:\n",
    "    if l<5:\n",
    "        _L.append(5)\n",
    "    elif l>15:\n",
    "        _L.append(15)\n",
    "    else:\n",
    "        _L.append(l)\n",
    "        \n",
    "# Avec L (optimisé)\n",
    "_L = [min(15,max(5,l)) for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = np.clip(T,5,15)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29034193",
   "metadata": {},
   "source": [
    "Arrondissez maintenant les valeurs des listes/tableaux à l'entier le plus proche"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "3424e734",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Arrondi des valeurs de L et T\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = []\n",
    "for l in L:\n",
    "    _L.append(round(l))\n",
    "        \n",
    "# Avec L (optimisé)\n",
    "_L = [round(l) for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = np.round(T)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35cf08a1",
   "metadata": {},
   "source": [
    "Répétez la même opération, mais en arrondissant:\n",
    "- à la valeur entière plancher\n",
    "- à la valeur entière plafond"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "7e5d00da",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Arrondi des valeurs de L et Tn à la valeur entière plancher\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = []\n",
    "for l in L:\n",
    "    _L.append(math.floor(l))\n",
    "        \n",
    "# Avec L (optimisé)\n",
    "_L = [math.floor(l) for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = np.floor(T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "b7e34689",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Arrondi des valeurs de L et Tn à la valeur entière plafond\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = []\n",
    "for l in L:\n",
    "    _L.append(math.ceil(l))\n",
    "        \n",
    "# Avec L (optimisé)\n",
    "_L = [math.ceil(l) for l in L]\n",
    "\n",
    "# Avec T\n",
    "_T = np.ceil(T)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "625e9b70",
   "metadata": {},
   "source": [
    "Dernière étape, plus complexe avec des nouveaux vecteurs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "2233837f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n"
     ]
    }
   ],
   "source": [
    "# Les nouveaux vecteurs\n",
    "L = list(range(20))\n",
    "T = np.array(L)\n",
    "print(L)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e519611e",
   "metadata": {},
   "source": [
    "Modifiez les vecteurs pour que toutes les valeurs paires soient égales à -1. Les valeurs impaires resteront inchangées.\n",
    "\n",
    "*Indice : pour NumPy, je vous invite à voir la documentation de la fonction np.where*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "b57551fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Modification des valeurs paires (passage à -1) de L et T\n",
    "\n",
    "# Avec L (non optimisé)\n",
    "_L = []\n",
    "for l in L:\n",
    "    if l%2:\n",
    "        _L.append(l)\n",
    "    else:\n",
    "        _L.append(-1)\n",
    "        \n",
    "# Avec L (optimisé)\n",
    "_L = [l if l%2 else -1 for l in L]\n",
    "\n",
    "# Avec T\n",
    "# np.where est une fonction plus complexe, qu'on pourra utiliser durant le semestre pour les plus avancés. Elle permet de faire des modifications conditionnelles\n",
    "# Les arguments fonctionnent comme ceux-ci:\n",
    "# np.where(condition, val_si_vrai, val_si_faux)\n",
    "# Arguments:\n",
    "# - condition : un tableau de booléen de taille quelconque. Ici, dans notre exemple, le tableau de booléen correspond aux valeurs de T qui sont impaires\n",
    "# - val_si_vrai : ce qu'il faut placer aux indices où la condition est vraie. Cette argument peut être une valeur unique, ou un tableau de la même taille que la variable condition.\n",
    "#                 Ici, val_si_vrai est notre tableau T, de même taille que condition: toutes les valeurs qui respectent la condition (valeurs impaires) restent inchangées\n",
    "# - val_si_vrai : ce qu'il faut placer aux indices où la condition est fausse. Cette argument peut être une valeur unique, ou un tableau de la même taille que la variable condition.\n",
    "#                 Ici, val_si_faux est -1: toutes les valeurs qui ne respectent pas la condition (valeurs paires) prennent comme valeur -1\n",
    "\n",
    "_T = np.where(T%2, T, -1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16438566",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Pour finir ce TDM, je vous invite donc, si ce n'est pas fait, **à noter de côté** les différentes fonctions NumPy que vous avez utilisé, sur une feuille ou un éditeur de texte. A vous ensuite, durant les prochaines semaines, d'utiliser les différentes fonctions vues aujourd'hui afin de coder efficacement.\n",
    "\n",
    "*De plus, comme vous avez pu le voir, c'est souvent plus rapide à écrire en 1/2 lignes, donc ça veut dire que vous finissez les TDM plus tôt ;)*"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "deep",
   "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.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
