├── .gitignore ├── Cal1-introPython.ipynb ├── Cal2-PythonPandas.ipynb ├── Cal3-PythonGraphes.ipynb ├── Cal4-PythonProg.ipynb ├── Cal5-PythonSklearnApprent.ipynb ├── LICENSE ├── README.md └── titanic-train.csv /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.gz 3 | **/.ipynb_checkpoints/ 4 | .ipynb_checkpoints/ 5 | __init__.py 6 | __pycache__/ 7 | data.csv 8 | data.npy 9 | filename.png 10 | testM.py 11 | titan.h5 12 | titan_tree.png 13 | -------------------------------------------------------------------------------- /Cal1-introPython.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "
\n", 8 | "\"INSA\"/ \n", 9 | "\n", 10 | "\"Wikistat\"/\n", 11 | "\n", 12 | "
" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "# \"Python\"/ [pour Statistique et Science des Données](https://github.com/wikistat/Intro-Python)" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "# Introduction à \"Python\"/ pour Calcul Scientifique - Statistique" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "**Résumé** Présentation de pyhton, exécution de commandes interactives ou de scripts avec un IDE, utilisation d'un calepin; les types et structures élémentaires de données, les structures de contrôle, les fonctions, classes et modules. Introduction à l'utilisation des librairies scientifiques: `Numpy, Matplotlib, Scipy` et au type `array`." 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "## 1 Introduction" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "### 1.1 Prérequis\n", 48 | "Ce calepin introduit le langage libre Python et décrit les premières commandes nécessaires au pré-traitement des données avant l'utilisation de méthodes statistiques avec ce langage ou avec R. Les aspects statistiques développés dans les différentes vignettes de [Wikistat](http://wikistat.fr) sont supposés acquis ainsi qu'une connaissance des principes élémentaires de programmation dans un langage matriciel comme R ou Matlab. \n", 49 | "\n", 50 | "Pour des approfondissements, il existe de très nombreuses ressources pédagogiques accessibles sur la toile dont le [tutoriel officiel](https://docs.python.org/3/tutorial/index.html) de Python 3.4., les sites [`pythontutor.com`](http://pythontutor.com/), [`courspython.com`](http://www.courspython.com/), le livre de [Sheppard (2014)](http://www.kevinsheppard.com/images/0/09/Python_introduction.pdf) qui présentent une introduction à Python pour l'\\'Econométrie et la Statistique et celui de [Mac Kinney (2013)](http://it-ebooks.info/book/1041/), principal auteur de la bibliothèque `pandas`. \n", 51 | "\n", 52 | "### 1.2 Installation\n", 53 | "Python et ses librairies peuvent être installés dans quasiment tout environnement matériel et système d'exploitation à partir du [site officiel](https://www.python.org/downloads/). Voici les principales librairies scientifiques définissant des structures de données et fonctions de calcul indispensables. \n", 54 | "- `ipython`: pour une utilisation interactive de Python, \n", 55 | "- `numpy`: pour utiliser vecteurs et tableaux, \n", 56 | "- `scipy`: intègre les principaux algorithmes numériques, \n", 57 | "- `matplotlib`: pour les graphes, \n", 58 | "- `pandas`: structure de données et feuilles de calcul, \n", 59 | "- `patsy`: formules statistiques,\n", 60 | "- `statsmodels`: modélisation statistique,\n", 61 | "- `seaborn`: visualisation de données,\n", 62 | "- `scikit-learn`: algorithmes d'apprentissage statistique.\n", 63 | "\n", 64 | "Néanmoins, compte tenu de la complexité de l'opération, il est plus simple pour le néophyte, surtout sous Windows, de faire appel à une procédure d'installation intégrant les principales librairies. Ces procédures sont développées par des entreprises commerciales mais libres de droits pour une utilisation académique.\n", 65 | "- *Continuum Analytics* propose [Anaconda](https://store.continuum.io/cshop/anaconda/) avec au choix la version 3.4 ou celle 2.7. `Conda` est l'utilitaire (commande en ligne) qui permet les mises à jour et installations des librairies complémentaires.\n", 66 | "- *Enthought* propose [Canopy](https://www.enthought.com/products/canopy/) qui n'installe pour le moment que la version 2.7 et intègre un *package manager* avec interface graphique. Attention, certaines librairies même \"collectives\" ne sont disponibles que dans la version commerciale ou celle \"académique\" de Canopy après inscription. \n", 67 | "\n", 68 | "D'un point de vue légal, les propositions sont identiques mais Canopy nécessite la création d'un compte académique. Seul \"souci\", ces versions n'incluent que les versions dites stables des différentes librairies et donc avec un temps de retard vis-à-vis des versions encore développement.\n", 69 | "\n", 70 | "C'est [Anaconda](https://store.continuum.io/cshop/anaconda/) qui est utilisée ici, car préférable pour un poste personnel, en version 2.7; C'est aussi la version installée par le CSN de l'INSA dans les salles de TP.\n", 71 | "\n", 72 | "## 2 Utilisation de Python\n", 73 | "Python exécute programmes ou scripts, programmes qui peuvent être pré-compilés pour plus d'efficacité. Ce langage s'exécute également à l'aide d'un interprète de commande (`IDLE`) ou {`IPython`) de manière interactive. En situation pédagogique, c'est l'utilisation et la réalisation d'un *notebook Ipython* (calepin) ou plutôt maintenant *Jupyter* qui est privilégiée à partir d'un simple navigateur (éviter *internet explorer*).\n", 74 | "\n", 75 | "\n", 76 | "### 2.1 Calepin *Jupyter*\n", 77 | "\n", 78 | "Les commandes sont regroupées dans des cellules suivies de leur résultat après exécution. Ces résultats et commentaires sont stockés dans un fichier spécifique `.ipynb` et sauvegardés. Les commandes LaTeX sont acceptées pour intégrer des formules, la mise en page est assurée par des balises HTML ou [*Markdown*](http://fr.wikipedia.org/wiki/Markdown). \n", 79 | "\n", 80 | "La commande de sauvegarde permet également d'extraire les seules commandes Python dans un fichier d'extension `.py`. C'est une façon simple et efficace de conserver tout l'historique d'une analyse pour en faire une présentation ou créer un tutoriel. Le calepin peut être en effet chargé sous un autre format: page `html`, fichier `.pdf` ou diaporama.\n", 81 | "\n", 82 | "Le projet [Jupyter](http://jupyter.org/) propose cet environnement de calepin pour beaucoup de langages (Pyhton, Julia, Scala...) dont R. Il devient un outil indispensable pour assurer simplement la *reproductibilité* des analyses. \n", 83 | "\n", 84 | "L'ouverture d'un navigateur sur un calepin (Ipython ou Jupyter) est obtenu, selon l'installation, à partir des menus ou en exécutant: \n", 85 | "`jupyter notebook`\n", 86 | "ou \n", 87 | "`ipython notebook` \n", 88 | "dans une fenêtre de commande.\n", 89 | "Une fois le calepin ouvert, \n", 90 | "- Entrer des commandes Python dans une cellule,\n", 91 | "- Cliquer sur le bouton d'exécution de la cellule.\n", 92 | "- Ajouter une ou des cellules de commentaires et balises HTML ou [Markdown](http://fr.wikipedia.org/wiki/Markdown).\n", 93 | "Itérer l'ajout de cellules. Une fois l'exécution terminée:\n", 94 | "- Sauver le calepin `.ipynb` \n", 95 | "- Charger éventuellement une version `.html` pour une page web.\n", 96 | "- Charger le fichier `.py` regroupant les commandes python pour une version opérationnelle.\n", 97 | "\n", 98 | "**Attention** Un calepin de IPython ou Jupyter est un outil de travail exploratoire efficace et un compte rendu nécessairement *chronologique* d'exécution; ce n'est pas le *rapport* d'une étude statistique dont l'organisation suit des [règles spécifiques](http://wikistat.fr/pdf/st-m-redac-rapport-stat.pdf). \n", 99 | "\n", 100 | "\n", 101 | "\n", 102 | "\n", 103 | "### 2.2 IDE `Spyder`\n", 104 | "Pour la réalisation d'applications et programmes plus complexes, l'usage d'un IDE (*integrated Development Environment*) libre comme [Spyder](http://code.google.com/p/spyderlib/) est recommandé. Ce dernier est intégré à la distribution `Anaconda` et sa présentation proche de celles de Matlab ou RStudio. Cet environnement exécutant IPython reconnaît évidemment les commandes précédentes. \n", 105 | " \n", 106 | "Comme pour RStudio, `Spider` ouvre plusieurs fenêtres:\n", 107 | "- un éditeur de commandes dont les boutons du menu exécutent tout le fichier ou interactivement la cellule courante, sauvent le fichier, contrôlent le débogage. Une cellule débute par la balise: `#%%`.\n", 108 | "- Un explorateur d'objets avec aide en ligne, des variables en cours, du répertoire courant. Les boutons de l'explorateur de variables permettent de supprimer, sauver les objets créés ou encore d'importer des données.\n", 109 | "- La console IPython avec les résultats et son historique.\n", 110 | "\n", 111 | "### 2.3 Exemple\n", 112 | "En résumé, utiliser un calepin pour des analyses exploratoires élémentaires et un IDE `Spyder` ou `Eclipse` (un peu compliqué!) pour la construction de programmes et modules. \n", 113 | "\n", 114 | "Selon l'installation et à partir du répertoire de travail, exécuter la commande:\n", 115 | "\n", 116 | "`jupyter notebook` \n", 117 | " \n", 118 | "qui ouvre le navigateur par défaut. \n", 119 | "\n", 120 | "Sur les postes géré par le CSN de l'INSA, si cela ne marche pas, il peut être nécessaire d'ajouter une variable d'environnement:\n", 121 | "\n", 122 | "`export PATH=/usr/local/insa/anaconda/bin:$PATH`\n", 123 | "\n", 124 | "Entrer les commandes ci-dessous dans le calepin et les exécuter cellule par cellule en cliquant sur le bouton d'exécution de la cellule courante." 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": { 131 | "collapsed": true 132 | }, 133 | "outputs": [], 134 | "source": [ 135 | "# Ceci est le début d'une session Python gérée à l'aide d'un calepin.\n", 136 | "# Le script est divisé en cellules avec généralement l'affichage d'au plus un résultat par cellule.\n", 137 | "## Importer les librairies nécessaires \n", 138 | "import matplotlib.pyplot as plt\n", 139 | "import numpy as np\n", 140 | "import pandas as pd\n", 141 | "from pylab import *\n", 142 | "import os \n", 143 | "## Définir si nécessaire le répertoire courant spécifique \n", 144 | "## à l'utilisateur\n", 145 | "os.chdir(r'.')\n", 146 | "## Commande magique pour obtenir les graphiques dans le calepin\n", 147 | "%matplotlib inline" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [ 156 | "#%% Créer un data frame avec pandas\n", 157 | "data = pd.DataFrame({\n", 158 | " 'Gender': ['f', 'f', 'm', 'f', 'm', 'm', 'f', 'm', 'f', 'm'],\n", 159 | " 'TV': [3.4, 3.5, 2.6, 4.7, 4.1, 4.0, 5.1, 4.0, 3.7, 2.1]})\n", 160 | "data" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "# Génération de variables aléatoires gaussiennes\n", 170 | "xx = randn(100,100)\n", 171 | "y = mean(xx,0)\n", 172 | "# graphique\n", 173 | "plot(y)\n", 174 | "show()" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "## 3. Types de données" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "### 3.1 Scalaires et chaînes\n", 189 | "La déclaration des variables est implicite ({integer, float, boolean, string), la syntaxe est très proche de celle de R mais il n'y a pas de type `factor`." 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [ 198 | "a=3 # est un entier\n", 199 | "b=1. # est un flottant\n", 200 | "# Attention:\n", 201 | "a/2 # a pour résultat 1.5 en Python 3.4 \n", 202 | " # mais 1 en 2.7" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "Opérateurs de comparaison : `==, >, <, !=` de résultat booléen." 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "# Comparaison\n", 219 | "a==b " 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": null, 225 | "metadata": {}, 226 | "outputs": [], 227 | "source": [ 228 | "# affichage et type des variables\n", 229 | "type(a)" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "# Chaîne de caractère\n", 239 | "a='bonjour '\n", 240 | "b='le '\n", 241 | "c='monde'\n", 242 | "a+b+c" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "Attention à l'encodage des caractères. Ce n'est pas le même en python 2 (ascii) et python 3 (utf-8). Ce n'est pas fait dans ces exemples élémentaires mais il est vivement recommander de gérer systématiquement des textes complexes avec caractères spéciaux (accents, guillements...) en utf-8" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": null, 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [ 258 | "a=u'bonjour' # encodage utf-8\n", 259 | "type(a)" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "a='bonjour' # encodage ascii\n", 269 | "type(a)" 270 | ] 271 | }, 272 | { 273 | "cell_type": "markdown", 274 | "metadata": {}, 275 | "source": [ 276 | "### 3.2 Structures de base" 277 | ] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": {}, 282 | "source": [ 283 | "#### Listes\n", 284 | "Les listes permettent des combinaisons de types. \n", 285 | "**Attention**, le premier élément d'une liste ou d'un tableau est indicé par **0**, pas par 1." 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": null, 291 | "metadata": {}, 292 | "outputs": [], 293 | "source": [ 294 | "# exemples de listes\n", 295 | "liste_A = [0,3,2,'hi']\n", 296 | "liste_B = [0,3,2,4,5,6,1]\n", 297 | "liste_C = [0,3,2,'hi',[1,2,3]] \n", 298 | "# Elément d'une liste \n", 299 | "liste_A[1]" 300 | ] 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": null, 305 | "metadata": {}, 306 | "outputs": [], 307 | "source": [ 308 | "liste_C[-1] # dernier élément" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": null, 314 | "metadata": {}, 315 | "outputs": [], 316 | "source": [ 317 | "liste_C[-1][0]" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": null, 323 | "metadata": {}, 324 | "outputs": [], 325 | "source": [ 326 | "liste_C[-2]" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": null, 332 | "metadata": {}, 333 | "outputs": [], 334 | "source": [ 335 | "liste_B[0:2] # Sous-liste" 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": null, 341 | "metadata": {}, 342 | "outputs": [], 343 | "source": [ 344 | "liste_B[0:5:2] # début:fin:pas" 345 | ] 346 | }, 347 | { 348 | "cell_type": "code", 349 | "execution_count": null, 350 | "metadata": {}, 351 | "outputs": [], 352 | "source": [ 353 | "liste_B[::-1]" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "metadata": {}, 360 | "outputs": [], 361 | "source": [ 362 | "# Fonctions de listes\n", 363 | "List=[3,2,4,1]\n", 364 | "List.sort() \n", 365 | "print(List)" 366 | ] 367 | }, 368 | { 369 | "cell_type": "code", 370 | "execution_count": null, 371 | "metadata": {}, 372 | "outputs": [], 373 | "source": [ 374 | "List.append('hi')\n", 375 | "print(List)" 376 | ] 377 | }, 378 | { 379 | "cell_type": "code", 380 | "execution_count": null, 381 | "metadata": {}, 382 | "outputs": [], 383 | "source": [ 384 | "List.count(3) " 385 | ] 386 | }, 387 | { 388 | "cell_type": "code", 389 | "execution_count": null, 390 | "metadata": {}, 391 | "outputs": [], 392 | "source": [ 393 | "List.extend([7,8,9])\n", 394 | "print(List)" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": null, 400 | "metadata": {}, 401 | "outputs": [], 402 | "source": [ 403 | "List.append([10,11,12])\n", 404 | "print(List)" 405 | ] 406 | }, 407 | { 408 | "cell_type": "markdown", 409 | "metadata": {}, 410 | "source": [ 411 | "#### t-uple\n", 412 | "Un tuple est similaire à une liste mais ne peut être modifié, il est défini par des parenthèses." 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": null, 418 | "metadata": {}, 419 | "outputs": [], 420 | "source": [ 421 | "MyTuple=(0,3,2,'h')\n", 422 | "MyTuple[1]" 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": null, 428 | "metadata": {}, 429 | "outputs": [], 430 | "source": [ 431 | "MyTuple[1]=10 # TypeError: \"tuple\" object" 432 | ] 433 | }, 434 | { 435 | "cell_type": "markdown", 436 | "metadata": {}, 437 | "source": [ 438 | "#### Dictionnaire\n", 439 | "Un dictionnaire est similaire à une liste mais chaque entrée est assignée par une clé / un nom, il est défini avec des accolades. Cet objet est utilisé pour la construction de l'index des colonnes (variables) du type *DataFrame* de la librairie `pandas`.\n" 440 | ] 441 | }, 442 | { 443 | "cell_type": "code", 444 | "execution_count": null, 445 | "metadata": {}, 446 | "outputs": [], 447 | "source": [ 448 | "months = {'Jan':31 , 'Fev': 28, 'Mar':31}\n", 449 | "months['Jan'] \t" 450 | ] 451 | }, 452 | { 453 | "cell_type": "code", 454 | "execution_count": null, 455 | "metadata": {}, 456 | "outputs": [], 457 | "source": [ 458 | "months.keys()" 459 | ] 460 | }, 461 | { 462 | "cell_type": "code", 463 | "execution_count": null, 464 | "metadata": {}, 465 | "outputs": [], 466 | "source": [ 467 | "months.values()" 468 | ] 469 | }, 470 | { 471 | "cell_type": "code", 472 | "execution_count": null, 473 | "metadata": {}, 474 | "outputs": [], 475 | "source": [ 476 | "months.items()" 477 | ] 478 | }, 479 | { 480 | "cell_type": "markdown", 481 | "metadata": {}, 482 | "source": [ 483 | "## 4. Syntaxe de Python" 484 | ] 485 | }, 486 | { 487 | "cell_type": "markdown", 488 | "metadata": {}, 489 | "source": [ 490 | "### 4.1 Structures de contrôle élémentaires\n", 491 | "Un bloc de commandes ou de codes est défini par *deux points suivis d'une indentation fixe*. Cela oblige à l'écriture de codes faciles à lire mais à être très attentif sur la gestion des indentations car la fin d'indentation signifie la fin d'un bloc de commandes." 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": {}, 497 | "source": [ 498 | "#### Structure conditionnelle" 499 | ] 500 | }, 501 | { 502 | "cell_type": "code", 503 | "execution_count": null, 504 | "metadata": {}, 505 | "outputs": [], 506 | "source": [ 507 | "# si alors sinon\n", 508 | "a=2\n", 509 | "if a>0:\n", 510 | " b=0\n", 511 | " print(b)\n", 512 | "else:\n", 513 | " b=-1\n", 514 | "print(b)" 515 | ] 516 | }, 517 | { 518 | "cell_type": "markdown", 519 | "metadata": {}, 520 | "source": [ 521 | "#### Structure itérative" 522 | ] 523 | }, 524 | { 525 | "cell_type": "code", 526 | "execution_count": null, 527 | "metadata": {}, 528 | "outputs": [], 529 | "source": [ 530 | "for i in range(4):\n", 531 | " print(i)" 532 | ] 533 | }, 534 | { 535 | "cell_type": "code", 536 | "execution_count": null, 537 | "metadata": {}, 538 | "outputs": [], 539 | "source": [ 540 | "for i in range(1,8,2):\n", 541 | " print(i)" 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": null, 547 | "metadata": {}, 548 | "outputs": [], 549 | "source": [ 550 | "# Définition d'une fonction\n", 551 | "def pythagorus(x,y):\n", 552 | " \"\"\" Calcule l'hypotenuse d'un triangle \"\"\"\n", 553 | " r = pow(x**2+y**2,0.5)\n", 554 | " return x,y,r\n", 555 | "pythagorus(3,4)" 556 | ] 557 | }, 558 | { 559 | "cell_type": "markdown", 560 | "metadata": {}, 561 | "source": [ 562 | "### 4.2 Fonctions" 563 | ] 564 | }, 565 | { 566 | "cell_type": "code", 567 | "execution_count": null, 568 | "metadata": {}, 569 | "outputs": [], 570 | "source": [ 571 | "# exemple d'appel\n", 572 | "pythagorus(x=3,y=4)" 573 | ] 574 | }, 575 | { 576 | "cell_type": "code", 577 | "execution_count": null, 578 | "metadata": {}, 579 | "outputs": [], 580 | "source": [ 581 | "# aide intégrée\n", 582 | "help(pythagorus)" 583 | ] 584 | }, 585 | { 586 | "cell_type": "code", 587 | "execution_count": null, 588 | "metadata": {}, 589 | "outputs": [], 590 | "source": [ 591 | "pythagorus.__doc__" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": null, 597 | "metadata": {}, 598 | "outputs": [], 599 | "source": [ 600 | "# Valeurs des paramètres par défaut\n", 601 | "def pythagorus(x=1,y=1):\n", 602 | " \"\"\" calcule l'hypoténuse d'un triangle \"\"\"\n", 603 | " r = pow(x**2+y**2,0.5)\n", 604 | " return x,y,r\n", 605 | "pythagorus()" 606 | ] 607 | }, 608 | { 609 | "cell_type": "markdown", 610 | "metadata": {}, 611 | "source": [ 612 | "### 4.4 Modules et librairies\n", 613 | "#### Modules \n", 614 | "Un module contient plusieurs fonctions et commandes qui sont regroupées dans un fichier d'extension `.py`. Insérer un fichier vide de nom `_init_.py` dans chaque dossier et sous-dossier contenant un module à importer. Un module est appelé par la commande `import`. Un module est considéré comme un script s'il contient des commandes. Lors de l'import d'un script, les commandes sont exécutées tandis que les fonctions sont seulement chargées." 615 | ] 616 | }, 617 | { 618 | "cell_type": "markdown", 619 | "metadata": {}, 620 | "source": [ 621 | "Commencer par définir un module dans un fichier texte contenant les commandes suivantes.\n", 622 | "\n", 623 | "``\n", 624 | "def DitBonjour():\n", 625 | " print(\"Bonjour\")\n", 626 | "def DivBy2(x):\n", 627 | " return x/2\n", 628 | "``\n", 629 | "\n", 630 | "Sauver le fichier avec pour nom `testM.py` dans le répertoire courant.\n", 631 | "\n", 632 | "Il est possible d'importer toutes les fonctions en une seule commande `import`. \n" 633 | ] 634 | }, 635 | { 636 | "cell_type": "code", 637 | "execution_count": null, 638 | "metadata": {}, 639 | "outputs": [], 640 | "source": [ 641 | "import testM\n", 642 | "testM.DitBonjour()" 643 | ] 644 | }, 645 | { 646 | "cell_type": "code", 647 | "execution_count": null, 648 | "metadata": {}, 649 | "outputs": [], 650 | "source": [ 651 | "print(testM.DivBy2(10))" 652 | ] 653 | }, 654 | { 655 | "cell_type": "code", 656 | "execution_count": null, 657 | "metadata": {}, 658 | "outputs": [], 659 | "source": [ 660 | "from testM import *\n", 661 | "DitBonjour()" 662 | ] 663 | }, 664 | { 665 | "cell_type": "code", 666 | "execution_count": null, 667 | "metadata": {}, 668 | "outputs": [], 669 | "source": [ 670 | "print(DivBy2(10))" 671 | ] 672 | }, 673 | { 674 | "cell_type": "markdown", 675 | "metadata": {}, 676 | "source": [ 677 | "Ou seulement celles qui seront utilisées. Préférer cette dernière méthode pour les grosses librairies." 678 | ] 679 | }, 680 | { 681 | "cell_type": "code", 682 | "execution_count": null, 683 | "metadata": {}, 684 | "outputs": [], 685 | "source": [ 686 | "import testM as tm\n", 687 | "tm.DitBonjour()" 688 | ] 689 | }, 690 | { 691 | "cell_type": "code", 692 | "execution_count": null, 693 | "metadata": {}, 694 | "outputs": [], 695 | "source": [ 696 | "print(tm.DivBy2(10))\n", 697 | "# délétion des objets\n", 698 | "%reset " 699 | ] 700 | }, 701 | { 702 | "cell_type": "code", 703 | "execution_count": null, 704 | "metadata": {}, 705 | "outputs": [], 706 | "source": [ 707 | "from testM import DitBonjour\n", 708 | "DitBonjour()" 709 | ] 710 | }, 711 | { 712 | "cell_type": "code", 713 | "execution_count": null, 714 | "metadata": {}, 715 | "outputs": [], 716 | "source": [ 717 | "print(DivBy2(10)) # erreur" 718 | ] 719 | }, 720 | { 721 | "cell_type": "markdown", 722 | "metadata": {}, 723 | "source": [ 724 | "Lors de son premier appel, un module est pré-compilé dans un fichier `.pyc` qui est utilisé pour les appels suivants. **Attention**, si le fichier a été modifié / corrigé, il a besoin d'être rechargé par la commande `reload(name)`.\n", 725 | "\n", 726 | "Une librairie (*package*) regroupe plusieurs modules dans différents sous-répertoires. Le chargement spécifique d'un des modules se fait en précisant le chemin. \n", 727 | "`import sound.effects.echo`" 728 | ] 729 | }, 730 | { 731 | "cell_type": "markdown", 732 | "metadata": {}, 733 | "source": [ 734 | "## 5. Calcul scientifique\n", 735 | "Voici trois des principales librairies indispensables au calcul scientifique. Deux autres librairies: `pandas`, `scikit-learn`, sont exposées en détail dans des calepins spécifiques." 736 | ] 737 | }, 738 | { 739 | "cell_type": "markdown", 740 | "metadata": {}, 741 | "source": [ 742 | "### 5.1 Principales librairies ou *packages*" 743 | ] 744 | }, 745 | { 746 | "cell_type": "markdown", 747 | "metadata": {}, 748 | "source": [ 749 | "#### `NumPy`\n", 750 | "Cette librairie définit le type de données `array` ainsi que les fonctions de calcul qui y sont associées. Il contient aussi quelques fonctions d'algèbre linéaire et statistiques. \n", 751 | "Il n'est finalement utilisé que pour la définition du type `array` car les fonctions numériques sont beaucoup plus développées dans `SciPy`. \n" 752 | ] 753 | }, 754 | { 755 | "cell_type": "markdown", 756 | "metadata": {}, 757 | "source": [ 758 | "#### `Matplotlib`\n", 759 | "Celle-ci propose des fonctions de visualisation / graphs avec des commandes proches de celles de Matlab. Aussi connue sous le nom de `pylab`. La [gallerie](http://matplotlib.org/1.3.1/gallery.html) de cette librairie propose tout un ensemble d'exemples de graphiques avec le code Python pour les générer. " 760 | ] 761 | }, 762 | { 763 | "cell_type": "code", 764 | "execution_count": null, 765 | "metadata": {}, 766 | "outputs": [], 767 | "source": [ 768 | "# Importation\n", 769 | "import numpy as np\n", 770 | "from pylab import *\n", 771 | "gaussian = lambda x: np.exp(-(0.5-x)**2/1.5)\n", 772 | "x=np.arange(-2,2.5,0.01)\n", 773 | "y=gaussian(x)\n", 774 | "plot(x,y)\n", 775 | "xlabel(\"x values\")\n", 776 | "ylabel(\"y values\")\n", 777 | "title(\"Gaussian function\")\n", 778 | "show()" 779 | ] 780 | }, 781 | { 782 | "cell_type": "markdown", 783 | "metadata": {}, 784 | "source": [ 785 | "#### `SciPy`\n", 786 | "Cette librairie est un ensemble très complet de modules d'algèbre linéaire, statistiques et autres algorithmes numériques. Le site de la documentation en fournit la [liste](http://docs.scipy.org/doc/scipy/reference). " 787 | ] 788 | }, 789 | { 790 | "cell_type": "markdown", 791 | "metadata": {}, 792 | "source": [ 793 | "### 5.2 Type Array\n", 794 | "C'est de loin la structure de données la plus utilisée pour le calcul scientifique sous Python. Elle décrit des tableaux ou *matrices* multi-indices de dimension $ n = 1, 2, 3, \\ldots , 40$. Tous les éléments sont de même type (booléen, entier, réel, complexe). \n", 795 | "\n", 796 | "Il est possible de contrôler précisément le type d'un `array`, par exemple pour gagner de la place en mémoire, en codant les entiers sur 8, 16, 32 ou 64 bits, de même pour les réels (*float*) ou les complexes.\n", 797 | "\n", 798 | "Les tableaux ou tables de données (*data frame*), bases d'analyses statistiques et regroupant des objets de types différents sont décrits avec la librairie `pandas`.\n", 799 | "#### Définition du type `array`" 800 | ] 801 | }, 802 | { 803 | "cell_type": "code", 804 | "execution_count": null, 805 | "metadata": {}, 806 | "outputs": [], 807 | "source": [ 808 | "# Importation\n", 809 | "import numpy as np\n", 810 | "my_1D_array = np.array([4,3,2])\n", 811 | "print(my_1D_array)" 812 | ] 813 | }, 814 | { 815 | "cell_type": "code", 816 | "execution_count": null, 817 | "metadata": {}, 818 | "outputs": [], 819 | "source": [ 820 | "my_2D_array = np.array([[1,0,0],[0,2,0],[0,0,3]])\n", 821 | "print(my_2D_array)" 822 | ] 823 | }, 824 | { 825 | "cell_type": "code", 826 | "execution_count": null, 827 | "metadata": {}, 828 | "outputs": [], 829 | "source": [ 830 | "myList=[1,2,3]\n", 831 | "my_array = np.array(myList)\n", 832 | "print(my_array)" 833 | ] 834 | }, 835 | { 836 | "cell_type": "code", 837 | "execution_count": null, 838 | "metadata": {}, 839 | "outputs": [], 840 | "source": [ 841 | "a=np.array([[0,1],[2,3],[4,5]])\n", 842 | "a[2,1]" 843 | ] 844 | }, 845 | { 846 | "cell_type": "code", 847 | "execution_count": null, 848 | "metadata": {}, 849 | "outputs": [], 850 | "source": [ 851 | "a[:,1]" 852 | ] 853 | }, 854 | { 855 | "cell_type": "markdown", 856 | "metadata": {}, 857 | "source": [ 858 | "#### Fonctions de type `array`" 859 | ] 860 | }, 861 | { 862 | "cell_type": "code", 863 | "execution_count": null, 864 | "metadata": {}, 865 | "outputs": [], 866 | "source": [ 867 | "np.arange(5)" 868 | ] 869 | }, 870 | { 871 | "cell_type": "code", 872 | "execution_count": null, 873 | "metadata": {}, 874 | "outputs": [], 875 | "source": [ 876 | "np.ones(3)" 877 | ] 878 | }, 879 | { 880 | "cell_type": "code", 881 | "execution_count": null, 882 | "metadata": {}, 883 | "outputs": [], 884 | "source": [ 885 | "np.ones((3,4))" 886 | ] 887 | }, 888 | { 889 | "cell_type": "code", 890 | "execution_count": null, 891 | "metadata": {}, 892 | "outputs": [], 893 | "source": [ 894 | "np.eye(3)" 895 | ] 896 | }, 897 | { 898 | "cell_type": "code", 899 | "execution_count": null, 900 | "metadata": {}, 901 | "outputs": [], 902 | "source": [ 903 | "np.linspace(3, 7, 3)" 904 | ] 905 | }, 906 | { 907 | "cell_type": "code", 908 | "execution_count": null, 909 | "metadata": {}, 910 | "outputs": [], 911 | "source": [ 912 | "np.mgrid[0:3,0:2]" 913 | ] 914 | }, 915 | { 916 | "cell_type": "code", 917 | "execution_count": null, 918 | "metadata": {}, 919 | "outputs": [], 920 | "source": [ 921 | "D=np.diag([1,2,4,3])\n", 922 | "print(D)\n", 923 | "print(np.diag(D))" 924 | ] 925 | }, 926 | { 927 | "cell_type": "code", 928 | "execution_count": null, 929 | "metadata": {}, 930 | "outputs": [], 931 | "source": [ 932 | "M=np.array([[10*n+m for n in range(3)] \n", 933 | "for m in range(2)]) \n", 934 | "print(M)" 935 | ] 936 | }, 937 | { 938 | "cell_type": "markdown", 939 | "metadata": {}, 940 | "source": [ 941 | "Le module `numpy.random` fournit toute une liste de fonctions pour la génération de matrices aléatoires." 942 | ] 943 | }, 944 | { 945 | "cell_type": "code", 946 | "execution_count": null, 947 | "metadata": {}, 948 | "outputs": [], 949 | "source": [ 950 | "from numpy import random\n", 951 | "random.rand(4,2) #tirage uniforme" 952 | ] 953 | }, 954 | { 955 | "cell_type": "code", 956 | "execution_count": null, 957 | "metadata": {}, 958 | "outputs": [], 959 | "source": [ 960 | "random.randn(4,2) #tirage selon la loi N(0,1)" 961 | ] 962 | }, 963 | { 964 | "cell_type": "code", 965 | "execution_count": null, 966 | "metadata": {}, 967 | "outputs": [], 968 | "source": [ 969 | "v=random.randn(1000)\n", 970 | "import matplotlib.pyplot as plt\n", 971 | "h=plt.hist(v,20) # histogramme à 20 pas\n", 972 | "show()" 973 | ] 974 | }, 975 | { 976 | "cell_type": "code", 977 | "execution_count": null, 978 | "metadata": {}, 979 | "outputs": [], 980 | "source": [ 981 | "A=random.randn(64,64)\n", 982 | "plt.imshow(A,interpolation=\"nearest\")\n", 983 | "plt.colorbar\n", 984 | "plt.show()" 985 | ] 986 | }, 987 | { 988 | "cell_type": "code", 989 | "execution_count": null, 990 | "metadata": { 991 | "collapsed": true 992 | }, 993 | "outputs": [], 994 | "source": [ 995 | "# Sauver, écrire dans un fichier, lire un fichier\n", 996 | "M=random.randn(10,10)\n", 997 | "np.savetxt('data.csv',M,fmt='%2.2f',delimiter=',')\n", 998 | "#au format propre à numpy : npy" 999 | ] 1000 | }, 1001 | { 1002 | "cell_type": "code", 1003 | "execution_count": null, 1004 | "metadata": {}, 1005 | "outputs": [], 1006 | "source": [ 1007 | "np.save('data.npy',M)\n", 1008 | "np.load('data.npy')" 1009 | ] 1010 | }, 1011 | { 1012 | "cell_type": "markdown", 1013 | "metadata": {}, 1014 | "source": [ 1015 | "#### *Slicing*" 1016 | ] 1017 | }, 1018 | { 1019 | "cell_type": "code", 1020 | "execution_count": null, 1021 | "metadata": {}, 1022 | "outputs": [], 1023 | "source": [ 1024 | "v=np.array([1,2,3,4,5])\n", 1025 | "print(v)" 1026 | ] 1027 | }, 1028 | { 1029 | "cell_type": "code", 1030 | "execution_count": null, 1031 | "metadata": {}, 1032 | "outputs": [], 1033 | "source": [ 1034 | "v[1:4]" 1035 | ] 1036 | }, 1037 | { 1038 | "cell_type": "code", 1039 | "execution_count": null, 1040 | "metadata": {}, 1041 | "outputs": [], 1042 | "source": [ 1043 | "v[1:4:2]" 1044 | ] 1045 | }, 1046 | { 1047 | "cell_type": "code", 1048 | "execution_count": null, 1049 | "metadata": {}, 1050 | "outputs": [], 1051 | "source": [ 1052 | "v[::]" 1053 | ] 1054 | }, 1055 | { 1056 | "cell_type": "code", 1057 | "execution_count": null, 1058 | "metadata": {}, 1059 | "outputs": [], 1060 | "source": [ 1061 | "v[ : :2] # par pas de 2" 1062 | ] 1063 | }, 1064 | { 1065 | "cell_type": "code", 1066 | "execution_count": null, 1067 | "metadata": {}, 1068 | "outputs": [], 1069 | "source": [ 1070 | "v[ : 3] " 1071 | ] 1072 | }, 1073 | { 1074 | "cell_type": "code", 1075 | "execution_count": null, 1076 | "metadata": {}, 1077 | "outputs": [], 1078 | "source": [ 1079 | "v[3 :] # à partir de l'indice 3" 1080 | ] 1081 | }, 1082 | { 1083 | "cell_type": "code", 1084 | "execution_count": null, 1085 | "metadata": {}, 1086 | "outputs": [], 1087 | "source": [ 1088 | "v[-1] # dernier élément" 1089 | ] 1090 | }, 1091 | { 1092 | "cell_type": "code", 1093 | "execution_count": null, 1094 | "metadata": {}, 1095 | "outputs": [], 1096 | "source": [ 1097 | "v[-2 :] # deux derniers éléments " 1098 | ] 1099 | }, 1100 | { 1101 | "cell_type": "code", 1102 | "execution_count": null, 1103 | "metadata": {}, 1104 | "outputs": [], 1105 | "source": [ 1106 | "M=random.rand(4,3) \n", 1107 | "print(M)" 1108 | ] 1109 | }, 1110 | { 1111 | "cell_type": "code", 1112 | "execution_count": null, 1113 | "metadata": {}, 1114 | "outputs": [], 1115 | "source": [ 1116 | "ind=[1,2]\n", 1117 | "M[ind] # lignes d'indices 1 et 2" 1118 | ] 1119 | }, 1120 | { 1121 | "cell_type": "code", 1122 | "execution_count": null, 1123 | "metadata": {}, 1124 | "outputs": [], 1125 | "source": [ 1126 | "M[:,ind] # colonnes d'indices 1 et 2" 1127 | ] 1128 | }, 1129 | { 1130 | "cell_type": "code", 1131 | "execution_count": null, 1132 | "metadata": {}, 1133 | "outputs": [], 1134 | "source": [ 1135 | "M[[0,2],[1,2]] # renvoie M[0,1] et M[2,2]" 1136 | ] 1137 | }, 1138 | { 1139 | "cell_type": "code", 1140 | "execution_count": null, 1141 | "metadata": {}, 1142 | "outputs": [], 1143 | "source": [ 1144 | "M[np.ix_([0,2],[1,2])]" 1145 | ] 1146 | }, 1147 | { 1148 | "cell_type": "code", 1149 | "execution_count": null, 1150 | "metadata": {}, 1151 | "outputs": [], 1152 | "source": [ 1153 | "(M>0.5)" 1154 | ] 1155 | }, 1156 | { 1157 | "cell_type": "code", 1158 | "execution_count": null, 1159 | "metadata": {}, 1160 | "outputs": [], 1161 | "source": [ 1162 | "M[M>0.5]" 1163 | ] 1164 | }, 1165 | { 1166 | "cell_type": "markdown", 1167 | "metadata": {}, 1168 | "source": [ 1169 | "#### Autres fonctions" 1170 | ] 1171 | }, 1172 | { 1173 | "cell_type": "code", 1174 | "execution_count": null, 1175 | "metadata": {}, 1176 | "outputs": [], 1177 | "source": [ 1178 | "a=np.array([[0,1],[2,3],[4,5]])\n", 1179 | "np.ndim(a) # Nombre de dimensions)" 1180 | ] 1181 | }, 1182 | { 1183 | "cell_type": "code", 1184 | "execution_count": null, 1185 | "metadata": {}, 1186 | "outputs": [], 1187 | "source": [ 1188 | "np.size(a) # Nombre d’éléments" 1189 | ] 1190 | }, 1191 | { 1192 | "cell_type": "code", 1193 | "execution_count": null, 1194 | "metadata": {}, 1195 | "outputs": [], 1196 | "source": [ 1197 | "np.shape(a) # Tuple contenant la dimension de a" 1198 | ] 1199 | }, 1200 | { 1201 | "cell_type": "code", 1202 | "execution_count": null, 1203 | "metadata": {}, 1204 | "outputs": [], 1205 | "source": [ 1206 | "np.transpose(a) # Transposée" 1207 | ] 1208 | }, 1209 | { 1210 | "cell_type": "code", 1211 | "execution_count": null, 1212 | "metadata": {}, 1213 | "outputs": [], 1214 | "source": [ 1215 | "a.T # autre façon de définir la transposée" 1216 | ] 1217 | }, 1218 | { 1219 | "cell_type": "code", 1220 | "execution_count": null, 1221 | "metadata": {}, 1222 | "outputs": [], 1223 | "source": [ 1224 | "a.min(), np.min(a) # Valeur min" 1225 | ] 1226 | }, 1227 | { 1228 | "cell_type": "code", 1229 | "execution_count": null, 1230 | "metadata": {}, 1231 | "outputs": [], 1232 | "source": [ 1233 | "a.sum(), np.sum(a) # Somme des valeurs" 1234 | ] 1235 | }, 1236 | { 1237 | "cell_type": "code", 1238 | "execution_count": null, 1239 | "metadata": {}, 1240 | "outputs": [], 1241 | "source": [ 1242 | "a.sum(axis=0) # Somme sur les colonnes" 1243 | ] 1244 | }, 1245 | { 1246 | "cell_type": "code", 1247 | "execution_count": null, 1248 | "metadata": {}, 1249 | "outputs": [], 1250 | "source": [ 1251 | "a.sum(axis=1) # sur les lignes" 1252 | ] 1253 | }, 1254 | { 1255 | "cell_type": "markdown", 1256 | "metadata": {}, 1257 | "source": [ 1258 | "Quelques manipulations:" 1259 | ] 1260 | }, 1261 | { 1262 | "cell_type": "code", 1263 | "execution_count": null, 1264 | "metadata": {}, 1265 | "outputs": [], 1266 | "source": [ 1267 | "np.r_[1:4,10,11] # Concaténation en ligne" 1268 | ] 1269 | }, 1270 | { 1271 | "cell_type": "code", 1272 | "execution_count": null, 1273 | "metadata": {}, 1274 | "outputs": [], 1275 | "source": [ 1276 | "np.c_[1:4,11:14] # Concaténation en colonne" 1277 | ] 1278 | }, 1279 | { 1280 | "cell_type": "code", 1281 | "execution_count": null, 1282 | "metadata": {}, 1283 | "outputs": [], 1284 | "source": [ 1285 | "np.c_[1:4,11:15] # erreur" 1286 | ] 1287 | }, 1288 | { 1289 | "cell_type": "code", 1290 | "execution_count": null, 1291 | "metadata": {}, 1292 | "outputs": [], 1293 | "source": [ 1294 | "np.arange(6).reshape(3,2)" 1295 | ] 1296 | }, 1297 | { 1298 | "cell_type": "code", 1299 | "execution_count": null, 1300 | "metadata": {}, 1301 | "outputs": [], 1302 | "source": [ 1303 | "A=np.array([[1,2],[3,4]])\n", 1304 | "np.tile(A,(3,2)) # Répétition de la matrice A" 1305 | ] 1306 | }, 1307 | { 1308 | "cell_type": "code", 1309 | "execution_count": null, 1310 | "metadata": {}, 1311 | "outputs": [], 1312 | "source": [ 1313 | "A=np.array([[1,2],[3,4]])\n", 1314 | "B=np.array([[11,12],[13,14]])\n", 1315 | "#Concaténation en ligne\n", 1316 | "np.concatenate((A,B),axis=0)" 1317 | ] 1318 | }, 1319 | { 1320 | "cell_type": "code", 1321 | "execution_count": null, 1322 | "metadata": {}, 1323 | "outputs": [], 1324 | "source": [ 1325 | "# Equivalent à\n", 1326 | "np.vstack((A,B))" 1327 | ] 1328 | }, 1329 | { 1330 | "cell_type": "code", 1331 | "execution_count": null, 1332 | "metadata": {}, 1333 | "outputs": [], 1334 | "source": [ 1335 | "# Concaténation en colonne\n", 1336 | "np.concatenate((A,B),axis=1)" 1337 | ] 1338 | }, 1339 | { 1340 | "cell_type": "code", 1341 | "execution_count": null, 1342 | "metadata": {}, 1343 | "outputs": [], 1344 | "source": [ 1345 | "# Equivalent à\n", 1346 | "np.hstack((A,B))" 1347 | ] 1348 | }, 1349 | { 1350 | "cell_type": "markdown", 1351 | "metadata": {}, 1352 | "source": [ 1353 | "#### Opérations sur les `array`s" 1354 | ] 1355 | }, 1356 | { 1357 | "cell_type": "code", 1358 | "execution_count": null, 1359 | "metadata": {}, 1360 | "outputs": [], 1361 | "source": [ 1362 | "# somme\n", 1363 | "a=np.arange(6).reshape(3,2)\n", 1364 | "b=np.arange(3,9).reshape(3,2)\n", 1365 | "c=np.transpose(b)\n", 1366 | "a+b" 1367 | ] 1368 | }, 1369 | { 1370 | "cell_type": "code", 1371 | "execution_count": null, 1372 | "metadata": {}, 1373 | "outputs": [], 1374 | "source": [ 1375 | "a*b # produit terme à terme" 1376 | ] 1377 | }, 1378 | { 1379 | "cell_type": "code", 1380 | "execution_count": null, 1381 | "metadata": {}, 1382 | "outputs": [], 1383 | "source": [ 1384 | "np.dot(a,c) # produit matriciel" 1385 | ] 1386 | }, 1387 | { 1388 | "cell_type": "code", 1389 | "execution_count": null, 1390 | "metadata": {}, 1391 | "outputs": [], 1392 | "source": [ 1393 | "np.power(a,2)" 1394 | ] 1395 | }, 1396 | { 1397 | "cell_type": "code", 1398 | "execution_count": null, 1399 | "metadata": {}, 1400 | "outputs": [], 1401 | "source": [ 1402 | "np.power(2,a)" 1403 | ] 1404 | }, 1405 | { 1406 | "cell_type": "code", 1407 | "execution_count": null, 1408 | "metadata": {}, 1409 | "outputs": [], 1410 | "source": [ 1411 | "a/3" 1412 | ] 1413 | }, 1414 | { 1415 | "cell_type": "markdown", 1416 | "metadata": {}, 1417 | "source": [ 1418 | "Les fonctions `genfromtxt`, `avetxt` permettent de lire, écrire des fichiers textes par exemple au format `.csv` mais ces fonctionnalités sont plus largement abordées avec la librairie `pandas`." 1419 | ] 1420 | }, 1421 | { 1422 | "cell_type": "markdown", 1423 | "metadata": {}, 1424 | "source": [ 1425 | "#### Fonctions d'algèbre linéaire" 1426 | ] 1427 | }, 1428 | { 1429 | "cell_type": "code", 1430 | "execution_count": null, 1431 | "metadata": {}, 1432 | "outputs": [], 1433 | "source": [ 1434 | "# Importation\n", 1435 | "import numpy as np\n", 1436 | "from scipy import linalg\n", 1437 | "A = np.array([[1,2],[3,4]])\n", 1438 | "linalg.inv(A)" 1439 | ] 1440 | }, 1441 | { 1442 | "cell_type": "code", 1443 | "execution_count": null, 1444 | "metadata": {}, 1445 | "outputs": [], 1446 | "source": [ 1447 | "linalg.det(A)" 1448 | ] 1449 | }, 1450 | { 1451 | "cell_type": "code", 1452 | "execution_count": null, 1453 | "metadata": {}, 1454 | "outputs": [], 1455 | "source": [ 1456 | "la,v = linalg.eig(A)\n", 1457 | "l1,l2 = la\n", 1458 | "# valeurs propres\n", 1459 | "print(l1, l2)" 1460 | ] 1461 | }, 1462 | { 1463 | "cell_type": "code", 1464 | "execution_count": null, 1465 | "metadata": {}, 1466 | "outputs": [], 1467 | "source": [ 1468 | "# 1er vecteur propre\n", 1469 | "print(v[:,0])" 1470 | ] 1471 | }, 1472 | { 1473 | "cell_type": "code", 1474 | "execution_count": null, 1475 | "metadata": {}, 1476 | "outputs": [], 1477 | "source": [ 1478 | "# SVD de A\n", 1479 | "U,s,V = linalg.svd(A) \n", 1480 | "print(s**2)" 1481 | ] 1482 | }, 1483 | { 1484 | "cell_type": "code", 1485 | "execution_count": null, 1486 | "metadata": {}, 1487 | "outputs": [], 1488 | "source": [ 1489 | "linalg.eig(np.dot(np.transpose(A),A))" 1490 | ] 1491 | }, 1492 | { 1493 | "cell_type": "markdown", 1494 | "metadata": {}, 1495 | "source": [ 1496 | "#### Tests élémentaires de Statistique" 1497 | ] 1498 | }, 1499 | { 1500 | "cell_type": "code", 1501 | "execution_count": null, 1502 | "metadata": {}, 1503 | "outputs": [], 1504 | "source": [ 1505 | "# Importation\n", 1506 | "import scipy.stats\n", 1507 | "rvs1 = scipy.stats.norm.rvs(loc=5, scale=10,size=500)\n", 1508 | "rvs2 = scipy.stats.norm.rvs(loc=5, scale=10,size=500)\n", 1509 | "rvs3 = scipy.stats.norm.rvs(loc=8, scale=10,size=500)\n", 1510 | "# t-test returns: t-statistic/two-tailed p-value\n", 1511 | "scipy.stats.ttest_ind(rvs1, rvs2)" 1512 | ] 1513 | }, 1514 | { 1515 | "cell_type": "code", 1516 | "execution_count": null, 1517 | "metadata": {}, 1518 | "outputs": [], 1519 | "source": [ 1520 | "scipy.stats.ttest_ind(rvs1, rvs3)" 1521 | ] 1522 | }, 1523 | { 1524 | "cell_type": "code", 1525 | "execution_count": null, 1526 | "metadata": {}, 1527 | "outputs": [], 1528 | "source": [ 1529 | "# Kolmogorov-Smirnov test\n", 1530 | "# returns: KS statistic / two-tailed p-value\n", 1531 | "scipy.stats.ks_2samp(rvs1, rvs2)" 1532 | ] 1533 | }, 1534 | { 1535 | "cell_type": "code", 1536 | "execution_count": null, 1537 | "metadata": {}, 1538 | "outputs": [], 1539 | "source": [ 1540 | "scipy.stats.ks_2samp(rvs1, rvs3)" 1541 | ] 1542 | }, 1543 | { 1544 | "cell_type": "markdown", 1545 | "metadata": {}, 1546 | "source": [ 1547 | "## Références\n", 1548 | "\n", 1549 | "**Mac Kinney W.** (2013). *Python for Data Analysis*, O’Reilly. [pdf](http://it-ebooks.info/book/104)\n", 1550 | "\n", 1551 | "**Sheppard K.** (2014). *Introduction to Python for Econometrics, Statistics and Data Analysis*, [pdf](https://www.kevinsheppard.com/images/0/09/Python_introduction.pdf)" 1552 | ] 1553 | } 1554 | ], 1555 | "metadata": { 1556 | "hide_input": false, 1557 | "kernelspec": { 1558 | "display_name": "Python 3", 1559 | "language": "python", 1560 | "name": "python3" 1561 | }, 1562 | "language_info": { 1563 | "codemirror_mode": { 1564 | "name": "ipython", 1565 | "version": 3 1566 | }, 1567 | "file_extension": ".py", 1568 | "mimetype": "text/x-python", 1569 | "name": "python", 1570 | "nbconvert_exporter": "python", 1571 | "pygments_lexer": "ipython3", 1572 | "version": "3.6.6" 1573 | }, 1574 | "toc": { 1575 | "nav_menu": { 1576 | "height": "512px", 1577 | "width": "252px" 1578 | }, 1579 | "number_sections": true, 1580 | "sideBar": true, 1581 | "skip_h1_title": false, 1582 | "toc_cell": false, 1583 | "toc_position": {}, 1584 | "toc_section_display": "block", 1585 | "toc_window_display": false 1586 | } 1587 | }, 1588 | "nbformat": 4, 1589 | "nbformat_minor": 1 1590 | } 1591 | -------------------------------------------------------------------------------- /Cal2-PythonPandas.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "
\n", 8 | "\"INSA\"/ \n", 9 | "\n", 10 | "\"Wikistat\"/\n", 11 | "
" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "# \"Python\"/ [pour Statistique et Science des Données](https://github.com/wikistat/Intro-Python)" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "# Trafic de données avec \"Python\"/ & \"Pandas\"/ \n", 26 | "\n", 27 | "**Résumé**: Utilisation de Python pour la préparation (*data munging* ou *wrangling* ou trafic) de données pas trop massives: qui tiennent en mémoire une fois réorganisées. Cette étape est abordée par l'initiation aux fonctionnalités de la librairie `pandas` et à la classe `DataFrame`; lire et écrire des fichiers, gérer une table de données et les types des variables, échantillonner, discrétiser, regrouper des modalités, description élémentaires uni et bi-variées; concaténation et jointure de tables." 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "## 1 Introduction\n", 35 | "### 1.1 Objectifs\n", 36 | "Le *data munging* ou *wrangling* (traduit ici par *trafic*) de données est l'ensemble des opérations permettant de passer de données brutes à une table (*data frame*) correcte et adaptée aux objectifs à atteindre par des méthodes statistiques d'analyse, exploration, modélisation ou apprentissage. \n", 37 | "\n", 38 | "En présence de données complexes, peu ou mal organisées, présentant des trous, trop massives pour tenir en mémoire... la qualité de cette étape est fondamentale (*garbage in garbage out*) pour la bonne réalisation d'une étude. Compte tenu de la diversité des situations envisageables, il serait vain de vouloir exposer tous les outils et techniques qui peuvent s'avérer nécessaires. Tâchons néanmoins de résumer les problèmes qui peuvent être rencontrés.\n", 39 | "### 1.2 Croissance du volume\n", 40 | "Le volume des données et sa croissance occasionnent schématiquement trois situations.\n", 41 | "1. Le fichier initial des données brutes peut être chargé intégralement en mémoire moyennant éventuellement de sauter quelques colonnes ou lignes du fichier (cf. section 3.1). C'est la situation courante, tout logiciel statistique comme R peut réaliser les traitements. *C'est l'objet des sections 2 à 6*.\n", 42 | "2. Le fichier initial est très volumineux mais la table (*DataFrame*), qui résulte de quelques trafics (*munging*) appropriés, tient en mémoire. Cette situations nécessite: lecture, analyse, transformation, ré-écriture, séquentielles du fichier ligne à ligne ou par bloc. Il existe des astuces avec R mais il est préférable d'utiliser des outils plus adaptés. Tout langage de programmation (java, c, perl, ruby...) peut être utilisé pour écrire le ou les programmes réalisant ce travail. Néanmoins Python, et plus précisément la librairie [`pandas`](http://pandas.pydata.org/), offrent un ensemble d'outils efficaces pour accomplir ces tâches sans avoir à ré-inventer la roue et ré-écrire tout un ensemble de fonctionnalités relativement basiques. Remarque : les procédures `univariate` et `freq` et l'étape `data` de SAS sont adaptées car elles ne chargent pas les données en mémoire pour réaliser des traitements rudimentaires. Néanmoins pour tout un tas de raisons trop longues à exposer, notamment de coût annuel de location, SAS perd régulièrement des parts de marché sur ce créneau. *Cette approche est introduite ci-dessous en section 7 et consiste à enchâsser dans une même structure itérative et séquentielle les étapes précédentes des sections 2 à 6*.\n", 43 | "3. Lorsque les données, très massives, sont archivées sur un système de données distribuées (*Hadoop Distributed File System* ou HDFS), trafic et prétraitement des données doivent tenir compte de cet environnement. L'environnement *Spark* et l'API `PySpark` permettant de gérer en python des données distribuées est à favoriser. *Cf. le [calepin](http://wikistat.fr/Notebooks/Cal6-PythonSpark.html)* concerné. \n", 44 | "\n", 45 | "### 1.3 Quelques problèmes\n", 46 | "Liste non exhaustive des problèmes pouvant être rencontrés et dont la résolution nécessite simultanément des compétences en Informatique, Statistique, Mathématiques et aussi \"métier\" du domaine de l'étude. \n", 47 | "- Identifier les \"individus\" $\\times$ \"variables\" (*instances*$\\times$*features* en langue informatique) de la table à mettre en forme à partir de bases de données variées; *i.e.* logs d'un site web, listes d'incidents, localisations...\n", 48 | "- Donnés atypiques (*outliers*): correction, suppression, transformation des variables ou méthode statistique robuste?\n", 49 | "- Variable qualitative avec beaucoup de modalités dont certaines très peu fréquentes: suppression, modalité `autres`, recodage aléatoire, regroupement \"métier\" ou méthode tolérante?\n", 50 | "- Distributions a-normales (log-normale, Poisson, multimodales...) et problèmes d'hétéroscédasticité: transformation, discrétisation ou méthodes tolérantes?\n", 51 | "- Données manquantes: suppressions (ligne ou colonne), imputation ou méthodes tolérantes ?\n", 52 | "- Représentations (splines, Fourier, ondelettes) et recalage (*time warping*) de données fonctionnelles.\n", 53 | "- Représentation de trajectoires, de chemins sur un graphe ? \n", 54 | "- Choix d'une distance (quadratique, absolue, géodésique...) entre les objets étudiés.\n", 55 | "- ...\n", 56 | "Bien entendu les \"bons\" choix dépendent directement de l'objectif poursuivi et des méthodes mises en oeuvre par la suite. D'où l'importance d'intégrer de façon précoce, dès la planification du recueil des données, les compétences statistiques nécessaires au sein d'une équipe. \n", 57 | "### 1.4 Fonctionnalités de `pandas`\n", 58 | "La richesse des fonctionnalités de la librairie `pandas` est une des raisons, si ce n'est la principale, d'utiliser Python pour extraire, préparer, éventuellement analyser, des données. En voici un bref aperçu. \n", 59 | "- *Objets*: les classes `Series` et `DataFrame` ou *table de données*.\n", 60 | "- *Lire, écrire* création et exportation de tables de données à partir de fichiers textes (séparateurs, `.csv`, format fixe, compressés), binaires (HDF5 avec `Pytable`), HTML, XML, JSON, MongoDB, SQL... \n", 61 | "- *Gestion* d'une table: sélection des lignes, colonnes, transformations, réorganisation par niveau d'un facteur, discrétisation de variables quantitatives, exclusion ou imputation élémentaire de données manquantes, permutation et échantillonnage aléatoire, variables indicatrices, chaînes de caractères...\n", 62 | "- *Statistiques* élémentaires uni et bivariées, tri à plat (nombre de modalités, de valeurs nulles, de valeurs manquantes...), graphiques associés, statistiques par groupe, détection élémentaire de valeurs atypiques...\n", 63 | "- *Manipulation* de tables: concaténations, fusions, jointures, tri, gestion des types et formats...\n", 64 | "### 1.5 Références\n", 65 | "Ce tutoriel élémentaire s'inspire largement du livre de référence (Mc Kinney, 2013) et de la [documentation en ligne](http://pandas.pydata.org/pandas-docs/stable/) à consulter sans modération. Cette documentation inclut également des [tutoriels](http://pandas.pydata.org/pandas-docs/stable/tutorials.html) à exécuter pour compléter et approfondir la première ébauche d'un sujet relativement technique et qui peut prendre des tournures très diverses en fonction de la qualité et des types de données traitées.\n", 66 | "### 1.6 Exemple\n", 67 | "Les données choisies pour illustrer ce tutoriel sont issues d'une compétition du site [Kaggle](https://www.kaggle.com/): [Titanic: Machine learnic from Disaster](https://www.kaggle.com/c/titanic-gettingStarted). Le concours est terminé mais les [données](https://www.kaggle.com/c/titanic-gettingStarted/data) sont toujours disponibles sur le site avec des tutoriels utilisant Excel, Python ou R. \n", 68 | "\n", 69 | "Une des raisons du drame, qui provoqua la mort de 1502 personnes sur les 2224 passagers et membres d'équipage, fut le manque de canots de sauvetage. Il apparaît que les chances de survie dépendaient de différents facteurs (sexe, âge, classe...). Le but du concours est de construire un modèle de prévision (classification supervisée) de survie en fonction de ces facteurs. Les données sont composées d'un échantillon d'apprentissage (891) et d'un échantillon test (418) chacun décrit par 11 variables dont la première indiquant la survie ou non lors du naufrage. " 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "Liste des variables\n", 77 | "\n", 78 | "Label | Intitulé\n", 79 | "----------|-------------\n", 80 | "survival | Survival (0 = No; 1 = Yes)\n", 81 | "pclass | Passenger Class (1 = 1st; 2 = 2nd; 3 = 3rd)\n", 82 | "name | Name\n", 83 | "sex | Sex\n", 84 | "age | Age\n", 85 | "sibsp | Number of Siblings/Spouses Aboard\n", 86 | "parch | Number of Parents/Children Aboard\n", 87 | "ticket | Ticket Number\n", 88 | "fare | Passenger Fare\n", 89 | "cabin | Cabin\n", 90 | "embarked | Port of Embarkation (C = Cherbourg; Q = Queenstown; S = Southampton)" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "## 2 Les classes `Series` et `DataFrame`\n", 98 | "De même que la librairie `Numpy` introduit le type `array` indispensable à la manipulation de matrices en calcul scientifique, celle `pandas` introduit les classes `Series` (séries chronologiques) et `DataFrame` ou table de données indispensables en statistique. \n", 99 | "\n", 100 | "### 2.1 *Series*\n", 101 | "La classe `Series` est l'association de deux `arrays` unidimensionnels. Le premier est un ensemble de valeurs indexées par le 2ème qui est souvent une série temporelle. Ce type est introduit principalement pour des applications en Econométrie et Finance où Python est largement utilisé." 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "### 2.2 *DataFrame*\n", 109 | "Cette classe est proche de celle du même nom dans le langage R, il s'agit d'associer avec le même index de lignes des colonnes ou variables de types différents (entier, réel, booléen, caractère). C'est un tableau bi-dimensionnel avec des index de lignes et de colonnes mais il peut également être vu comme une liste de `Series` partageant le même index. L'index de colonne (noms des variables) est un objet de type `dict` (dictionnaire). C'est la classe qui sera principalement utilisée dans ce tutoriel. " 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "# Exemple de data frame\n", 119 | "import pandas as pd\n", 120 | "data = {\"state\": [\"Ohio\", \"Ohio\", \"Ohio\", \n", 121 | " \"Nevada\", \"Nevada\"],\n", 122 | " \"year\": [2000, 2001, 2002, 2001, 2002],\n", 123 | " \"pop\": [1.5, 1.7, 3.6, 2.4, 2.9]}\n", 124 | "frame = pd.DataFrame(data)\n", 125 | "# ordre des colonnes\n", 126 | "pd.DataFrame(data, columns=[\"year\", \"state\", \"pop\"])" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "# index des lignes et valeurs manquantes (NaN)\n", 136 | "frame2=pd.DataFrame(data, columns=[\"year\", \"state\", \"pop\", \"debt\"],\n", 137 | " index=[\"one\", \"two\", \"three\", \"four\", \"five\"])\n", 138 | "# liste des colonnes\n", 139 | "frame.columns" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "# valeurs d'une colonnes\n", 149 | "frame[\"state\"]" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "frame.year" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "# \"imputation\"\n", 168 | "frame2[\"debt\"] = 16.5\n", 169 | "frame2" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": {}, 176 | "outputs": [], 177 | "source": [ 178 | "# créer une variable\n", 179 | "frame2[\"eastern\"] = frame2.state == \"Ohio\"\n", 180 | "frame2" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": {}, 187 | "outputs": [], 188 | "source": [ 189 | "frame2.columns" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [ 198 | "# supprimer une variable\n", 199 | "del frame2[u\"eastern\"]\n", 200 | "frame2.columns" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "### 2.3 Index\n", 208 | "Les index peuvent être définis par emboîtement et beaucoup d'autres fonctionnalités sur la gestion des index sont décrites par Mac Kinney (2013) (chapitre 5): \n", 209 | "- `append` nouvel index par concaténation,\n", 210 | "- `diff` différence ensembliste,\n", 211 | "- `intersection` intersection ensembliste,\n", 212 | "- `union` union ensembliste\n", 213 | "- `isin` vrai si la valeur est dans la liste,\n", 214 | "- `delete` suppression de l'index $i$,\n", 215 | "- `drop` suppression d'une valeur d'index, \n", 216 | "- `is_monotonic` vrai si les valeurs sont croissantes, \n", 217 | "- `is_unique` vrai si toutes les valeurs sont différentes, \n", 218 | "- `nique` tableau des valeurs uniques de l'index." 219 | ] 220 | }, 221 | { 222 | "cell_type": "markdown", 223 | "metadata": {}, 224 | "source": [ 225 | "## 3 Lire écrire des tables de données\n", 226 | "`Pandas` offre des outils efficaces pour lire écrire des fichiers selon différents formats (csv, texte, fixe, compressé, xml, html, hdf5) ou interagir avec des bases de données SQL, MongoDB, des APIs web. Ce document se contente de décrire les fonctions les plus utiles `read_csv` et `read_table` pour lire des fichiers textes et générer un objet de classe ` DataFrame`. \n", 227 | "\n", 228 | "En principe ces fonctions font appel à un code écrit en C dont très rapide à l'exécution sauf pour l'emploi de certaines options (`skip\\_footer, sep`} autre qu'un seul caractère), à éviter, qui provoquent une exécution en Python (`engine=Python`). \n", 229 | "\n", 230 | "La réciproque pour l'écriture est obtenue par les commandes `data.to_csv` ou `_table` avec des options similaires. \n", 231 | "\n", 232 | "### 3.1 Syntaxe\n", 233 | "L'exemple de base est donné pour lire un fichier au format `.csv` dont les valeurs sont séparées par des \",\" et dont la première ligne contient le nom des variables.\n", 234 | "``\n", 235 | "import pandas as pd\n", 236 | "data=pd.read_csv(\"fichier.csv\")\n", 237 | "data=pd.read_table(\"fichier.csv\", sep=\",\")\n", 238 | "``\n", 239 | "\n", 240 | "Il est important de connaître la liste des possibilités et options offertes par cette simple commande. Voici les principales ci-dessous et un lien à la [liste complète](http://pandas.pydata.org/pandas-docs/stable/io.html#io-read-csv-table).\n", 241 | "- `path` chemin ou non du fichier ou URL.\n", 242 | "- `sep` délimiteur comme \\verb+ , ; | \\t + ou \\verb# \\s+ # pour un nombre variable d'espaces. \n", 243 | "- `header` défaut 0, la première ligne contient le nom des variables; si `None` les noms sont générés ou définis par ailleurs.\n", 244 | "- `index_col` noms ou numéros de colonnes définissant les index de lignes, index pouvant être hiérarchisés comme les facteurs d'un plan d'expérience.\n", 245 | "- `names` si {\\tt header=None}, liste des noms des variables. \n", 246 | "- `nrows` utile pour tester et limiter le nombre de ligne à lire.\n", 247 | "- `skiprow` liste de lignes à sauter en lecture.\n", 248 | "- `skip_footer` nombre de lignes à sauter en fin de fichier.\n", 249 | "- `na_values` définition du ou des codes signalant des valeurs manquantes. Ils peuvent être définis dans un dictionnaire pour associer variables et codes de valeurs manquantes spécifiques.\n", 250 | "- `usecols` sélectionne une liste des variable à lire pour éviter de lire des champs ou variables volumineuses et inutiles.\n", 251 | "- `skip_blan_lines` à `True` pour sauter les lignes blanches.\n", 252 | "- `converters` appliquer une fonction à une colonne ou variable.\n", 253 | "- `day_first` par défaut `False`, pour des dates françaises au format `7/06/2013`.\n", 254 | "- `chunksize` taille des morceaux à lire itérativement.\n", 255 | "- `verbose` imprime des informations comme le nombre de valeurs manquantes des variables non numériques.\n", 256 | "- `encoding` type d'encodage comme \"utf-8\" ou \"latin-1\"\n", 257 | "- `thousand` séparateur des miliers: \".\" ou \",\".\n", 258 | "\n", 259 | "Remarques:\n", 260 | "- De nombreuses options de gestion des dates et séries ne sont pas citées.\n", 261 | "- `chunksize` provoque la lecture d'un gros fichiers par morceaux de même taille (nombre de lignes). Des fonctions (comptage, dénombrement...) peuvent ensuite s'appliquer itérativement sur les morceaux.\n", 262 | "\\end{itemize}\n" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": {}, 268 | "source": [ 269 | "### 3.2 Exemple\n", 270 | "Les données du naufrage du Titanic illustrent l'utilisation de `pandas`. Elles sont lues directement à partir de leur URL ou sinon les charger [ici](http://www.math.univ-toulouse.fr/~besse/Wikistat/data) vers le répertoire de travail de Python." 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": null, 276 | "metadata": {}, 277 | "outputs": [], 278 | "source": [ 279 | "# Importations\n", 280 | "import pandas as pd\n", 281 | "import numpy as np\n", 282 | "# tester la lecture\n", 283 | "# path=\"\"\n", 284 | "path='./'\n", 285 | "df = pd.read_csv(path+'titanic-train.csv',nrows=5)\n", 286 | "df" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": null, 292 | "metadata": {}, 293 | "outputs": [], 294 | "source": [ 295 | "df.tail()" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": null, 301 | "metadata": {}, 302 | "outputs": [], 303 | "source": [ 304 | "# tout lire\n", 305 | "df = pd.read_csv(path+\"titanic-train.csv\")\n", 306 | "df.head()" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": null, 312 | "metadata": {}, 313 | "outputs": [], 314 | "source": [ 315 | "# Des variables sont inexploitables\n", 316 | "# Choisir les colonnes utiles\n", 317 | "df=pd.read_csv(path+\"titanic-train.csv\",\n", 318 | " usecols=[1,2,4,5,6,7,9,11],nrows=5)\n", 319 | "df.head()" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": {}, 325 | "source": [ 326 | "À partir de la version 0.15, `pandas`, inclut un type `category` assez proche de celui ` factor` de R. Il devrait normalement être déclaré dans un dictionnaire au moment par exemple de la lecture (`dtype={\"Surv\":pd.Categorical...}`) mais ce n'est pas le cas, c'est donc le type objet qui est déclaré puis modifié. Il est vivement recommandé de bien affecter les bons types à chaque variable ne serait-ce que pour éviter de faire des opérations douteuses, par exemple arithmétiques sur des codes de modalités." 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": null, 332 | "metadata": {}, 333 | "outputs": [], 334 | "source": [ 335 | "df=pd.read_csv(path+\"titanic-train.csv\",skiprows=1,header=None,usecols=[1,2,4,5,9,11],\n", 336 | " names=[\"Surv\",\"Classe\",\"Genre\",\"Age\",\"Prix\",\"Port\"],dtype={\"Surv\":object,\n", 337 | " \"Classe\":object,\"Genre\":object,\"Port\":object})\n", 338 | "df.head()" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": null, 344 | "metadata": {}, 345 | "outputs": [], 346 | "source": [ 347 | "df.dtypes" 348 | ] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": {}, 353 | "source": [ 354 | "Redéfinition des bons types." 355 | ] 356 | }, 357 | { 358 | "cell_type": "code", 359 | "execution_count": null, 360 | "metadata": {}, 361 | "outputs": [], 362 | "source": [ 363 | "df[\"Surv\"]=pd.Categorical(df[\"Surv\"],ordered=False)\n", 364 | "df[\"Classe\"]=pd.Categorical(df[\"Classe\"],ordered=False)\n", 365 | "df[\"Genre\"]=pd.Categorical(df[\"Genre\"],ordered=False)\n", 366 | "df[\"Port\"]=pd.Categorical(df[\"Port\"],ordered=False)\n", 367 | "df.dtypes" 368 | ] 369 | }, 370 | { 371 | "cell_type": "markdown", 372 | "metadata": {}, 373 | "source": [ 374 | "Remarque: il est également possible de tout lire avant de laisser \"tomber\" les variable inexploitables. C'est le rôle de la commande: \n", 375 | "\n", 376 | "`df = df.drop([\"Name\", \"Ticket\", \"Cabin\"], axis=1)`" 377 | ] 378 | }, 379 | { 380 | "cell_type": "markdown", 381 | "metadata": {}, 382 | "source": [ 383 | "### 3.3 Echantillonnage simple\n", 384 | "Comme dans R, le type `DataFrame` de Python est chargé en mémoire. Si, malgré les options précédentes permettant de sélectionner, les colonnes, les types des variables... le fichier est encore trop gros, il reste possible, avant de chercher une configuration matérielle lourde et en première approximation, de tirer un échantillon aléatoire simple selon une distribution uniforme. Un tirage stratifié demanderait plus de travail. Cela suppose de connaître le nombre de ligne du fichier ou une valeur inférieure proche." 385 | ] 386 | }, 387 | { 388 | "cell_type": "code", 389 | "execution_count": null, 390 | "metadata": {}, 391 | "outputs": [], 392 | "source": [ 393 | "# pour les données titanic:\n", 394 | "N=891 # taille du fichier\n", 395 | "n=200 # taille de l'échantillon\n", 396 | "lin2skipe=[0] # ne pas lire la première ligne\n", 397 | "# ne pas lire N-n lignes tirées aléatoirement\n", 398 | "lin2skipe.extend(np.random.choice(np.arange(1,N+1),\n", 399 | " (N-n),replace=False))\n", 400 | "df_small=pd.read_csv(path+\"titanic-train.csv\",\n", 401 | " skiprows=lin2skipe,header=None, \n", 402 | " usecols=[1,2,4,5,9,11],\n", 403 | " names=[\"Surv\",\"Classe\",\"Genre\",\"Age\",\n", 404 | " \"Prix\",\"Port\"])\n", 405 | "df_small" 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": {}, 411 | "source": [ 412 | "## 3 Gérer une table de données\n", 413 | "### 3.1 Discrétisation d'une variable quantitative\n", 414 | "Pour la discrétisation d'une variable quantitative. Il est d'un bon usage de définir les bornes des classes à des quantiles, plutôt qu'également espacées, afin de construire des classes d'effectifs sensiblement égaux. Ceci est obtenu par la fonction `qcut`. La fonction `cut` propose par défaut des bornes équi-réparties à moins de fournir une liste de ces bornes." 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": null, 420 | "metadata": {}, 421 | "outputs": [], 422 | "source": [ 423 | "df[\"AgeQ\"]=pd.qcut(df.Age,3,labels=[\"Ag1\",\"Ag2\",\n", 424 | " \"Ag3\"])\n", 425 | "df[\"PrixQ\"]=pd.qcut(df.Prix,3,labels=[\"Pr1\",\"Pr2\",\n", 426 | " \"Pr3\"])\n", 427 | "df[\"PrixQ\"].describe()" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "metadata": {}, 433 | "source": [ 434 | "### 3.2 Modifier / regrouper des modalités\n", 435 | "Le recodage des variables qualitatives ou renommage en clair des modalités est obtenu simplement." 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": null, 441 | "metadata": {}, 442 | "outputs": [], 443 | "source": [ 444 | "df[\"Surv\"]=df[\"Surv\"].cat.rename_categories(\n", 445 | " [\"Vnon\",\"Voui\"])\n", 446 | "df[\"Classe\"]=df[\"Classe\"].cat.rename_categories(\n", 447 | " [\"Cl1\",\"Cl2\",\"Cl3\"])\n", 448 | "df[\"Genre\"]=df[\"Genre\"].cat.rename_categories(\n", 449 | " [\"Gfem\",\"Gmas\"])\n", 450 | "df[\"Port\"]=df[\"Port\"].cat.rename_categories(\n", 451 | " [\"Pc\",\"Pq\",\"Ps\"])" 452 | ] 453 | }, 454 | { 455 | "cell_type": "code", 456 | "execution_count": null, 457 | "metadata": {}, 458 | "outputs": [], 459 | "source": [ 460 | "df.head()" 461 | ] 462 | }, 463 | { 464 | "cell_type": "markdown", 465 | "metadata": {}, 466 | "source": [ 467 | "Il est possible d'associer recodage et regroupement des modalités en définissant un dictionnaire de transformation." 468 | ] 469 | }, 470 | { 471 | "cell_type": "code", 472 | "execution_count": null, 473 | "metadata": {}, 474 | "outputs": [], 475 | "source": [ 476 | "data = pd.DataFrame({\"food\":[\"bacon\",\"pulled pork\", \n", 477 | " \"bacon\", \"Pastrami\",\n", 478 | " \"corned beef\", \"Bacon\", \"pastrami\", \"honey ham\",\n", 479 | " \"nova lox\"],\n", 480 | " \"ounces\": [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})\n", 481 | "data" 482 | ] 483 | }, 484 | { 485 | "cell_type": "code", 486 | "execution_count": null, 487 | "metadata": {}, 488 | "outputs": [], 489 | "source": [ 490 | "meat_to_animal = {\n", 491 | " \"bacon\": \"pig\",\n", 492 | " \"pulled pork\": \"pig\",\n", 493 | " \"pastrami\": \"cow\",\n", 494 | " \"corned beef\": \"cow\",\n", 495 | " \"honey ham\": \"pig\",\n", 496 | " \"nova lox\": \"salmon\"\n", 497 | "}\n", 498 | "# Eviter les mélanges de majuscules minuscules \n", 499 | "# en mettant tout en minuscule\n", 500 | "data[\"animal\"] = data[\"food\"].map(\n", 501 | " str.lower).map(meat_to_animal)\n", 502 | "data" 503 | ] 504 | }, 505 | { 506 | "cell_type": "code", 507 | "execution_count": null, 508 | "metadata": {}, 509 | "outputs": [], 510 | "source": [ 511 | "data[\"food\"].map(lambda x: meat_to_animal[x.lower()])" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": null, 517 | "metadata": {}, 518 | "outputs": [], 519 | "source": [ 520 | "dfs = pd.DataFrame({\"key\": [\"b\", \"b\", \"a\", \"c\",\n", 521 | " \"a\", \"b\"],\"data1\": range(6)})\n", 522 | "pd.get_dummies(dfs[\"key\"])" 523 | ] 524 | }, 525 | { 526 | "cell_type": "markdown", 527 | "metadata": {}, 528 | "source": [ 529 | "### 3.3 Variables indicatrices\n", 530 | "Générer des indicatrices des modalités ou *dummy variables*." 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": null, 536 | "metadata": {}, 537 | "outputs": [], 538 | "source": [ 539 | "dummies = pd.get_dummies(dfs['key'], prefix='key')\n", 540 | "df_with_dummy = dfs[['data1']].join(dummies)\n", 541 | "df_with_dummy" 542 | ] 543 | }, 544 | { 545 | "cell_type": "markdown", 546 | "metadata": {}, 547 | "source": [ 548 | "### 3.4 Permutation et tirage aléatoires\n", 549 | "Permutation aléatoire:" 550 | ] 551 | }, 552 | { 553 | "cell_type": "code", 554 | "execution_count": null, 555 | "metadata": {}, 556 | "outputs": [], 557 | "source": [ 558 | "dfs = pd.DataFrame(np.arange(5 * 4).reshape(5, 4))\n", 559 | "sampler = np.random.permutation(5)\n", 560 | "sampler\n", 561 | "dfs\n", 562 | "dfs.take(sampler)" 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "metadata": {}, 568 | "source": [ 569 | "Tirage aléatoire avec remplacement ou *bootstrap* ; celui sans remplacement est traité section 3.3." 570 | ] 571 | }, 572 | { 573 | "cell_type": "code", 574 | "execution_count": null, 575 | "metadata": {}, 576 | "outputs": [], 577 | "source": [ 578 | "bag = np.array([5, 7, -1, 6, 4])\n", 579 | "sampler = np.random.randint(0, len(bag), size=10)\n", 580 | "draws = bag.take(sampler)\n", 581 | "draws" 582 | ] 583 | }, 584 | { 585 | "cell_type": "markdown", 586 | "metadata": {}, 587 | "source": [ 588 | "### 3.5 Transformations, opérations\n", 589 | "Les opérations arithmétiques entre `Series` et `DataFrame` sont possibles au même titre qu'entre `array`. Si les index ne correspondent pas, des valeurs manquantes (NAN) sont créées à moins d'utiliser des méthodes d'arithmétique `flexible` (`add, sub, div, mul`) autorisant la complétion par une valeur par défaut, généralement 0.\n", 590 | "\n", 591 | "Une fonction quelconque (`lambda`) peut être appliquée avec une même commande qu'`apply` de R. " 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": null, 597 | "metadata": {}, 598 | "outputs": [], 599 | "source": [ 600 | "# la table de données\n", 601 | "frame = pd.DataFrame(np.random.randn(4,3), \n", 602 | " columns=list(\"bde\"),\n", 603 | " index=[\"Utah\", \"Ohio\", \"Texas\", \"Oregon\"])\n", 604 | "# une fonction\n", 605 | "f = lambda x: x.max() - x.min()\n", 606 | "frame.apply(f, axis=1)" 607 | ] 608 | }, 609 | { 610 | "cell_type": "markdown", 611 | "metadata": {}, 612 | "source": [ 613 | "### 3.6 Tri et rangs\n", 614 | "Trier une table selon les valeurs d'une variable ou d'un index." 615 | ] 616 | }, 617 | { 618 | "cell_type": "code", 619 | "execution_count": null, 620 | "metadata": {}, 621 | "outputs": [], 622 | "source": [ 623 | "frame = pd.DataFrame(np.arange(8).reshape((2,4)), \n", 624 | " index=[\"three\", \"one\"],\n", 625 | " columns=[\"d\", \"a\", \"b\", \"c\"])\n", 626 | "frame.sort_index()" 627 | ] 628 | }, 629 | { 630 | "cell_type": "code", 631 | "execution_count": null, 632 | "metadata": {}, 633 | "outputs": [], 634 | "source": [ 635 | "frame.sort_index(axis=1)" 636 | ] 637 | }, 638 | { 639 | "cell_type": "code", 640 | "execution_count": null, 641 | "metadata": {}, 642 | "outputs": [], 643 | "source": [ 644 | "frame.sort_index(axis=1, ascending=False)" 645 | ] 646 | }, 647 | { 648 | "cell_type": "code", 649 | "execution_count": null, 650 | "metadata": {}, 651 | "outputs": [], 652 | "source": [ 653 | "frame.sort_values(by=\"b\")" 654 | ] 655 | }, 656 | { 657 | "cell_type": "markdown", 658 | "metadata": {}, 659 | "source": [ 660 | "La commande `rank` remplace les valeurs par leur rang dans l'ordre des lignes ou des colonnes." 661 | ] 662 | }, 663 | { 664 | "cell_type": "code", 665 | "execution_count": null, 666 | "metadata": {}, 667 | "outputs": [], 668 | "source": [ 669 | "frame = pd.DataFrame({\"b\": [4.3, 7, -3, 2], \n", 670 | " \"a\": [0, 1, 0, 1],\"c\": [-2, 5, 8, -2.5]})\n", 671 | "frame.rank(axis=1)" 672 | ] 673 | }, 674 | { 675 | "cell_type": "code", 676 | "execution_count": null, 677 | "metadata": {}, 678 | "outputs": [], 679 | "source": [ 680 | "frame.rank(axis=0)" 681 | ] 682 | }, 683 | { 684 | "cell_type": "markdown", 685 | "metadata": {}, 686 | "source": [ 687 | "## 4 Statistiques descriptives élémentaires\n", 688 | "Continuer l'étude des données sur le naufrage du Titanic. Les commandes ci-dessous permettent des premiers diagnostics sur la qualité des données.\n", 689 | "### 4.1 Description univariée" 690 | ] 691 | }, 692 | { 693 | "cell_type": "code", 694 | "execution_count": null, 695 | "metadata": {}, 696 | "outputs": [], 697 | "source": [ 698 | "df.dtypes" 699 | ] 700 | }, 701 | { 702 | "cell_type": "code", 703 | "execution_count": null, 704 | "metadata": {}, 705 | "outputs": [], 706 | "source": [ 707 | "df.describe()" 708 | ] 709 | }, 710 | { 711 | "cell_type": "code", 712 | "execution_count": null, 713 | "metadata": {}, 714 | "outputs": [], 715 | "source": [ 716 | "df.head()" 717 | ] 718 | }, 719 | { 720 | "cell_type": "code", 721 | "execution_count": null, 722 | "metadata": {}, 723 | "outputs": [], 724 | "source": [ 725 | "import matplotlib.pyplot as plt\n", 726 | "%matplotlib inline" 727 | ] 728 | }, 729 | { 730 | "cell_type": "code", 731 | "execution_count": null, 732 | "metadata": {}, 733 | "outputs": [], 734 | "source": [ 735 | "df[\"Age\"].hist()\n", 736 | "plt.show()" 737 | ] 738 | }, 739 | { 740 | "cell_type": "code", 741 | "execution_count": null, 742 | "metadata": {}, 743 | "outputs": [], 744 | "source": [ 745 | "df[\"Age\"].plot(kind=\"box\")\n", 746 | "plt.show()" 747 | ] 748 | }, 749 | { 750 | "cell_type": "code", 751 | "execution_count": null, 752 | "metadata": {}, 753 | "outputs": [], 754 | "source": [ 755 | "df[\"Prix\"].plot(kind=\"hist\")\n", 756 | "plt.show()" 757 | ] 758 | }, 759 | { 760 | "cell_type": "code", 761 | "execution_count": null, 762 | "metadata": {}, 763 | "outputs": [], 764 | "source": [ 765 | "# qualitatif\n", 766 | "df[\"Surv\"].value_counts()" 767 | ] 768 | }, 769 | { 770 | "cell_type": "code", 771 | "execution_count": null, 772 | "metadata": {}, 773 | "outputs": [], 774 | "source": [ 775 | "df[\"Classe\"].value_counts()" 776 | ] 777 | }, 778 | { 779 | "cell_type": "code", 780 | "execution_count": null, 781 | "metadata": {}, 782 | "outputs": [], 783 | "source": [ 784 | "df[\"Genre\"].value_counts()" 785 | ] 786 | }, 787 | { 788 | "cell_type": "code", 789 | "execution_count": null, 790 | "metadata": {}, 791 | "outputs": [], 792 | "source": [ 793 | "df[\"Port\"].value_counts()" 794 | ] 795 | }, 796 | { 797 | "cell_type": "markdown", 798 | "metadata": {}, 799 | "source": [ 800 | "### 4.2 Description bivariée" 801 | ] 802 | }, 803 | { 804 | "cell_type": "code", 805 | "execution_count": null, 806 | "metadata": {}, 807 | "outputs": [], 808 | "source": [ 809 | "df.plot(kind=\"scatter\",x=\"Age\",y=\"Prix\")\n", 810 | "plt.show()" 811 | ] 812 | }, 813 | { 814 | "cell_type": "code", 815 | "execution_count": null, 816 | "metadata": {}, 817 | "outputs": [], 818 | "source": [ 819 | "# afficher une sélection\n", 820 | "df[df[\"Age\"]>60][[\"Genre\",\"Classe\",\"Age\",\"Surv\"]]" 821 | ] 822 | }, 823 | { 824 | "cell_type": "code", 825 | "execution_count": null, 826 | "metadata": {}, 827 | "outputs": [], 828 | "source": [ 829 | "df.boxplot(column=\"Age\",by=\"Classe\")\n", 830 | "plt.show()" 831 | ] 832 | }, 833 | { 834 | "cell_type": "code", 835 | "execution_count": null, 836 | "metadata": {}, 837 | "outputs": [], 838 | "source": [ 839 | "df.boxplot(column=\"Prix\",by=\"Surv\")\n", 840 | "plt.show()" 841 | ] 842 | }, 843 | { 844 | "cell_type": "code", 845 | "execution_count": null, 846 | "metadata": {}, 847 | "outputs": [], 848 | "source": [ 849 | "# table de contingence\n", 850 | "table=pd.crosstab(df[\"Surv\"],df[\"Classe\"])\n", 851 | "print(table)" 852 | ] 853 | }, 854 | { 855 | "cell_type": "code", 856 | "execution_count": null, 857 | "metadata": {}, 858 | "outputs": [], 859 | "source": [ 860 | "# Mosaic plot\n", 861 | "from statsmodels.graphics.mosaicplot import mosaic\n", 862 | "mosaic(df,[\"Classe\",\"Genre\"])\n", 863 | "plt.show()" 864 | ] 865 | }, 866 | { 867 | "cell_type": "code", 868 | "execution_count": null, 869 | "metadata": {}, 870 | "outputs": [], 871 | "source": [ 872 | "mosaic(df,[\"Surv\",\"Classe\"])\n", 873 | "plt.show()" 874 | ] 875 | }, 876 | { 877 | "cell_type": "markdown", 878 | "metadata": {}, 879 | "source": [ 880 | "### 4.3 Imputation de données manquantes\n", 881 | "La gestion des données manquantes est souvent un point délicat. De nombreuses stratégies ont été élaborées, les principales sont décrites dans une [vignette](http://wikistat.fr/pdf/st-m-app-idm.pdf). Nous ne décrivons ici que les plus élémentaires à [mettre en oeuvre](http://pandas.pydata.org/pandas-docs/version/0.15.2/missing_data.html) avec `pandas`.\n", 882 | "\n", 883 | "Il est ainsi facile de supprimer toutes les observations présentant des données manquantes lorsque celles-ci sont peu nombreuses et majoritairement regroupées sur certaines lignes ou colonnes.\n", 884 | "\n", 885 | "``\n", 886 | "df = df.dropna(axis=0)\n", 887 | "df = df.dropna(axis=1)\n", 888 | "``\n", 889 | "\n", 890 | "`Pandas` permet également de faire le choix pour une variable qualitative de considérer ` `np.nan` comme une modalité spécifique ou d'ignorer l'observation correspondante.\n", 891 | "\n", 892 | "Autres stratégies:\n", 893 | "* Cas quantitatif: une valeur manquante est imputée par la moyenne ou la médiane.\n", 894 | "* Cas d'une série chronologique: imputation par la valeur précédente ou suivante ou par interpolation linéaire, polynomiale ou encore lissage spline.\n", 895 | "* Cas qualitatif: modalité la plus fréquente ou répartition aléatoire selon les fréquences observées des modalités.\n", 896 | "\n", 897 | "La variable âge contient de nombreuses données manquantes. La fonction `fillna` présente plusieurs options d'imputation." 898 | ] 899 | }, 900 | { 901 | "cell_type": "code", 902 | "execution_count": null, 903 | "metadata": {}, 904 | "outputs": [], 905 | "source": [ 906 | "# Remplacement par la médiane d'une variable quantitative\n", 907 | "df=df.fillna(df.median())\n", 908 | "df.describe()" 909 | ] 910 | }, 911 | { 912 | "cell_type": "code", 913 | "execution_count": null, 914 | "metadata": {}, 915 | "outputs": [], 916 | "source": [ 917 | "# par la modalité \"médiane\" de AgeQ\n", 918 | "df.info()\n", 919 | "df.AgeQ=df[\"AgeQ\"].fillna(\"Ag2\")\n", 920 | "# par le port le plus fréquent\n", 921 | "df[\"Port\"].value_counts()\n", 922 | "df.Port=df[\"Port\"].fillna(\"Ps\")\n", 923 | "df.info()" 924 | ] 925 | }, 926 | { 927 | "cell_type": "markdown", 928 | "metadata": {}, 929 | "source": [ 930 | "Ces imputations sont pour le moins très rudimentaires et d'autres sont à privilégier pour des modélisations plus soignées mais ces méthodes font généralement appel à R.\n", 931 | "\n", 932 | "D'autres fonctions (Mac Kinney, 2013) sont proposées pour supprimer les duplicatas (` drop\\_duplicates`), modifier les dimensions, traquer des atypiques unidimensionnels selon un modèle gaussien ou par rapport à des quantiles." 933 | ] 934 | }, 935 | { 936 | "cell_type": "markdown", 937 | "metadata": {}, 938 | "source": [ 939 | "## 5 Manipuler des tables de données\n", 940 | "### 5.1 Jointure\n", 941 | "Il s'agit de \"jointer\" deux tables partageant la même clef ou encore de concaténer horizontalement les lignes en faisant correspondre les valeurs d'une variable clef qui peuvent ne pas être uniques." 942 | ] 943 | }, 944 | { 945 | "cell_type": "code", 946 | "execution_count": null, 947 | "metadata": {}, 948 | "outputs": [], 949 | "source": [ 950 | "# tables\n", 951 | "df1 = pd.DataFrame({\"key\": [\"b\", \"b\", \"a\", \"c\", \n", 952 | " \"a\",\"a\", \"b\"],\"data1\": range(7)})\n", 953 | "df2 = pd.DataFrame({\"key\": [\"a\", \"b\", \"d\"],\n", 954 | " \"data2\": range(3)})\n", 955 | "pd.merge(df1,df2,on=\"key\")" 956 | ] 957 | }, 958 | { 959 | "cell_type": "markdown", 960 | "metadata": {}, 961 | "source": [ 962 | "La gestion des clefs manquantes est en option: entre autres, ne pas introduire de ligne (ci-dessus), insérer des valeurs manquantes ci-dessous. " 963 | ] 964 | }, 965 | { 966 | "cell_type": "code", 967 | "execution_count": null, 968 | "metadata": {}, 969 | "outputs": [], 970 | "source": [ 971 | "# valeurs manquantes\n", 972 | "pd.merge(df1,df2,on=\"key\", how=\"outer\")" 973 | ] 974 | }, 975 | { 976 | "cell_type": "markdown", 977 | "metadata": {}, 978 | "source": [ 979 | "### 5.2 Concaténation selon un axe\n", 980 | "Concaténation verticale (axis=0) ou horizontales (axis=1) de tables. La concaténation horizontale est similaire à la jointure (option `outer`)." 981 | ] 982 | }, 983 | { 984 | "cell_type": "code", 985 | "execution_count": null, 986 | "metadata": {}, 987 | "outputs": [], 988 | "source": [ 989 | "# tables\n", 990 | "df1 = pd.DataFrame({\"key\": [\"b\", \"b\", \"a\", \"c\", \n", 991 | " \"a\", \"a\", \"b\"],\"var\": range(7)})\n", 992 | "df2 = pd.DataFrame({\"key\": [\"a\", \"b\", \"d\"],\n", 993 | " \"var\": range(3)})\n", 994 | "# concaténation verticales\n", 995 | "pd.concat([df1,df2],axis=0)" 996 | ] 997 | }, 998 | { 999 | "cell_type": "code", 1000 | "execution_count": null, 1001 | "metadata": {}, 1002 | "outputs": [], 1003 | "source": [ 1004 | "# concaténation horizontale\n", 1005 | "pd.concat([df1,df2],axis=1)" 1006 | ] 1007 | }, 1008 | { 1009 | "cell_type": "markdown", 1010 | "metadata": {}, 1011 | "source": [ 1012 | "## 6 Trafic séquentiel de gros fichiers\n", 1013 | "Étape suivante associée de la croissance du volume: les fichiers des données brutes ne tiennent pas en mémoire. Il \"suffit\" d'intégrer ou enchâsser les étapes des sections précédentes dans la lecture séquentielle d'un gros fichier. En apparence, simple d'un point de vue méthodologique, cette étape peut consommer beaucoup de temps par tests et remises en cause incessantes des choix de sélection, transformation, recodage... des variables. Il est crucial de se doter d'outils efficaces. \n", 1014 | "\n", 1015 | "Il s'agit donc de lire les données par morceau (nombre fixé de lignes) ou ligne à ligne, traiter chaque morceau, le ré-écrire dans un fichier de format binaire plutôt que texte; le choix du format HDF5 semble le plus efficace du point de vue technique et pour servir d'interface à d'autres environnements: C, java, Matlab... et R car une librairie ({\\tt rhdf5} de Bioconductor) gère ce format.\n", 1016 | "\n", 1017 | "La procédure est comparable à une étape `Data` de SAS, qui lit/écrit les tables ligne à ligne.\n", 1018 | "\n", 1019 | "Deux librairies: `h5py` et `PyTables` gèrent le format HDF5 en Python. Pour simplifier la tâche, `pandas` intègre une classe `HDFStore` utilisant `PyTables` qui doit donc être installée. \n", 1020 | "\n", 1021 | "**Attention**: ce format n'est pas adapté à une gestion *parallélisée*, notamment en écriture. \n", 1022 | "\n", 1023 | "\n", 1024 | "### 6.1 Lecture séquentielle\n", 1025 | "L'exemple est ici donné pour lire un fichier texte mais beaucoup d'autres formats (excel, hdf, sql, json, msgpack, html, gbq, stata, clipboard, pickle) sont connus de `pandas`." 1026 | ] 1027 | }, 1028 | { 1029 | "cell_type": "code", 1030 | "execution_count": null, 1031 | "metadata": {}, 1032 | "outputs": [], 1033 | "source": [ 1034 | "# importations\n", 1035 | "import pandas as pd\n", 1036 | "import numpy as np\n", 1037 | "# lire tout le fichier par morceaux\n", 1038 | "# avec l'option chunksize\n", 1039 | "Partition=pd.read_csv(path+\"titanic-train.csv\",skiprows=1,\n", 1040 | " header=None,usecols=[1,2,4,5,9,11],\n", 1041 | " names=[\"Surv\",\"Classe\",\"Genre\",\"Age\",\n", 1042 | " \"Prix\",\"Port\"],dtype={\"Surv\":object,\n", 1043 | " \"Classe\":object,\"Genre\":object,\"Port\":object},\n", 1044 | " chunksize=100)\n", 1045 | "# ouverture du fichier HDF5\n", 1046 | "stock=pd.HDFStore(\"titan.h5\")\n", 1047 | "# boucle de lecture\n", 1048 | "for Part in Partition:\n", 1049 | " # \"nettoyage\" préliminaire des données\n", 1050 | " #Part=Part.drop([\"Name\",\"Ticket\",\"Cabin\"],axis=1)\n", 1051 | " # ... autres opérations\n", 1052 | " # création de la table \"df\" dans \"stock\" puis\n", 1053 | " # extension de celle-ci par chaque \"Part\"\n", 1054 | " stock.append(\"df\",Part)\n", 1055 | "# dernier morceau lu et ajouté\n", 1056 | "Part.head()" 1057 | ] 1058 | }, 1059 | { 1060 | "cell_type": "code", 1061 | "execution_count": null, 1062 | "metadata": {}, 1063 | "outputs": [], 1064 | "source": [ 1065 | "# Il est généralement utile de fermer le fichier\n", 1066 | "stock.close()" 1067 | ] 1068 | }, 1069 | { 1070 | "cell_type": "markdown", 1071 | "metadata": {}, 1072 | "source": [ 1073 | "**Attention** aux types implicites des variables. Si, par exemple, une donnée manquante n'apparaît pas dans une colonne du 1er morceau mais dans le 2ème, cela peut engendrer un conflit de type. Expliciter systématiquement les types et noms des variables dans un dictionnaire en paramètre.\n", 1074 | "\n", 1075 | "### 6.2 Utilisation d'une table HDF5" 1076 | ] 1077 | }, 1078 | { 1079 | "cell_type": "code", 1080 | "execution_count": null, 1081 | "metadata": {}, 1082 | "outputs": [], 1083 | "source": [ 1084 | "# Ouverture du fichier\n", 1085 | "Archiv=pd.HDFStore(\"titan.h5\")\n", 1086 | "# sélection de la table et affichage de l'entête\n", 1087 | "Archiv.select(\"df\").head()" 1088 | ] 1089 | }, 1090 | { 1091 | "cell_type": "markdown", 1092 | "metadata": {}, 1093 | "source": [ 1094 | "Cette partie est à développer pour illustrer les fonctionnalités de `pandas` permettant d'interroger / requêter (*querying* notamment SQL) une table archivée dans un fichier HDF5. Consulter la [documentation en ligne](http://pandas.pydata.org/pandas-docs/dev/io.html#hdf5-pytables) à ce sujet. \n", 1095 | "\n", 1096 | "### 6.3 Echantillon aléatoire simple\n", 1097 | "Le fichier créé au format HDF5 peut être encore très volumineux. Par souci d'efficacité, son raffinement, son exploitation, voire même son analyse pour modélisation, peuvent ou même doivent être opérés sur un simple échantillon aléatoire." 1098 | ] 1099 | }, 1100 | { 1101 | "cell_type": "code", 1102 | "execution_count": null, 1103 | "metadata": {}, 1104 | "outputs": [], 1105 | "source": [ 1106 | "# extraction du nombre de lignes / individus\n", 1107 | "nrows = Archiv.get_storer(\"df\").nrows\n", 1108 | "# génération des index aléatoires\n", 1109 | "r = np.random.randint(0,nrows,size=10)\n", 1110 | "print(r)" 1111 | ] 1112 | }, 1113 | { 1114 | "cell_type": "code", 1115 | "execution_count": null, 1116 | "metadata": {}, 1117 | "outputs": [], 1118 | "source": [ 1119 | "# extraction des lignes d'index fixés\n", 1120 | "df_ech=Archiv.select(\"df\",where=pd.Index(r))\n", 1121 | "df_ech" 1122 | ] 1123 | }, 1124 | { 1125 | "cell_type": "markdown", 1126 | "metadata": {}, 1127 | "source": [ 1128 | "Il \"suffit\" alors d'appliquer les outils des sections 4 à 6 précédentes." 1129 | ] 1130 | }, 1131 | { 1132 | "cell_type": "markdown", 1133 | "metadata": {}, 1134 | "source": [ 1135 | "### 6.4 Echanges entre R et Pyhton\n", 1136 | "Les données ayant été préparées, nettoyées, le transfert de la table dans R permet de déployer toute la richesse des librairies développées dans cet environnement plus familier au statisticien. Il est possible d'appeler des commandes R à partir de Python avec la librairie `rpy2` et réciproquement d'appeler des commandes Python de R avec la librairie `rpython`. \n", 1137 | "\n", 1138 | "La librairie `rpy2` définit la commande `load_data` qui charge un `data.frame` de R dans un `DataFrame` tandis que celle `convert_to_r_dataframe` génère un objet R.\n", 1139 | "\n", 1140 | "Le plus efficace mais sans doute pas le plus simple, consisterait à lire directement, à partir de R, le fichier intermédiaire précédent au format binaire HDF5 en utilisant la librairie ` rhdf5` de Bioconductor. Cette démarche pose des problèmes pour la gestions des variables qualitatives et plus généralement celle de la classe `DataFrame`. Une alternative simple consiste à construire un fichier intermédiaire au format classique `.csv`. \n", 1141 | "\n", 1142 | "\n", 1143 | "## À suivre...\n", 1144 | "Ces traitements font appel à de très nombreuses opérations de lectures / écritures sur un seul ordinateur, un seul disque au regard du volume des calculs; ils ne sont pas adaptés à une parallélisation sur un ordinateur multiprocesseur. La gestion et l'analyse de plus gros volumes de données nécessite une distribution de celles-ci sur plusieurs serveurs / disques. D'autres technologies doivent être utilisées; c'est actuellement le couple *Spark/Hadoop* le plus en vogue.\n", 1145 | "\n", 1146 | "**Intérêt**: *Spark* est utilisable avec java, Scala et aussi Python. L'investissement dans ce langage est donc rentable. " 1147 | ] 1148 | }, 1149 | { 1150 | "cell_type": "markdown", 1151 | "metadata": {}, 1152 | "source": [ 1153 | "## Références\n", 1154 | "\n", 1155 | "**Mac Kinney W.** (2013). *Python for Data Analysis*, O’Reilly. [pdf](http://it-ebooks.info/book/104)" 1156 | ] 1157 | } 1158 | ], 1159 | "metadata": { 1160 | "hide_input": false, 1161 | "kernelspec": { 1162 | "display_name": "Python 3", 1163 | "language": "python", 1164 | "name": "python3" 1165 | }, 1166 | "language_info": { 1167 | "codemirror_mode": { 1168 | "name": "ipython", 1169 | "version": 3 1170 | }, 1171 | "file_extension": ".py", 1172 | "mimetype": "text/x-python", 1173 | "name": "python", 1174 | "nbconvert_exporter": "python", 1175 | "pygments_lexer": "ipython3", 1176 | "version": "3.6.6" 1177 | }, 1178 | "toc": { 1179 | "nav_menu": { 1180 | "height": "512px", 1181 | "width": "252px" 1182 | }, 1183 | "number_sections": true, 1184 | "sideBar": true, 1185 | "skip_h1_title": false, 1186 | "toc_cell": false, 1187 | "toc_position": {}, 1188 | "toc_section_display": "block", 1189 | "toc_window_display": false 1190 | } 1191 | }, 1192 | "nbformat": 4, 1193 | "nbformat_minor": 1 1194 | } 1195 | -------------------------------------------------------------------------------- /Cal4-PythonProg.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "
\n", 8 | "\"INSA\"/ \n", 9 | "\n", 10 | "\"Wikistat\"/\n", 11 | "\n", 12 | "
" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "# \"Python\"/ [pour Statistique et Science des Données](https://github.com/wikistat/Intro-Python)" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "# Eléments de programmation en \"Python\"/ pour Calcul Scientifique - Statistique" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "**Résumé**: Compléments de programmation en python: structures de contrôle, programmation fonctionnelle (map, reduce, lambda), introduction aux classes et objets. \n", 34 | "\n", 35 | "## Introduction\n", 36 | "\n", 37 | "L'objectif de ce tutoriel est d'introduire quelques outils et concepts plus avancés de la programmation en Python pour dans le but d'améliorer la performance et la lisibilité des codes. Les notions de classe, de programmation objet et celle de *programmation fonctionnelle* qui en découle sont fondamentales. Elles sont fondamentales pour le bon usage de certaines librairie dont *Scikit-learn* (section 3.3). C'est aussi une introduction à l'utilisation des fonctionnalités *MapReduce* parallélisables et donc à la base de l'algorithmique pour données distribuées (*Hadoop*) avec [*PySpark*](https://github.com/wikistat/Ateliers-Big-Data/tree/master/1-Intro-PySpark)." 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "## 1 Structures de contrôle\n", 45 | "\n", 46 | "### 1.1 Structure itérative `for`\n", 47 | "\n", 48 | "Une boucle `for` permet, comme dans la pluspart des langages de programmation, de parcourir les éléments d'un objet *itérable*. En python cela peut-être une liste, un tuple, une chaîne de caractères (*string*), mais également des objets spécialement conçus pour cela, appelés *iterator*.\n", 49 | "\n", 50 | "Syntaxe de la structure `for`:\n", 51 | "\n", 52 | "`for` *variable* `in range` *iterator*: \n", 53 | "
     instruction\n", 54 | "\n", 55 | "La ligne `for` se termine par deux points ':'. Le bloc de codes à l'intérieur de la boucle est *indenté*. \n", 56 | "\n", 57 | "Voici comment parcourir différentes structures itératives en se souvenant que: **de façon générale, il faut éviter les boucles `for` dans un langage interprété** en utilisant les autres fonctionnalités (section 2) prévues dans les librairies pour parcourir des tableaux ou matrices.\n", 58 | "\n", 59 | "\n", 60 | "\n", 61 | "##### `range`\n", 62 | "La fonction `range` produit une liste d'entiers mais aussi un objet itérable d'entiers utilisés au fur et à mesure des itérations de la boucle.\n", 63 | "\n", 64 | "**Attention** `xrange` de python2 est remplacé par `range` en python3." 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": { 71 | "code_folding": [] 72 | }, 73 | "outputs": [], 74 | "source": [ 75 | "for i in range(5):\n", 76 | " print (i)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "L'appel à la fonction range(n) permet d'itérer sur les entiers de 0 à n-1 mais il est possible de spécifier des intervalles des valeurs de chaque pas d'itération: `range(2,11,2) range(10,0,-1)`" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "##### Strings\n", 91 | "Les chaînes de caractères sont également des objets parcourables avec la boucle `for`: caractère après caractère." 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "for character in \"Hi There!\":\n", 101 | " print (character)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "##### Dictionnaires\n", 109 | "Un dictionnaire peut être parcouru terme à terme, cependant, comme la pluspart des objets python, les dictionnaires possèdent des fonctions permettant de les transformer en une liste:\n", 110 | "\n", 111 | " .items()\n", 112 | " .keys()\n", 113 | " .values()\n", 114 | "\n", 115 | "ou un *iterator*\n", 116 | "\n", 117 | " .iteritems()\n", 118 | " .iterkeys()\n", 119 | " .itervalues()\n", 120 | "\n", 121 | "\n", 122 | "Les dictionnaires n'ont pas de structure ordonnée, les valeurs ne s'affichent pas forcément dans l'ordre dans lequel on les a entrées." 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "dico={\"a\":1,\"b\":2,\"c\":3}\n", 132 | "for k in dico.keys():\n", 133 | " print (k)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "Les fonctions `.items()` et `.iteritems()` permettent de parcourir les couples (clés, objets) des dictionnaires." 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": null, 146 | "metadata": {}, 147 | "outputs": [], 148 | "source": [ 149 | "# Si une seule variable itérative est spécifiée, celle-ci est un-tuple.\n", 150 | "for kv in dico.items():\n", 151 | " print (kv)" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [ 160 | "# Si deux variables itératives sont spécifiées, la première est la clé, la seconde la valeur correspondante\n", 161 | "for k,v in dico.items():\n", 162 | " print (k)\n", 163 | " print (v)" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "# Si la clé ou la valeur du dictionnaire n'est pas nécessaire, elle n'est pas stockée dans une variable en utilisant \"_\"\n", 173 | "for k,_ in dico.items():\n", 174 | " print (k)" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "##### Numpy Array\n", 182 | "La fonction `nditer` permet de parcourir tous les éléments d'un tableau (*array*) *Numpy*." 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "metadata": {}, 189 | "outputs": [], 190 | "source": [ 191 | "import numpy as np\n", 192 | "a = np.arange(6).reshape(2,3)\n", 193 | "for x in np.nditer(a):\n", 194 | " print (x)" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": {}, 200 | "source": [ 201 | "Appliquer directement la boucle `for` sur le tableau *Numpy* permet de parcourir les différentes lignes de ce tableau." 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": null, 207 | "metadata": {}, 208 | "outputs": [], 209 | "source": [ 210 | "for x in a:\n", 211 | " print (x)" 212 | ] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "metadata": {}, 217 | "source": [ 218 | "Pour parcourir les colonnes, la métode la plus simple consiste à parcourir les lignes de la matrice transposée." 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": {}, 225 | "outputs": [], 226 | "source": [ 227 | "for x in a.T:\n", 228 | " print (x)" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "Il est cependant très important de comprendre que les *arrays numpy* ont été conçus pour appliquer des fonctions directement sur l'ensemble du tableau en évitant des boucles. Ainsi nombre de fonctions natives permettent de résoudre de nombreux problèmes sans avoir à parcourir l'ensemble de la matrice: `sum`, `max`, etc... permettent d'obtenir les résultats escomptés de manière bien plus rapide qu'en utilisant les boucles `for`." 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": {}, 242 | "outputs": [], 243 | "source": [ 244 | "mina = a.max() # Retourne la valeur maximum de l'array a\n", 245 | "minxa = a.max(axis=1) # Retourne la valeur maximum de chaque ligne de l'array a\n", 246 | "minya = a.max(axis=0) # Retourne la valeur maximum de chaque colonne de l'array a\n", 247 | "print(mina, minxa, minya)" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": {}, 253 | "source": [ 254 | "Pour appliquer des fonctions plus complexes sur chaque ligne et/ou colonne de l'array, il existe également des fonctions natives de la librairie *numpy* permettant d'appliquer ces fonctions efficacement comme par exemple `apply_along_axis`, similaire à `apply` de R. " 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": null, 260 | "metadata": {}, 261 | "outputs": [], 262 | "source": [ 263 | "def scale(x):\n", 264 | " xcr = (x-np.mean(x))/np.std(x)\n", 265 | " return xcr\n", 266 | "X = np.random.randint(5, size=(3, 3))\n", 267 | "Xcr = np.apply_along_axis(scale,1,X)\n", 268 | "print(X, Xcr)" 269 | ] 270 | }, 271 | { 272 | "cell_type": "markdown", 273 | "metadata": {}, 274 | "source": [ 275 | "##### Pandas DataFrame\n", 276 | "De la même manière que pour les arrays *numpy*, les DataFrame *Pandas* ne sont pas conçus pour être parcourus facilement. Il est, là encore, préférable d'utiliser au maximum les méthodes natives de pandas ou de numpy, ou de convertir au préalable les colonnes souhaitées en listes ou encore même de convertir un *DataFrame* en *array* avant un traitemnte itératif.\n", 277 | "\n", 278 | "Il éxiste deux fonctions permettant de parcourir les différentes colonnes d'une DataFrame *Pandas*:\n", 279 | "\n", 280 | "* `iterrows()`: retourne le couple (index, ligne). Chaque ligne est parcourue sous la forme d'un objet *Series* de *Pandas*. Se construction est très coûteuse et rend la méthode très lente .\n", 281 | "* `itertuples()`: retoure un object specifique Pandas, où l'index, et les valeurs des colonnes de chaque ligne est accesible via des méthodes spécifiques. Plus rapide que `iterrows`." 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": null, 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "import pandas as pd\n", 291 | "df = pd.DataFrame([[\"A\",4],[\"B\",5],[\"C\",6]],index=[1,2,3],columns=[\"Letter\",\"Number\"])\n", 292 | "\n", 293 | "for i,r in df.iterrows():\n", 294 | " print(i,r)\n", 295 | "\n", 296 | "for ir in df.itertuples():\n", 297 | " print(ir)" 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "metadata": {}, 303 | "source": [ 304 | "#### Librarie *Itertools* \n", 305 | "La librairie native de python *itertools* possède de nombreuses fonctions générant des *iterators*. \n", 306 | "Plus d'exemples dans la [documentation](https://docs.python.org/3/library/itertools.html)." 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": null, 312 | "metadata": {}, 313 | "outputs": [], 314 | "source": [ 315 | "import itertools\n", 316 | "#zip : Concatenne les éléments de plusieurs objets itérables\n", 317 | "zip_list = []\n", 318 | "for k in zip('ABCD',[1,2,3,4],\"wxyz\"):\n", 319 | " zip_list.append(k)\n", 320 | "print(\"zip\")\n", 321 | "print(zip_list)" 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": null, 327 | "metadata": {}, 328 | "outputs": [], 329 | "source": [ 330 | "#permutation : retourne tous les arrangements possibles de liste de longueur n.\n", 331 | "permutation_list = []\n", 332 | "for k in itertools.permutations(\"ABCD\",2):\n", 333 | " permutation_list.append(k)\n", 334 | "print(\"permutations\")\n", 335 | "print(permutation_list)" 336 | ] 337 | }, 338 | { 339 | "cell_type": "markdown", 340 | "metadata": {}, 341 | "source": [ 342 | "#### One-Line Statement\n", 343 | "Après avoir parouru différents éléments d'un objet itérable et pour enregistrer un résultat pour chaque étape dans une liste, il est possible d'écrire la boucle `for` sur une seule ligne de la manière suivante:\n", 344 | "\n", 345 | "[result *for* variable in *iterator*]" 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": null, 351 | "metadata": {}, 352 | "outputs": [], 353 | "source": [ 354 | "# Version1\n", 355 | "A1=[]\n", 356 | "for k in range(10):\n", 357 | " A1.append(k*k)\n", 358 | " \n", 359 | "# Version 2\n", 360 | "A2 = [k*k for k in range(10)]\n", 361 | "print(A1,A2)" 362 | ] 363 | }, 364 | { 365 | "cell_type": "markdown", 366 | "metadata": {}, 367 | "source": [ 368 | "#### Affectation des indices\n", 369 | "Des expressions telles que x = x + 1 ou x = x - 2 apparaissent très souvent dans le corps des boucles. Le language Python permet de simplifier ces notations." 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": null, 375 | "metadata": {}, 376 | "outputs": [], 377 | "source": [ 378 | "a = 17\n", 379 | "s = \"hi\"\n", 380 | "a += 3 # Equivalent to a = a + 3\n", 381 | "a -= 3 # Equivalent to a = a - 3\n", 382 | "a *= 3 # Equivalent to a = a * 3\n", 383 | "a /= 3 # Equivalent to a = a / 3\n", 384 | "a %= 3 # Equivalent to a = a % 3\n", 385 | "s += \" there\" # Equivalent to s = s + “ there\"" 386 | ] 387 | }, 388 | { 389 | "cell_type": "markdown", 390 | "metadata": {}, 391 | "source": [ 392 | "### 1.2 Structure itérative `while`\n", 393 | "La boucle *for* permet de parcourir l'ensemble des éléments d'un objet itérable, ou un nombre déterminé d'éléments de ce dernier. Cependant, ce nombre n'est pas toujours prévisible et il est possible de parcourir ces éléments et arrêter le parcours lorsqu'une condition est respéctée ou non. C'est l'objet de l'instruction `while` avec la syntaxe:\n", 394 | "\n", 395 | "`while` *condition*:\n", 396 | "
     instructions\n", 397 | "\n", 398 | "Cette instruction permet de répéter en boucle les instructions jusqu'à ce que la *condition* soit vérifiée." 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": null, 404 | "metadata": {}, 405 | "outputs": [], 406 | "source": [ 407 | "# Incrémentation de `count` jusqu'à ce qu'elle dépasse la valeur 100.000\n", 408 | "count = 1 \n", 409 | "while count <= 100000: \n", 410 | " count += 1 \n", 411 | "print (count)" 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": {}, 417 | "source": [ 418 | "L'instruction `break` permet de sortir de la boucle `while` même si sa condition est respectée." 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": null, 424 | "metadata": {}, 425 | "outputs": [], 426 | "source": [ 427 | "while True: \n", 428 | " number = int(input(\"Enter the numeric grade: \")) \n", 429 | " if number >= 0 and number <= 100:\n", 430 | " break\n", 431 | " else:\n", 432 | " print (\"Error: grade must be between 100 and 0\" )\n", 433 | " print (number)" 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": {}, 439 | "source": [ 440 | "### 1.3 Structures conditionnelles `if - else`\n", 441 | "L'instruction conditionelle `if-else` est une des plus communes en programmation informatique. En python, elle se présente sous la forme suivante:\n", 442 | "\n", 443 | "`if` *condition*:\n", 444 | "
     instructions 1\n", 445 | "
`else`:\n", 446 | "
     Instructions 2\n" 447 | ] 448 | }, 449 | { 450 | "cell_type": "code", 451 | "execution_count": null, 452 | "metadata": {}, 453 | "outputs": [], 454 | "source": [ 455 | "number=1\n", 456 | "if number==1:\n", 457 | " print (True)\n", 458 | "else:\n", 459 | " print (False)" 460 | ] 461 | }, 462 | { 463 | "cell_type": "markdown", 464 | "metadata": {}, 465 | "source": [ 466 | "Lorsque plus de deux alternative sont possibles, utiliser l'instruction `elif` pour énumérer les différentes possibilités." 467 | ] 468 | }, 469 | { 470 | "cell_type": "code", 471 | "execution_count": null, 472 | "metadata": {}, 473 | "outputs": [], 474 | "source": [ 475 | "number=13\n", 476 | "if number<5:\n", 477 | " print(\"A\")\n", 478 | "elif number <10:\n", 479 | " print(\"B\")\n", 480 | "elif number <20:\n", 481 | " print(\"C\")\n", 482 | "else:\n", 483 | " print(\"D\")" 484 | ] 485 | }, 486 | { 487 | "cell_type": "markdown", 488 | "metadata": {}, 489 | "source": [ 490 | "#### One-Line Statement\n", 491 | "Comme pour la boucle for, il est possible d'écrire l'instruction `if_else` en une seule ligne lorsque le code est simple." 492 | ] 493 | }, 494 | { 495 | "cell_type": "code", 496 | "execution_count": null, 497 | "metadata": {}, 498 | "outputs": [], 499 | "source": [ 500 | "number=10\n", 501 | "\"A\" if number >10 else \"B\"" 502 | ] 503 | }, 504 | { 505 | "cell_type": "markdown", 506 | "metadata": {}, 507 | "source": [ 508 | "#### One line with for Loop\n", 509 | "Différentes combinaisons sont possibles pour associer à la fois l'instruction `if_else` avec une boucle `for` en une ligne." 510 | ] 511 | }, 512 | { 513 | "cell_type": "code", 514 | "execution_count": null, 515 | "metadata": {}, 516 | "outputs": [], 517 | "source": [ 518 | "#Sélectionne uniquement les valeur paire\n", 519 | "l1 = [k for k in range(10) if k%2==0]\n", 520 | "l1" 521 | ] 522 | }, 523 | { 524 | "cell_type": "code", 525 | "execution_count": null, 526 | "metadata": {}, 527 | "outputs": [], 528 | "source": [ 529 | "#Retourne \"even\" si l'élement k est pair, \"odd\" sinon.\n", 530 | "l2 = [\"even\" if k%2==0 else \"odd\" for k in range(10)]\n", 531 | "l2" 532 | ] 533 | }, 534 | { 535 | "cell_type": "markdown", 536 | "metadata": {}, 537 | "source": [ 538 | "## 2 Programmation fonctionnelle \n", 539 | "L'utilisation de *higher-order functions* permet d'éxecuter rapidement des schémas classiques:\n", 540 | "\n", 541 | "* appliquer la même fonction aux éléments d'une liste,\n", 542 | "* séléctionner, ou non, les différents éléments d'une liste selon une certaine condition,\n", 543 | "* ...\n", 544 | "\n", 545 | "**Important**: il s'agit ici d'introduitre les éléments de *programmation fonctionnelle*, présents dans Python, et utilisés systématiquement, car *parallélisable*, dans des architectures distribuées (*e. g. Hadoop, Spark*).\n", 546 | "\n", 547 | "### 2.1 `map`\n", 548 | "\n", 549 | "La première de cette fonction est la fonction `map`. Elle permet d'appliquer une fonction sur toutes les valeurs d'une liste et retourne une nouvelle liste avec les résultats correspondants.\n" 550 | ] 551 | }, 552 | { 553 | "cell_type": "code", 554 | "execution_count": null, 555 | "metadata": {}, 556 | "outputs": [], 557 | "source": [ 558 | "import random\n", 559 | "numbers = [random.randrange(-10,10) for k in range(10)]\n", 560 | "abs_numbers = map(abs,numbers) # Applique la fonction \"valeur absolue\" à tout les élements de la liste\n", 561 | "print(numbers,list(abs_numbers))" 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "execution_count": null, 567 | "metadata": {}, 568 | "outputs": [], 569 | "source": [ 570 | "def first_capital_letters(txt):\n", 571 | " if txt[0].islower():\n", 572 | " txt = txt[0].upper()+txt[1:]\n", 573 | " return txt\n", 574 | "\n", 575 | "name=[\"Jason\",\"bryan\",\"hercule\",\"Karim\"]\n", 576 | "list(map(first_capital_letters,name))" 577 | ] 578 | }, 579 | { 580 | "cell_type": "markdown", 581 | "metadata": {}, 582 | "source": [ 583 | "### 2.1 `filter`\n", 584 | "\n", 585 | "La fonction `filter` permet d'appliquer une fonction test à chaque valeur d'une liste. Si la fonction test est vérifiée, la valeur est ajoutée dans une nouvelle liste. Sinon, la valeur n'est pas prise en compte. La nouvelle liste, constitué de toutes les valeures \"positive\" selon la fonction test, est retournée." 586 | ] 587 | }, 588 | { 589 | "cell_type": "code", 590 | "execution_count": null, 591 | "metadata": {}, 592 | "outputs": [], 593 | "source": [ 594 | "def is_odd(n):\n", 595 | " return n % 2 == 1\n", 596 | "list(filter(is_odd,range(20)))" 597 | ] 598 | }, 599 | { 600 | "cell_type": "markdown", 601 | "metadata": {}, 602 | "source": [ 603 | "### 2.2 `reduce` \n", 604 | "\n", 605 | "La dernière fonction est la fonction `reduce`. Cette approche est loin d'être intuitive. Le meilleur moyen de comprendre le mode d'emploi de cette fonction est d'utiliser un exemple.\n", 606 | "\n", 607 | "L'objectif est de calculer la somme de tous les entier de 0 à 9.\n", 608 | "\n", 609 | "- Générer dans un premier temps la liste contenant tout ces éléments `r10 = [0,1,2,3,4,5,6,7,8,9]`\n", 610 | "- La fonction `reduce`, applique une première fois la fonction `sum_and_print` sur les deux premiers éléments de la liste. \n", 611 | "- Exécution récursive: la fonction `sum_and_print` est appliquée sur le résultat de la première opération et sur le 3ème éléments de la liste\n", 612 | "- Itération récursive jusqu'à ce que tous les éléments de la liste soient parcourus\n", 613 | "\n", 614 | "La fonction `reduce` a donc deux arguments: une fonction et une liste.\n" 615 | ] 616 | }, 617 | { 618 | "cell_type": "code", 619 | "execution_count": null, 620 | "metadata": { 621 | "scrolled": true 622 | }, 623 | "outputs": [], 624 | "source": [ 625 | "import functools\n", 626 | "def sum_and_print(x,y):\n", 627 | " print(\"Input: \", x,y)\n", 628 | " print(\"Output: \", y)\n", 629 | " return x+y\n", 630 | "\n", 631 | "r10 = range(10)\n", 632 | "res =functools.reduce(sum_and_print, r10)\n", 633 | "print(res)" 634 | ] 635 | }, 636 | { 637 | "cell_type": "markdown", 638 | "metadata": {}, 639 | "source": [ 640 | "Par défaut, la fonction passée en paramètre de la fonction `reduce` effectue sa première opérations sur les deux premiers éléments de la listes passés en paramètre. Mais il est possible de spécifier une valeur initiale en troisième paramètre. La première opération sera alors effectuée sur cette valeur initiale et le premier élément de la liste." 641 | ] 642 | }, 643 | { 644 | "cell_type": "code", 645 | "execution_count": null, 646 | "metadata": {}, 647 | "outputs": [], 648 | "source": [ 649 | "def somme(x,y):\n", 650 | " return x+y\n", 651 | "\n", 652 | "r10 = range(10)\n", 653 | "res =functools.reduce(somme, r10,1000)\n", 654 | "print(res)" 655 | ] 656 | }, 657 | { 658 | "cell_type": "markdown", 659 | "metadata": {}, 660 | "source": [ 661 | "### 2.4 `lambda`" 662 | ] 663 | }, 664 | { 665 | "cell_type": "markdown", 666 | "metadata": {}, 667 | "source": [ 668 | "L'utilisation de fonctions génériques permet de simplifier le code mais il est coûteux de définir une nouvelle fonction qui peut ne pas être réutilisée, comme par exemple celle de l'exemple précédent.\n", 669 | "L'appel `lambda` permet de créer une fonction de façon temporaire. La définition de ces fonctions est assez restrictive, puisqu'elle implique une définition sur *une seule ligne*, et ne permet pas d'assignation. \n", 670 | "\n", 671 | "Autre point important, l'exécution de cette fonction sur des données distribuées est implicitement parallélisée.\n", 672 | "\n", 673 | "Ainsi les précédents exemples peuvent-être ré-écrits de la manière suivante:" 674 | ] 675 | }, 676 | { 677 | "cell_type": "code", 678 | "execution_count": null, 679 | "metadata": {}, 680 | "outputs": [], 681 | "source": [ 682 | "name=[\"Jason\",\"bryan\",\"hercule\",\"Karim\"]\n", 683 | "list(map(lambda x : x[0].upper()+x[1:] if x[0].islower() else x,name))" 684 | ] 685 | }, 686 | { 687 | "cell_type": "code", 688 | "execution_count": null, 689 | "metadata": {}, 690 | "outputs": [], 691 | "source": [ 692 | "list(filter(lambda x : x % 2 == 1 ,range(10)))" 693 | ] 694 | }, 695 | { 696 | "cell_type": "code", 697 | "execution_count": null, 698 | "metadata": {}, 699 | "outputs": [], 700 | "source": [ 701 | "r10 = range(10)\n", 702 | "res =functools.reduce(lambda x,y:x+y, r10,1000)\n", 703 | "res" 704 | ] 705 | }, 706 | { 707 | "cell_type": "markdown", 708 | "metadata": {}, 709 | "source": [ 710 | "## 3 Classes et objets\n", 711 | "### 3.1 Définitions et exemples\n", 712 | "Les classes sont des objets communs à tous les langages orientés objets. Ce sont des objets constitués de\n", 713 | "\n", 714 | "- *attributs*: des paramètres fixes, de différentes natures, attribués à l'objet, \n", 715 | "- *méthodes*: des fonctions qui permettent d'appliquer des transformations sur ces attributs. \n", 716 | "\n", 717 | "Ci dessous on définit une classe \"Elève\" dans laquelle un élève est décrit par son nom, son prénom et ses notes. On notera la *convention de nommage* des méthodes qui commence par une minuscule, mais qui possède une majuscule à chaque début de nouveau mot. " 718 | ] 719 | }, 720 | { 721 | "cell_type": "code", 722 | "execution_count": null, 723 | "metadata": {}, 724 | "outputs": [], 725 | "source": [ 726 | "class Eleve:\n", 727 | " \"\"\"Classe définissant un élève caractérisé par:\n", 728 | " - son nom\n", 729 | " - son prénom\n", 730 | " - ses notes\n", 731 | " \"\"\" \n", 732 | " \n", 733 | " def __init__(self, nom, prenom): #constructeur de la classe\n", 734 | " \"\"\" Construit un élève avec les nom et prenom passé en paramètre et une liste de notes vide.\"\"\"\n", 735 | " self._nom = nom\n", 736 | " self._prenom=prenom\n", 737 | " self._notes = []\n", 738 | " \n", 739 | " def getNom(self):\n", 740 | " \"\"\" retourne le nom de l'élève \"\"\"\n", 741 | " return self._nom\n", 742 | " \n", 743 | " def getNotes(self):\n", 744 | " \"\"\" retourne les notes de l'élève\"\"\"\n", 745 | " return self._notes\n", 746 | " \n", 747 | " def getNoteMax(self):\n", 748 | " \"\"\" retourne la note max de l'élève\"\"\"\n", 749 | " return max(self._notes)\n", 750 | " \n", 751 | " def getMean(self):\n", 752 | " \"\"\" retourne la moyenne de l'élève\"\"\"\n", 753 | " return np.mean(self._notes)\n", 754 | " \n", 755 | " def getNbNote(self):\n", 756 | " \"\"\" retourne le nombre de note de l'élève\"\"\"\n", 757 | " return len(self._notes)\n", 758 | " \n", 759 | " def addNote(self, note):\n", 760 | " \"\"\" ajoute la note 'note' à la liste de note de l'élève\"\"\"\n", 761 | " self._notes.append(note)" 762 | ] 763 | }, 764 | { 765 | "cell_type": "markdown", 766 | "metadata": {}, 767 | "source": [ 768 | "Toutes les classes sont composées d'un *constructeur* qui a pour nom \\_\\_init\\_\\_. Il s'agit d'une méthode spéciale d'instance que Python reconnaît et sait utiliser dans certains contextes. La fonction \\_\\_init\\_\\_ est automatiquement appelée à la création d'une nouvelle classe et prend en paramètre `self`, qui représente l'objet instantié, et les différents attributs nécessaires à sa création." 769 | ] 770 | }, 771 | { 772 | "cell_type": "code", 773 | "execution_count": null, 774 | "metadata": {}, 775 | "outputs": [], 776 | "source": [ 777 | "eleve1 = Eleve(\"Jean\",\"Bon\")\n", 778 | "eleve1._nom" 779 | ] 780 | }, 781 | { 782 | "cell_type": "markdown", 783 | "metadata": {}, 784 | "source": [ 785 | "Les attributs de la classe sont directement accessibles de la manière suivante:\n", 786 | "\n", 787 | "`objet.nomDeLAtribbut`\n", 788 | "\n", 789 | "Cependant, par convention, il est conseillé de définir une méthode pour avoir accès à cet objet.\n", 790 | "Les méthodes qui permettent d'accéder à des attributs de l'objets sont appelés des *accessors*. Dans la classe élève, les méthodes commençant par `get` sont des *accessors*." 791 | ] 792 | }, 793 | { 794 | "cell_type": "code", 795 | "execution_count": null, 796 | "metadata": {}, 797 | "outputs": [], 798 | "source": [ 799 | "eleve1.getNom()" 800 | ] 801 | }, 802 | { 803 | "cell_type": "markdown", 804 | "metadata": {}, 805 | "source": [ 806 | "Les méthodes permettant de modifier les attributs d'un objet sont appelées des *mutators*. La fonction `addNote`, qui permet d'ajouter une note à la liste de notes de l'élève est un *mutator*." 807 | ] 808 | }, 809 | { 810 | "cell_type": "code", 811 | "execution_count": null, 812 | "metadata": {}, 813 | "outputs": [], 814 | "source": [ 815 | "print(eleve1.getNotes())\n", 816 | "eleve1.addNote(15)\n", 817 | "print(eleve1.getNotes())" 818 | ] 819 | }, 820 | { 821 | "cell_type": "code", 822 | "execution_count": null, 823 | "metadata": {}, 824 | "outputs": [], 825 | "source": [ 826 | "for k in range(10):\n", 827 | " eleve1.addNote(np.random.randint(20))" 828 | ] 829 | }, 830 | { 831 | "cell_type": "code", 832 | "execution_count": null, 833 | "metadata": {}, 834 | "outputs": [], 835 | "source": [ 836 | "print (eleve1.getNbNote())\n", 837 | "print (eleve1.getNoteMax())\n", 838 | "print (eleve1.getMean())" 839 | ] 840 | }, 841 | { 842 | "cell_type": "markdown", 843 | "metadata": {}, 844 | "source": [ 845 | "### 3.2 Héritage\n", 846 | "\n", 847 | "L'*héritage* est une fonctionnalité qui permet de définir une classe \"fille\" à partir d'une autre classe \"mère\". La classe fille hérite alors automatiquement de tous les attributs et de toutes les méthodes de la classe mère. " 848 | ] 849 | }, 850 | { 851 | "cell_type": "code", 852 | "execution_count": null, 853 | "metadata": {}, 854 | "outputs": [], 855 | "source": [ 856 | "class EleveSpecial(Eleve):\n", 857 | " \n", 858 | " def __init__(self, nom, prenom, optionName):\n", 859 | " Eleve.__init__(self, nom, prenom)\n", 860 | " self._optionName = optionName\n", 861 | " self._optionNotes = []\n", 862 | "\n", 863 | " def getNotesOption(self):\n", 864 | " \"\"\" retourne les notes de l'élève\"\"\"\n", 865 | " return self._optionNotes\n", 866 | " \n", 867 | " def addNoteOption(self, note):\n", 868 | " \"\"\" ajoute la note 'note' à la liste de note de l'élève\"\"\"\n", 869 | " self._optionNotes.append(note)" 870 | ] 871 | }, 872 | { 873 | "cell_type": "code", 874 | "execution_count": null, 875 | "metadata": {}, 876 | "outputs": [], 877 | "source": [ 878 | "eleve2 = EleveSpecial(\"Sam\",\"Stress\",\"latin\")" 879 | ] 880 | }, 881 | { 882 | "cell_type": "code", 883 | "execution_count": null, 884 | "metadata": {}, 885 | "outputs": [], 886 | "source": [ 887 | "eleve2.addNote(14)\n", 888 | "print (eleve2.getNotes())\n", 889 | "eleve2.addNoteOption(12)\n", 890 | "print (eleve2.getNotesOption())" 891 | ] 892 | }, 893 | { 894 | "cell_type": "markdown", 895 | "metadata": {}, 896 | "source": [ 897 | "### 3.3 Classes de *Scikit-learn*\n", 898 | "\n", 899 | "Les méthodes d'apprentissage statistique de la librairie *Scikit-learn* sont un parfait exemple d'utilisation de classes. Sans entrer en détail dans l'implémentation de cette méthode, voici l'exemple de la Régression linéaire par moindres carrés. \n", 900 | "\n", 901 | "**Toutes** les fonctions de *Scikit-learn* sont définies sur de modèle. Voir dans le [tutoriel d'apprentissage statistique](https://github.com/wikistat/Intro-Python) avec *Scikit-learn* comment ces propriétés sont utilisées pour enchaîner (*pipeline*) des exécutions.\n", 902 | "\n", 903 | "L'objet [`LinearRegression`](http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html) est une classe qui est initée avec des attributs suivants:\n", 904 | "\n", 905 | "- `fit_intercept` : si le terme constant doit être estimé ou considéré à 0\n", 906 | "- `normalize` : si le jeux d'apprentissage doit être normalisé avant la regression\n", 907 | "- `copy_X` : si le jeux d'apprentissage doit être copié pour éviter les effets de bords\n", 908 | "- `n_jobs` : le nombre de processeur à utiliser" 909 | ] 910 | }, 911 | { 912 | "cell_type": "code", 913 | "execution_count": null, 914 | "metadata": {}, 915 | "outputs": [], 916 | "source": [ 917 | "from sklearn.linear_model import LinearRegression\n", 918 | "lr = LinearRegression(fit_intercept=True, normalize=False, copy_X=True, n_jobs=1)\n", 919 | "print (lr.fit_intercept, lr.normalize, lr.copy_X, lr.n_jobs)" 920 | ] 921 | }, 922 | { 923 | "cell_type": "markdown", 924 | "metadata": {}, 925 | "source": [ 926 | "La classe `LinearRegression` possède également des attributs qui sont mis à jour à l'aide des *méthodes*:\n", 927 | "\n", 928 | "- `coef_` : coefficients estimés \n", 929 | "- `residues_` : somme des résidus\n", 930 | "- `intercept_` : terme constant\n", 931 | "\n", 932 | "La méthode `fit` de cette classe est un *mutator*. Elle prend en paramètre le jeux d'apprentissage, `X_train` et la variable réponse correspondante `Y_train` pour estimer les paramètres de la regression linéaire et ainsi mettre à jour les attributs correspondants." 933 | ] 934 | }, 935 | { 936 | "cell_type": "code", 937 | "execution_count": null, 938 | "metadata": {}, 939 | "outputs": [], 940 | "source": [ 941 | "X_train=[[0, 0], [1, 1], [2, 2]]\n", 942 | "Y_train = [0, 1, 2]\n", 943 | "lr.fit (X_train, Y_train)\n", 944 | "lr.coef_" 945 | ] 946 | }, 947 | { 948 | "cell_type": "markdown", 949 | "metadata": {}, 950 | "source": [ 951 | "La classe `LinearRegression` possède aussi d'autres *méthodes* qui utilisent les attributs de la classe. Par exemple:\n", 952 | "\n", 953 | "- `predict`: estime la prévision de la variable réponse d'un jeu test `X_test`\n", 954 | "- `score`: retourne le coefficient *R2* de qualité de la prévision.\n" 955 | ] 956 | }, 957 | { 958 | "cell_type": "code", 959 | "execution_count": null, 960 | "metadata": {}, 961 | "outputs": [], 962 | "source": [ 963 | "X_test = [[1.5,1.5],[2,4],[7.3,7.1]]\n", 964 | "Y_test = [1.5,2.4,7]\n", 965 | "pred = lr.predict(X_test)\n", 966 | "s = lr.score(X_test,Y_test)\n", 967 | "print(pred,s)" 968 | ] 969 | }, 970 | { 971 | "cell_type": "markdown", 972 | "metadata": { 973 | "collapsed": true 974 | }, 975 | "source": [ 976 | "## 4 *Packing* et *Unpacking*\n", 977 | "Section plus technique qui peut être sautée en première lecture.\n", 978 | "\n", 979 | "L'opérateur `*` permet, selon la situation, de \"paquéter\" ou \"dépaquéter\" les éléments d'une liste.\n", 980 | "\n", 981 | "L'opérateur `**` permet, selon la situation, de \"paquéter\" ou \"dépaquéter\" les éléments d'un dictionnaire." 982 | ] 983 | }, 984 | { 985 | "cell_type": "markdown", 986 | "metadata": {}, 987 | "source": [ 988 | "### 4.1 *Unpacking*\n", 989 | "\n", 990 | "Dans l'exemple ci-dessous. Les opérateurs \\* et \\*\\* dépaquettent les listes et dictionnaires pour les passer en arguments de fonctions." 991 | ] 992 | }, 993 | { 994 | "cell_type": "code", 995 | "execution_count": null, 996 | "metadata": {}, 997 | "outputs": [], 998 | "source": [ 999 | "def unpacking_list_and_print(a, b):\n", 1000 | " print (a)\n", 1001 | " print (b)\n", 1002 | "listarg = [3,4]\n", 1003 | "unpacking_list_and_print(*listarg)\n", 1004 | " \n", 1005 | "def unpacking_dict_and_print(k1=0, k2=0):\n", 1006 | " print (k1)\n", 1007 | " print (k2)\n", 1008 | "dictarg = {'k1':4, 'k2':8}\n", 1009 | "unpacking_dict_and_print(**dictarg)" 1010 | ] 1011 | }, 1012 | { 1013 | "cell_type": "markdown", 1014 | "metadata": {}, 1015 | "source": [ 1016 | "### 4.2 *Packing*\n", 1017 | "Ces opérateurs sont surtout utiles dans le sens du \"packing\". Les fonctions sont alors définies de sorte à recevoir un nombre inconnu d'argument qui seront ensuite \"paquétés\" et traités dans la fonction.\n", 1018 | "\n", 1019 | "L'argument `*args` permet à la fonction de recevoir un nombre supplémentaire inconnu d'arguments sans mot-clef associé." 1020 | ] 1021 | }, 1022 | { 1023 | "cell_type": "code", 1024 | "execution_count": null, 1025 | "metadata": {}, 1026 | "outputs": [], 1027 | "source": [ 1028 | "\n", 1029 | "def packing_and_print_args(required_arg, *args):\n", 1030 | " print (\"arg Nécessaire:\", required_arg)\n", 1031 | " for i, arg in enumerate(args):\n", 1032 | " print (\"args %d:\" %i, arg)\n", 1033 | "\n", 1034 | "packing_and_print_args(1, \"two\", 3)\n", 1035 | "packing_and_print_args(1, \"two\", [1,2,3],{\"a\":1,\"b\":2,\"c\":3})" 1036 | ] 1037 | }, 1038 | { 1039 | "cell_type": "markdown", 1040 | "metadata": {}, 1041 | "source": [ 1042 | "L'argument `**kwargs` permet à la fonction de recevoir un nombre supplémentaire inconnu d'arguments avec mot-clef." 1043 | ] 1044 | }, 1045 | { 1046 | "cell_type": "code", 1047 | "execution_count": null, 1048 | "metadata": {}, 1049 | "outputs": [], 1050 | "source": [ 1051 | "def packing_and_print_kwargs(def_kwarg=2, **kwargs):\n", 1052 | " print (\"kwarg défini:\", def_kwarg)\n", 1053 | " for i,(k,v) in enumerate(kwargs.items()):\n", 1054 | " print (\"kwarg %d:\" %i ,k , v) \n", 1055 | "\n", 1056 | "packing_and_print_kwargs(def_kwarg=1, sup_arg1=\"two\", sup_arg2=3)\n", 1057 | "packing_and_print_kwargs(sup_arg1=\"two\", sup_arg2=3, sup_arg3=[1,2,3])" 1058 | ] 1059 | }, 1060 | { 1061 | "cell_type": "markdown", 1062 | "metadata": {}, 1063 | "source": [ 1064 | "Les arguments `*args` et `**kwargs` peuvent être combinés dans une autre fonctions." 1065 | ] 1066 | }, 1067 | { 1068 | "cell_type": "code", 1069 | "execution_count": null, 1070 | "metadata": {}, 1071 | "outputs": [], 1072 | "source": [ 1073 | "def packing_and_print_args_and_kwargs(required_arg ,def_kwarg=2, *args, **kwargs):\n", 1074 | " print (\"arg Nécessaire:\", required_arg)\n", 1075 | " for i, arg in enumerate(args):\n", 1076 | " print (\"args %d:\" %i, arg)\n", 1077 | " print (\"kwarg défini:\", def_kwarg)\n", 1078 | " for i,(k,v) in enumerate(kwargs.items()):\n", 1079 | " print (\"kwarg %d:\" %i ,k , v )\n", 1080 | "\n", 1081 | "packing_and_print_args_and_kwargs(1, \"two\", [1,2,3] ,sup_arg1=\"two\", sup_arg2=3 )" 1082 | ] 1083 | }, 1084 | { 1085 | "cell_type": "markdown", 1086 | "metadata": {}, 1087 | "source": [ 1088 | "Ces deux opérateurs sont très utiles pour gérer des classes liées par des héritages. Les arguments `*args **kwargs` permettent alors de gérer la tranmission de cet héritage sans avoir à redéfinir les arguments à chaque étape." 1089 | ] 1090 | }, 1091 | { 1092 | "cell_type": "code", 1093 | "execution_count": null, 1094 | "metadata": {}, 1095 | "outputs": [], 1096 | "source": [ 1097 | "class Objet(object):\n", 1098 | " def __init__(self, attribut=None, *args, **kwargs):\n", 1099 | " print (attribut)\n", 1100 | "\n", 1101 | "class Objet2Point0(Objet):\n", 1102 | " def __init__(self, *args, **kwargs):\n", 1103 | " super(Objet, self).__init__(*args, **kwargs)\n", 1104 | "\n", 1105 | "class Objet3Point0(Objet2Point0):\n", 1106 | " def __init__(self,attribut2=None, *args, **kwargs):\n", 1107 | " super(Objet2Point0, self).__init__(*args, **kwargs)\n", 1108 | " print (attribut2)\n", 1109 | "\n", 1110 | "my_data = {'attribut': 'Argument1', 'attribut2': 'Argument2'}\n", 1111 | "Objet3Point0(**my_data)" 1112 | ] 1113 | }, 1114 | { 1115 | "cell_type": "markdown", 1116 | "metadata": {}, 1117 | "source": [ 1118 | "## Référence\n", 1119 | "\n", 1120 | "**Lambert K. et Osborne M.** (2010). *Fundamentals of Python: From First Programs Through Data Structures*, Course Technology." 1121 | ] 1122 | } 1123 | ], 1124 | "metadata": { 1125 | "hide_input": false, 1126 | "kernelspec": { 1127 | "display_name": "Python 3", 1128 | "language": "python", 1129 | "name": "python3" 1130 | }, 1131 | "language_info": { 1132 | "codemirror_mode": { 1133 | "name": "ipython", 1134 | "version": 3 1135 | }, 1136 | "file_extension": ".py", 1137 | "mimetype": "text/x-python", 1138 | "name": "python", 1139 | "nbconvert_exporter": "python", 1140 | "pygments_lexer": "ipython3", 1141 | "version": "3.6.6" 1142 | }, 1143 | "toc": { 1144 | "nav_menu": { 1145 | "height": "330px", 1146 | "width": "252px" 1147 | }, 1148 | "number_sections": true, 1149 | "sideBar": true, 1150 | "skip_h1_title": false, 1151 | "toc_cell": false, 1152 | "toc_position": {}, 1153 | "toc_section_display": "block", 1154 | "toc_window_display": true 1155 | } 1156 | }, 1157 | "nbformat": 4, 1158 | "nbformat_minor": 1 1159 | } 1160 | -------------------------------------------------------------------------------- /Cal5-PythonSklearnApprent.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "
\n", 8 | "\"INSA\"/ \n", 9 | "\n", 10 | "\"Wikistat\"/\n", 11 | "\n", 12 | "
" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "# \"Python\"/ [pour Statistique et Science des Données](https://github.com/wikistat/Intro-Python)" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "# Apprentissage Statistique / Machine avec \"Python\"/ & \"Scikit-Learn\"/\n", 27 | "**Résumé**: Ce calepin introduit l'utilisation de la librairie `scikit-learn` pour la modélisation et l'apprentissage. Pourquoi utiliser `scikit-learn` ? Ou non ? Liste des fonctionnalités, quelques exemples de mise en oeuvre de modélisation ([régression logistique](http://wikistat.fr/pdf/st-m-app-rlogit.pdf), [$k$-plus proches voisins](http://wikistat.fr/pdf/st-m-app-add.pdf), [arbres de décision](http://wikistat.fr/pdf/st-m-app-cart.pdf), [forêts aléatoires](http://wikistat.fr/pdf/st-m-app-agreg.pdf). Optimisation des paramètres (complexité) des modèles par [validation croisée](http://wikistat.fr/pdf/st-m-app-risque-estim.pdf). Fontions de chaînage (*pipeline*) de transformations et estimations. D'autres fonctionalités de `Scikit-learn` sont abordées dans les calepins du [dépot sur l'apprentissage](https://github.com/wikistat/Apprentissage) statistique. " 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "## 1 Introduction\n", 35 | "### 1.1 `Scikit-learn` *vs.* R\n", 36 | "L'objectif de ce tutoriel est d'introduire l'utilisation de la librairie `scikit-learn` de Python. Seule l'utilisation directe des fonctions de modélisation sont abordées d'une manière analogue à la mise en oeuvre de R dont les librairies offrent l'accès à bien plus de méthodes. La comparaison avec R repose sur les remarques suivantes.\n", 37 | "\n", 38 | "- Cette librairie manipule des objets de classe `array` de `numpy` *chargés en mémoire* et donc de taille limitée par la RAM de l'ordinateur; de façon analogue R charge en RAM des objets de type `data.frame`.\n", 39 | "- `Scikit-learn` (0.18) ne reconnaît pas (ou pas encore ?) la classe `DataFrame` de `pandas`; `scikit-learn` utilise la classe `array` de `numpy`. C'est un problème pour la gestion de variables qualitatives complexes. Une variable binaire est simplement remplacée par un codage *(0,1)* mais, en présence de plusieurs modalités, traiter celles-ci comme des entiers n'a pas de sens statistique et remplacer une variable qualitative par l'ensemble des indicatrices (*dummy variables (0,1)*) de ses modalités complique les stratégies de sélection de modèle tout en rendant inexploitable l'interprétation statistique. \n", 40 | "- Les implémentations en Python de certains algorithmes dans `scikit-learn` sont souvent plus efficaces et utilisent implicitement les capacités de parallélisation.\n", 41 | "- R offre beaucoup plus de possibilités pour la comparaison de modèles statistiques et leur interprétation. \n", 42 | "\n", 43 | "En conséquences:\n", 44 | "- Préférer R et ses librairies si la présentation des résultats et surtout leur interprétation (modèles) est prioritaire, si l'utilisation et / ou la comparaison de beaucoup de méthodes est recherchée.\n", 45 | "- Préférer Python et `scikit-learn` pour mettre au point une chaîne de traitements (*pipe line*) opérationnelle de l'extraction à une analyse privilégiant la prévision brute à l'interprétation et pour des données quantitatives ou rendues quantitatives (\"vectorisation\" de corpus de textes).\n", 46 | "\n", 47 | "En revanche, si les données sont trop volumineuses pour la taille du disque et distribuées sur les n\\oe uds d'un *cluster* avec *Hadoop*, consulter les [calepins](https://github.com/wikistat/Ateliers-Big-Data/tree/master/1-Intro-PySpark) sur l'utilisation de *Spark*.\n", 48 | "\n", 49 | "\n", 50 | "### 1.2 Fonctions d'apprentissage de `Scikit-learn`\n", 51 | "La communauté qui développe cette librairie est très active et la fait évoluer rapidement. Ne pas hésiter à consulter la [documentation](http://scikit-learn.org/stable/user_guide.html) pour des compléments. Voici une sélection de ses principales fonctionnalités en lien avec la modélisation.\n", 52 | "\n", 53 | "- Transformations (standardisation, discrétisation binaire, regroupement de modalités, imputations rudimentaires de données manquantes) , \"vectorisation\" de corpus de textes (encodage, catalogue, Tf-idf), images;\n", 54 | "- Modéle linéaire général avec pénalisation (ridge, lasso, elastic net...), analyse discriminante linéaire et quadratique, $k$ plus proches voisins, processus gaussiens, classifieur bayésien naïf, arbres de régression et classification (CART), agrégation de modèles (bagging, random forest, adaboost, gradient tree boosting), perceptron multicouche (réseau de neurones), SVM (classification, régression, détection d'atypiques...);\n", 55 | "- Algorithmes de validation croisée (loo, k-fold, VC stratifiée...) et sélection de modèles, optimisation sur une grille de paramètres, séparation aléatoire apprentissage et test, courbe ROC;\n", 56 | "- Enchaînement (*pipeline*) de traitements.\n", 57 | "\n", 58 | "En résumé, cette librairie est focalisée sur les aspects \"machine\" de l'apprentissage de données quantitatives (séries, signaux, images) volumineuses tandis que R intègre l'analyse de variables qualitatives complexes et l'interprétation statistique fine des résultats au détriment parfois de l'efficacité des calculs.\n", 59 | "\n", 60 | "### 1.3 Objectif\n", 61 | "L'objectif est d'illustrer la mise en oeuvre de quelques fonctionnalités. Consulter la [documentation](http://scikit-learn.org/stable/user_guide.html) et ses nombreux [exemples](http://scikit-learn.org/stable/auto_examples/index.html) pour plus de détails sur les possibilités d'utilisation de `scikit-learn`. \n", 62 | "\n", 63 | "Deux jeux de données élémentaires sont utilisés. Celui [déjà étudié](https://github.com/wikistat/Intro-Python) avec `pandas` et concernant le naufrage du Titanic. Il mélange des variables explicatives qualitatives et quantitatives dans un objet de la classe `DataFrame`. Pour être utilisé dans `scikit-learn` les données doivent être transformées en un objet de classe `Array` de `numpy` par le remplacement des variables qualitatives par les indicatrices de leurs modalités. L'autre ensemble de données est entièrement quantitatif. C'est un problème classique et simplifié de [reconnaissance de caractères](http://archive.ics.uci.edu/ml/datasets/Pen-Based+Recognition+of+Handwritten+Digits) qui est inclus dans la librairie `scikit-learn`.\n", 64 | "\n", 65 | "Après la phase d'exploration ([calepin précédent](https://github.com/wikistat/Intro-Python)), ce sont les fonctions de modélisation et apprentissage qui sont abordées: [régression logistique](http://wikistat.fr/pdf/st-m-app-rlogit.pdf) (titanic), [$k$- plus proches voisins](http://wikistat.fr/pdf/st-m-app-add.pdf) (caractères), [arbres de discrimination](http://wikistat.fr/pdf/st-m-app-cart.pdf), et [forêts aléatoires](http://wikistat.fr/pdf/st-m-app-agreg.pdf). Les paramètres de complexité des modèles sont optimisés par minimisation de l'[erreur de prévision](http://wikistat.fr/pdf/st-m-app-risque-estim.pdf) estimée par [validation croisée](http://wikistat.fr/pdf/st-m-app-risque-estim.pdf) *V-fold$. \n", 66 | "\n", 67 | "D'autres fonctionnalités sont rapidement illustrées : enchaînement (*pipeline*) de méthodes et automatisation, détection d'observations atypiques. Leur maîtrise est néanmoins importante pour la mise en exploitation de codes complexes efficaces." 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "## 2 Extraction des échantillons\n", 75 | "Le travail préliminaire consiste à séparer les échantillons en une partie *apprentissage* et une autre de *test* pour estimer sans biais l'[erreur de prévision](http://wikistat.fr/pdf/st-m-app-risque-estim.pdf). L'optimisation (biais-variance) de la complexité des modèles est réalisée en minimisant l'erreur estimée par [validation croisée](http://wikistat.fr/pdf/st-m-app-risque-estim.pdf) $V-fold$. \n", 76 | "\n", 77 | "### 2.1 Données \"Caractères\"\n", 78 | "Elles sont disponibles dans la librairie `Scikit-learn`." 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "# Importations \n", 88 | "import matplotlib.pyplot as plt\n", 89 | "from sklearn import datasets\n", 90 | "%matplotlib inline\n", 91 | "# les données\n", 92 | "digits = datasets.load_digits()\n", 93 | "# Contenu et mode d'obtention\n", 94 | "print(digits)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "images_and_labels = list(zip(digits.images, \n", 104 | " digits.target))\n", 105 | "for index, (image, label) in enumerate(images_and_labels[:8]):\n", 106 | " plt.subplot(2, 4, index + 1)\n", 107 | " plt.axis('off')\n", 108 | " plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')\n", 109 | " plt.title('Training: %i' % label)" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "# variables prédictives et cible\n", 119 | "X=digits.data\n", 120 | "y=digits.target" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "from sklearn.model_selection import train_test_split\n", 130 | "X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=11)" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "### 2.2 Données \"Titanic\"\n", 138 | "\n", 139 | "Les données sur le naufrage du Titanic sont décrites dans le calepin consacré à la librairie *pandas*. Reconstruire la table des données en lisant le fichier .csv." 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "# Lire les données d'apprentissage\n", 149 | "import pandas as pd\n", 150 | "path='' # si les données sont déjà dans le répertoire courant\n", 151 | "# path='http://www.math.univ-toulouse.fr/~besse/Wikistat/data/'\n", 152 | "df=pd.read_csv(path+'titanic-train.csv',skiprows=1,header=None,usecols=[1,2,4,5,9,11],\n", 153 | " names=[\"Surv\",\"Classe\",\"Genre\",\"Age\",\"Prix\",\"Port\"],dtype={\"Surv\":object,\"Classe\":object,\"Genre\":object,\"Port\":object})\n", 154 | "df.head()" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "df.shape # dimensions\n", 164 | "# Redéfinir les types \n", 165 | "df[\"Surv\"]=pd.Categorical(df[\"Surv\"],ordered=False)\n", 166 | "df[\"Classe\"]=pd.Categorical(df[\"Classe\"],ordered=False)\n", 167 | "df[\"Genre\"]=pd.Categorical(df[\"Genre\"],ordered=False)\n", 168 | "df[\"Port\"]=pd.Categorical(df[\"Port\"],ordered=False)\n", 169 | "df.dtypes" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": {}, 176 | "outputs": [], 177 | "source": [ 178 | "df.count()" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "# imputation des valeurs manquantes\n", 188 | "df[\"Age\"]=df[\"Age\"].fillna(df[\"Age\"].median())\n", 189 | "df.Port=df[\"Port\"].fillna(\"S\")" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [ 198 | "# Discrétiser les variables quantitatives\n", 199 | "df[\"AgeQ\"]=pd.qcut(df.Age,3,labels=[\"Ag1\",\"Ag2\",\"Ag3\"])\n", 200 | "df[\"PrixQ\"]=pd.qcut(df.Prix,3,labels=[\"Pr1\",\"Pr2\",\"Pr3\"])\n", 201 | "# redéfinir les noms des modalités \n", 202 | "df[\"Surv\"]=df[\"Surv\"].cat.rename_categories([\"Vnon\",\"Voui\"])\n", 203 | "df[\"Classe\"]=df[\"Classe\"].cat.rename_categories([\"Cl1\",\"Cl2\",\"Cl3\"])\n", 204 | "df[\"Genre\"]=df[\"Genre\"].cat.rename_categories([\"Gfem\",\"Gmas\"])\n", 205 | "df[\"Port\"]=df[\"Port\"].cat.rename_categories([\"Pc\",\"Pq\",\"Ps\"])\n", 206 | "df.head()" 207 | ] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "metadata": {}, 212 | "source": [ 213 | "Il est nécessaire de transformer les données car `scikit-learn` ne reconnaît pas la classe `DataFrame` de `pandas`, ce qui est bien dommage. Les variables qualitatives sont comme précédemment remplacées par les indicatrices de leurs modalités et les variables quantitatives conservées. Cela introduit une évidente redondance dans les données mais les procédures de sélection de modèle feront le tri." 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "# Table de départ\n", 223 | "df.head()" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "# Construction des indicatrices\n", 233 | "df_q=df.drop([\"Age\",\"Prix\"],axis=1)\n", 234 | "df_q.head()\n", 235 | "# Indicatrices\n", 236 | "dc=pd.DataFrame(pd.get_dummies(df_q[[\"Surv\",\"Classe\",\"Genre\",\"Port\",\"AgeQ\",\"PrixQ\"]]))\n", 237 | "dc.head()" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "# Table des indicatrices\n", 247 | "df1=pd.get_dummies(df_q[[\"Surv\",\"Classe\",\"Genre\",\"Port\",\"AgeQ\",\"PrixQ\"]])\n", 248 | "# Une seule indicatrice par variable binaire\n", 249 | "df1=df1.drop([\"Surv_Vnon\",\"Genre_Gmas\"],axis=1)\n", 250 | "# Variables quantitatives\n", 251 | "df2=df[[\"Age\",\"Prix\"]]\n", 252 | "# Concaténation\n", 253 | "df_c=pd.concat([df1,df2],axis=1)\n", 254 | "# Vérification\n", 255 | "df_c.columns" 256 | ] 257 | }, 258 | { 259 | "cell_type": "markdown", 260 | "metadata": {}, 261 | "source": [ 262 | "Extraction des échantillons d'apprentissage et test." 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": null, 268 | "metadata": {}, 269 | "outputs": [], 270 | "source": [ 271 | "# variables explicatives\n", 272 | "T=df_c.drop([\"Surv_Voui\"],axis=1)\n", 273 | "# Variable à modéliser\n", 274 | "z=df_c[\"Surv_Voui\"]\n", 275 | "# Extractions\n", 276 | "from sklearn.model_selection import train_test_split\n", 277 | "T_train,T_test,z_train,z_test=train_test_split(T,z,test_size=0.2,random_state=11)" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "**Attention**: l'échantillon test des données \"Titanic\" est relativement petit, l'estimation de l'erreur de prévision est donc sujette à caution car probablement de grande variance. Il suffit de changer l'initialisation (paramètre ` random_state`) et ré-exécuter les scripts pour s'en assurer. " 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "## 3 *K* plus proches voisins\n", 292 | "Les images des caractères sont codées par des variables quantitatives. Le problème de reconnaissance de forme ou de discrimination est adapté à l'algorithme des [$k$-plus proches voisins](http://wikistat.fr/pdf/st-m-app-add.pdf). Le paramètre à optimiser pour contrôler la complexité du modèle est le nombre de voisin `n_neighbors`. Les autres options sont décrites dans la [documentation](http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html)." 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": null, 298 | "metadata": {}, 299 | "outputs": [], 300 | "source": [ 301 | "from sklearn.neighbors import KNeighborsClassifier\n", 302 | "knn = KNeighborsClassifier(n_neighbors=10)\n", 303 | "digit_knn=knn.fit(X_train, y_train) \n", 304 | "# Estimation de l'erreur de prévision\n", 305 | "# sur l'échantillon test\n", 306 | "1-digit_knn.score(X_test,y_test)" 307 | ] 308 | }, 309 | { 310 | "cell_type": "markdown", 311 | "metadata": {}, 312 | "source": [ 313 | "Optimisation du paramètre de complexité du modèle par validation croisée en cherchant l'erreur minimale sur une grille de valeurs du paramètre avec `cv=5`-*fold cross validation* et `n_jobs=-1` pour une exécution en parallèle utilisant tous les processeurs sauf 1. Attention, comme la validation croisée est aléatoire, deux exécutions successives ne donnent pas le même résultat." 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "metadata": {}, 320 | "outputs": [], 321 | "source": [ 322 | "from sklearn.model_selection import GridSearchCV\n", 323 | "# grille de valeurs\n", 324 | "param=[{\"n_neighbors\":list(range(1,15))}]\n", 325 | "knn= GridSearchCV(KNeighborsClassifier(),param,cv=5,n_jobs=-1)\n", 326 | "digit_knnOpt=knn.fit(X_train, y_train)\n", 327 | "# paramètre optimal\n", 328 | "digit_knnOpt.best_params_[\"n_neighbors\"]" 329 | ] 330 | }, 331 | { 332 | "cell_type": "markdown", 333 | "metadata": {}, 334 | "source": [ 335 | "Le modèle `digit_knnOpt` est déjà estimé avec la valeur \"optimale\" du paramètre." 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": null, 341 | "metadata": {}, 342 | "outputs": [], 343 | "source": [ 344 | "# Estimation de l'erreur de prévision sur l'échantillon test\n", 345 | "1-digit_knnOpt.score(X_test,y_test)" 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": null, 351 | "metadata": {}, 352 | "outputs": [], 353 | "source": [ 354 | "# Prévision\n", 355 | "y_chap = digit_knnOpt.predict(X_test)\n", 356 | "# matrice de confusion\n", 357 | "table=pd.crosstab(y_test,y_chap)\n", 358 | "print(table)\n", 359 | "plt.matshow(table)\n", 360 | "plt.title(\"Matrice de Confusion\")\n", 361 | "plt.colorbar()\n", 362 | "plt.show()" 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "metadata": {}, 368 | "source": [ 369 | "### 3.3 Régression logistique\n", 370 | "La prévision de la survie, variable binaire des données \"Titanic\", se prêtent à une [régression logistique](http://wikistat.fr/pdf/st-m-app-rlogit.pdf). Les versions pénalisées (ridge, lasso, elastic net, lars) du modèle linéaire général sont les algorithmes les plus développés dans `Scikit-learn` au détriment de ceux plus classiques (*forward, backward, step-wise*) de sélection de variables en optimisant un critère de type AIC. Une version lasso de la régression logistique est testée afin d'introduire la sélection automatique des variables.\n", 371 | "\n", 372 | "Estimation et erreur de prévision du modèle complet sur l'échantillon test." 373 | ] 374 | }, 375 | { 376 | "cell_type": "code", 377 | "execution_count": null, 378 | "metadata": {}, 379 | "outputs": [], 380 | "source": [ 381 | "from sklearn.linear_model import LogisticRegression\n", 382 | "logit = LogisticRegression()\n", 383 | "titan_logit=logit.fit(T_train, z_train)\n", 384 | "# Erreur sur l'écahntillon test\n", 385 | "1-titan_logit.score(T_test, z_test)" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "execution_count": null, 391 | "metadata": {}, 392 | "outputs": [], 393 | "source": [ 394 | "# Coefficients\n", 395 | "titan_logit.coef_ " 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "Comme pour le modèle linéaire, il faudrait construire les commandes d'aide à l'interprétation des résultats.\n", 403 | "\n", 404 | "Pénalisation et optimisation du paramètre par validation croisée. Il existe une fonction spécifique mais son mode d'emploi est peu documenté; `GridSearchCV` lui est préférée." 405 | ] 406 | }, 407 | { 408 | "cell_type": "code", 409 | "execution_count": null, 410 | "metadata": {}, 411 | "outputs": [], 412 | "source": [ 413 | "# grille de valeurs\n", 414 | "param=[{\"C\":[0.01,0.096,0.098,0.1,0.12,1,10]}]\n", 415 | "logit = GridSearchCV(LogisticRegression(penalty=\"l1\"),\n", 416 | " param,cv=5,n_jobs=-1)\n", 417 | "titan_logitOpt=logit.fit(T_train, z_train)\n", 418 | "# paramètre optimal\n", 419 | "titan_logitOpt.best_params_[\"C\"]" 420 | ] 421 | }, 422 | { 423 | "cell_type": "markdown", 424 | "metadata": {}, 425 | "source": [ 426 | "Estimation de l'erreur de prévision par le modèle \"optimal\"." 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": null, 432 | "metadata": {}, 433 | "outputs": [], 434 | "source": [ 435 | "# Erreur sur l'échantillon test\n", 436 | "1-titan_logitOpt.score(T_test, z_test)" 437 | ] 438 | }, 439 | { 440 | "cell_type": "markdown", 441 | "metadata": {}, 442 | "source": [ 443 | "Petit souci supplémentaire, l'objet produit par `GridSearchCV` ne connaît pas l'attribut `.coef_`. Il faut donc ré-estimer le modèle pour connaître les coefficients." 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "execution_count": null, 449 | "metadata": {}, 450 | "outputs": [], 451 | "source": [ 452 | "# Estimation avec le paramètre optimal et coefficients\n", 453 | "LogisticRegression(penalty=\"l1\",C=titan_logitOpt.best_params_['C']).fit(T_train, z_train).coef_" 454 | ] 455 | }, 456 | { 457 | "cell_type": "markdown", 458 | "metadata": {}, 459 | "source": [ 460 | "Commenter : parcimonie du modèle vs. erreur de prévision." 461 | ] 462 | }, 463 | { 464 | "cell_type": "markdown", 465 | "metadata": {}, 466 | "source": [ 467 | "## 4 Arbre de décision\n", 468 | "### 4.1 Implémentation\n", 469 | "Les [arbres binaires de décision](http://wikistat.fr/pdf/st-m-app-cart.pdf) (CART: *classification and regression trees*) s'appliquent à tous types de variables. Les options de l'algorithme sont décrites dans la [documentation](http://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html). La complexité du modèle est gérée par deux paramètres : `max_depth`, qui détermine le nombre max de feuilles dans l'arbre, et le nombre minimales `min_samples_split` d'observations requises pour rechercher une dichotomie. \n", 470 | "\n", 471 | "**Attention**: Même s'il s'agit d'une implémentation proche de celle originale proposée par Breiman et al. (1984) il n'existe pas (encore?) comme dans R (package `rpart`) un paramètre de pénalisation de la déviance du modèle par sa complexité (nombre de feuilles) afin de construire une séquence d'arbres emboîtés dans la perspective d'un élagage (*pruning*) optimal par validation croisée. La fonction générique de $k$-*fold cross validation* `GridSearchCV` est utilisée pour optimiser le paramètre de profondeur mais sans beaucoup de précision dans l'élagage car ce dernier élimine tout un niveau et pas les seules feuilles inutiles à la qualité de la prévision.\n", 472 | "\n", 473 | "En revanche, l'implémentation anticipe sur celles des [méthodes d'agrégation de modèles](http://wikistat.fr/pdf/st-m-app-agreg.pdf) en intégrant les paramètres (nombre de variables tirées, importance...) qui leurs sont spécifiques. D'autre part, la représentation graphique d'un arbre n'est pas incluse et nécessite l'implémentation d'un autre logiciel libre: [Graphviz](http://www.graphviz.org/). \n", 474 | "\n", 475 | "Tout ceci souligne encore les objectifs de développement de cette librairie: temps de calcul et prévision brute au détriment d'une recherche d'interprétation. Dans certains exemples éventuellement pas trop compliqués, un arbre élagué de façon optimal peut en effet prévoir à peine moins bien (différence non significative) qu'une agrégation de modèles (forêt aléatoire ou **boosting**) et apporter un éclairage nettement plus pertinent qu'un algorithme de type \"boîte noire\". \n", 476 | "\n", 477 | "## 4.2 Données \"Titanic\"\n", 478 | "Estimation de l'arbre complet." 479 | ] 480 | }, 481 | { 482 | "cell_type": "code", 483 | "execution_count": null, 484 | "metadata": {}, 485 | "outputs": [], 486 | "source": [ 487 | "from sklearn.tree import DecisionTreeClassifier\n", 488 | "tree=DecisionTreeClassifier()\n", 489 | "digit_tree=tree.fit(T_train, z_train) \n", 490 | "# Estimation de l'erreur de prévision\n", 491 | "1-digit_tree.score(T_test,z_test)" 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": {}, 497 | "source": [ 498 | "Optimisation du paramètre de complexité du modèle par validation croisée en cherchant l'erreur minimale sur une grille de valeurs du paramètre avec `cv=5`-*fold cross validation* et `n_jobs=-1` pour une exécution en parallèle utilisant tous les processeurs sauf 1. Attention, comme la validation croisée est aléatoire et un arbre un modèle instable, deux exécutions successives ne donnent pas nécessairement le même résultat." 499 | ] 500 | }, 501 | { 502 | "cell_type": "code", 503 | "execution_count": null, 504 | "metadata": {}, 505 | "outputs": [], 506 | "source": [ 507 | "param=[{\"max_depth\":list(range(2,10))}]\n", 508 | "titan_tree= GridSearchCV(DecisionTreeClassifier(),param,cv=5,n_jobs=-1)\n", 509 | "titan_opt=titan_tree.fit(T_train, z_train)\n", 510 | "# paramètre optimal\n", 511 | "titan_opt.best_params_" 512 | ] 513 | }, 514 | { 515 | "cell_type": "markdown", 516 | "metadata": {}, 517 | "source": [ 518 | "La valeur \"optimale\" du paramètre reste trop importante pour la lisibilité de l'arbre. Une valeur plus faible est utilisée." 519 | ] 520 | }, 521 | { 522 | "cell_type": "code", 523 | "execution_count": null, 524 | "metadata": {}, 525 | "outputs": [], 526 | "source": [ 527 | "tree=DecisionTreeClassifier(max_depth=3)\n", 528 | "titan_tree=tree.fit(T_train, z_train)\n", 529 | "# Estimation de l'erreur de prévision\n", 530 | "# sur l'échantillon test\n", 531 | "1-titan_tree.score(T_test,z_test)" 532 | ] 533 | }, 534 | { 535 | "cell_type": "markdown", 536 | "metadata": {}, 537 | "source": [ 538 | "Noter l'amélioration de l'erreur." 539 | ] 540 | }, 541 | { 542 | "cell_type": "code", 543 | "execution_count": null, 544 | "metadata": {}, 545 | "outputs": [], 546 | "source": [ 547 | "# prévision de l'échantillon test\n", 548 | "z_chap = titan_tree.predict(T_test)\n", 549 | "# matrice de confusion\n", 550 | "table=pd.crosstab(z_test,z_chap)\n", 551 | "print(table)" 552 | ] 553 | }, 554 | { 555 | "cell_type": "markdown", 556 | "metadata": {}, 557 | "source": [ 558 | "Tracer l'arbre avec le logiciel Graphviz." 559 | ] 560 | }, 561 | { 562 | "cell_type": "code", 563 | "execution_count": null, 564 | "metadata": {}, 565 | "outputs": [], 566 | "source": [ 567 | "from sklearn.tree import export_graphviz\n", 568 | "from sklearn.externals.six import StringIO \n", 569 | "import pydotplus\n", 570 | "dot_data = StringIO() \n", 571 | "export_graphviz(titan_tree, out_file=dot_data) \n", 572 | "graph=pydotplus.graph_from_dot_data(dot_data.getvalue()) \n", 573 | "graph.write_png(\"titan_tree.png\") " 574 | ] 575 | }, 576 | { 577 | "cell_type": "markdown", 578 | "metadata": {}, 579 | "source": [ 580 | "L'arbre est généré dans un fichier image à visualiser pour se rende compte qu'il est plutôt mal élagué et pas directement interprétable sans les noms en clair des variables et modalités." 581 | ] 582 | }, 583 | { 584 | "cell_type": "code", 585 | "execution_count": null, 586 | "metadata": {}, 587 | "outputs": [], 588 | "source": [ 589 | "from IPython.display import Image\n", 590 | "Image(filename='titan_tree.png')" 591 | ] 592 | }, 593 | { 594 | "cell_type": "markdown", 595 | "metadata": {}, 596 | "source": [ 597 | "### 4.3 Données \"Caractères\"\n", 598 | "La même démarche est utilisée pour ces données." 599 | ] 600 | }, 601 | { 602 | "cell_type": "code", 603 | "execution_count": null, 604 | "metadata": {}, 605 | "outputs": [], 606 | "source": [ 607 | "# Arbre complet\n", 608 | "tree=DecisionTreeClassifier()\n", 609 | "digit_tree=tree.fit(X_train, y_train) \n", 610 | "# Estimation de l'erreur de prévision\n", 611 | "1-digit_tree.score(X_test,y_test)" 612 | ] 613 | }, 614 | { 615 | "cell_type": "code", 616 | "execution_count": null, 617 | "metadata": {}, 618 | "outputs": [], 619 | "source": [ 620 | "# Optimisation par validation croisée\n", 621 | "param=[{\"max_depth\":list(range(5,15))}]\n", 622 | "digit_tree= GridSearchCV(DecisionTreeClassifier(),param,cv=5,n_jobs=-1)\n", 623 | "digit_treeOpt=digit_tree.fit(X_train, y_train)\n", 624 | "digit_treeOpt.best_params_" 625 | ] 626 | }, 627 | { 628 | "cell_type": "code", 629 | "execution_count": null, 630 | "metadata": {}, 631 | "outputs": [], 632 | "source": [ 633 | "# Estimation de l'erreur de prévision\n", 634 | "1-digit_treeOpt.score(X_test,y_test)" 635 | ] 636 | }, 637 | { 638 | "cell_type": "code", 639 | "execution_count": null, 640 | "metadata": {}, 641 | "outputs": [], 642 | "source": [ 643 | "# Echantillon test\n", 644 | "y_chap = digit_treeOpt.predict(X_test)\n", 645 | "# matrice de confusion\n", 646 | "table=pd.crosstab(y_test,y_chap)\n", 647 | "print(table)" 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": null, 653 | "metadata": {}, 654 | "outputs": [], 655 | "source": [ 656 | "plt.matshow(table)\n", 657 | "plt.title(\"Matrice de Confusion\")\n", 658 | "plt.colorbar()\n", 659 | "plt.show()" 660 | ] 661 | }, 662 | { 663 | "cell_type": "markdown", 664 | "metadata": {}, 665 | "source": [ 666 | "Comme pour les autres méthodes, l'objet `GridSearchCV` ne contient pas tous les attibuts, dont celui `tree`, et ne permet pas de construire l'arbre. Il faudrait le ré-estimer mais comme il est bien trop complexe, ce résultat n'est pas produit." 667 | ] 668 | }, 669 | { 670 | "cell_type": "markdown", 671 | "metadata": {}, 672 | "source": [ 673 | "## 5 Forêts aléatoires\n", 674 | "L'algorithme d'agrégation de modèles le plus utilisé est celui des [forêts aléatoires](http://wikistat.fr/pdf/st-m-app-agreg.pdf) (random forest) de Breiman (2001) ce qui ne signifie pas qu'il conduit toujours à la meilleure prévision. Voir la [documentation](http://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html#sklearn.ensemble.RandomForestClassifier) pour la signification de tous les paramètres.\n", 675 | "\n", 676 | "Plus que le nombre d'arbres `n_estimators`, le paramètre à optimiser est le nombre de variables tirées aléatoirement pour la recherche de la division optimale d'un noeud: `max_features`. Par défaut, il prend la valeur $\\frac{p}{3}$ en régression et $\\sqrt{p}$ en discrimination.\n", 677 | "### 5.1 Données \"Caractères\"" 678 | ] 679 | }, 680 | { 681 | "cell_type": "code", 682 | "execution_count": null, 683 | "metadata": {}, 684 | "outputs": [], 685 | "source": [ 686 | "from sklearn.ensemble import RandomForestClassifier \n", 687 | "# définition des paramètres\n", 688 | "forest = RandomForestClassifier(n_estimators=500, \n", 689 | " criterion='gini', max_depth=None,\n", 690 | " min_samples_split=2, min_samples_leaf=1, \n", 691 | " max_features='auto', max_leaf_nodes=None,\n", 692 | " bootstrap=True, oob_score=True)\n", 693 | "# apprentissage et erreur out-of-bag\n", 694 | "forest = forest.fit(X_train,y_train)\n", 695 | "print(1-forest.oob_score_)" 696 | ] 697 | }, 698 | { 699 | "cell_type": "code", 700 | "execution_count": null, 701 | "metadata": {}, 702 | "outputs": [], 703 | "source": [ 704 | "# erreur de prévision sur le test\n", 705 | "1-forest.score(X_test,y_test)" 706 | ] 707 | }, 708 | { 709 | "cell_type": "markdown", 710 | "metadata": {}, 711 | "source": [ 712 | "L'optimisation du paramètre `max_features` peut être réalisée en minimisant l'erreur de prévision *out-of-bag*. Ce n'est pas prévu, il est aussi possible comme précédemment de minimiser l'erreur par validation croisée." 713 | ] 714 | }, 715 | { 716 | "cell_type": "code", 717 | "execution_count": null, 718 | "metadata": {}, 719 | "outputs": [], 720 | "source": [ 721 | "param=[{\"max_features\":list(range(4,64,4))}]\n", 722 | "digit_rf= GridSearchCV(RandomForestClassifier(n_estimators=100),param,cv=5,n_jobs=-1)\n", 723 | "digit_rfOpt=digit_rf.fit(X_train, y_train)\n", 724 | "# paramètre optimal\n", 725 | "digit_rfOpt.best_params_" 726 | ] 727 | }, 728 | { 729 | "cell_type": "markdown", 730 | "metadata": {}, 731 | "source": [ 732 | "Comme pour les autres méthodes, l'objet `GridSearchCV` ne propose pas tous les attributs et donc pas d'erreur *out-of-bag* ou d'importance des variables. Voir le tutoriel sur la [prévision du pic d'ozone](https://github.com/wikistat/Apprentissage/tree/master/Pic-ozone) pour plus de détails." 733 | ] 734 | }, 735 | { 736 | "cell_type": "code", 737 | "execution_count": null, 738 | "metadata": {}, 739 | "outputs": [], 740 | "source": [ 741 | "# erreur de prévision sur le test\n", 742 | "1-digit_rfOpt.score(X_test,y_test)" 743 | ] 744 | }, 745 | { 746 | "cell_type": "code", 747 | "execution_count": null, 748 | "metadata": {}, 749 | "outputs": [], 750 | "source": [ 751 | "# prévision\n", 752 | "y_chap = digit_rfOpt.predict(X_test)\n", 753 | "# matrice de confusion\n", 754 | "table=pd.crosstab(y_test,y_chap)\n", 755 | "print(table)\n", 756 | "plt.matshow(table)\n", 757 | "plt.title(\"Matrice de Confusion\")\n", 758 | "plt.colorbar()\n", 759 | "plt.show()" 760 | ] 761 | }, 762 | { 763 | "cell_type": "markdown", 764 | "metadata": {}, 765 | "source": [ 766 | "### 5.2 Données \"Titanic\"\n", 767 | "Même démarche." 768 | ] 769 | }, 770 | { 771 | "cell_type": "code", 772 | "execution_count": null, 773 | "metadata": {}, 774 | "outputs": [], 775 | "source": [ 776 | "# définition des paramètres\n", 777 | "forest = RandomForestClassifier(n_estimators=500, criterion='gini', max_depth=None, \n", 778 | " min_samples_split=2, min_samples_leaf=1, max_features='auto', max_leaf_nodes=None,bootstrap=True, oob_score=True)\n", 779 | "# apprentissage\n", 780 | "forest = forest.fit(T_train,z_train)\n", 781 | "print(1-forest.oob_score_)" 782 | ] 783 | }, 784 | { 785 | "cell_type": "code", 786 | "execution_count": null, 787 | "metadata": {}, 788 | "outputs": [], 789 | "source": [ 790 | "# erreur de prévision sur le test\n", 791 | "1-forest.score(T_test,z_test)" 792 | ] 793 | }, 794 | { 795 | "cell_type": "code", 796 | "execution_count": null, 797 | "metadata": {}, 798 | "outputs": [], 799 | "source": [ 800 | "# optimisation de max_features\n", 801 | "param=[{\"max_features\":list(range(2,15))}]\n", 802 | "titan_rf= GridSearchCV(RandomForestClassifier(n_estimators=100),param,cv=5,n_jobs=-1)\n", 803 | "titan_rfOpt=titan_rf.fit(T_train, z_train)\n", 804 | "# paramètre optimal\n", 805 | "titan_rfOpt.best_params_" 806 | ] 807 | }, 808 | { 809 | "cell_type": "code", 810 | "execution_count": null, 811 | "metadata": {}, 812 | "outputs": [], 813 | "source": [ 814 | "# erreur de prévision sur le test\n", 815 | "1-titan_rfOpt.score(T_test,z_test)" 816 | ] 817 | }, 818 | { 819 | "cell_type": "code", 820 | "execution_count": null, 821 | "metadata": {}, 822 | "outputs": [], 823 | "source": [ 824 | "# prévision\n", 825 | "z_chap = titan_rfOpt.predict(T_test)\n", 826 | "# matrice de confusion\n", 827 | "table=pd.crosstab(z_test,z_chap)\n", 828 | "print(table)" 829 | ] 830 | }, 831 | { 832 | "cell_type": "markdown", 833 | "metadata": {}, 834 | "source": [ 835 | "Modifier la valeur du paramètre pour constater sa faible influence sur la qualité plutôt médiocre du résultat. \n", 836 | "\n", 837 | "**Attention**, comme déjà signalé, l'échantillon test est de relativement faible taille (autour de 180), il serait opportun d'itérer l'extraction aléatoire d'échantillons tests (validation croisée *Monte Carlo*) pour tenter de réduire la variance de cette estimation et avoir une idée de sa distribution.\n", 838 | "\n", 839 | "C'est fait dans d'autres calepins du [dépôt d'apprentissage](https://github.com/wikistat/Apprentissage)." 840 | ] 841 | }, 842 | { 843 | "cell_type": "markdown", 844 | "metadata": {}, 845 | "source": [ 846 | "## 6 Fonction *pipeline*\n", 847 | "Pour enchaîner et brancher (*plugin*) plusieurs traitements, généralement des transformations suivies d'une modélisation. Utiliser les fonctionnalités de cette section sans modération afin d'optimiser la structure et l'efficacité (parallélisation) de codes complexes. \n", 848 | "\n", 849 | "### 6.1 Familles de transformations (*transformers*)\n", 850 | "Classification ou régression sont souvent la dernière étape d'un procédé long et complexe. Dans la \"vraie vie\", les données ont besoin d'être extraites, sélectionnées, nettoyées, standardisées, complétées... (*data munging*) avant d'alimenter un algorithme d'apprentissage. Pour structurer le code, *Sciki-learn* propose d'utiliser le principe d'une API (*application programming interface*) nommée *transformer*. \n", 851 | "\n", 852 | "Ces fonctionnalités sont illustrées sur les mêmes données de reconnaissance de caractères. " 853 | ] 854 | }, 855 | { 856 | "cell_type": "code", 857 | "execution_count": null, 858 | "metadata": {}, 859 | "outputs": [], 860 | "source": [ 861 | "# Rechargement des données\n", 862 | "from sklearn.datasets import load_digits\n", 863 | "from sklearn.model_selection import train_test_split\n", 864 | "digits = load_digits()\n", 865 | "X, y = digits.data, digits.target\n", 866 | "X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)\n", 867 | "\n", 868 | "# Plot\n", 869 | "sample_id = 42\n", 870 | "plt.imshow(X[sample_id].reshape((8, 8)), interpolation=\"nearest\", cmap=plt.cm.Blues)\n", 871 | "plt.title(\"y = %d\" % y[sample_id])\n", 872 | "plt.show()" 873 | ] 874 | }, 875 | { 876 | "cell_type": "markdown", 877 | "metadata": {}, 878 | "source": [ 879 | "#### Normalisations, réductions" 880 | ] 881 | }, 882 | { 883 | "cell_type": "code", 884 | "execution_count": null, 885 | "metadata": {}, 886 | "outputs": [], 887 | "source": [ 888 | "import numpy as np\n", 889 | "from sklearn.preprocessing import StandardScaler\n", 890 | "tf = StandardScaler()\n", 891 | "tf.fit(X_train, y_train)\n", 892 | "Xt_train = tf.transform(X) \n", 893 | "print(\"Moyenne avant centrage et réduction =\", np.mean(X_train))\n", 894 | "print(\"Moyenne après centrage et réduction =\", np.mean(Xt_train))\n", 895 | "# See also Binarizer, MinMaxScaler, Normalizer, ..." 896 | ] 897 | }, 898 | { 899 | "cell_type": "code", 900 | "execution_count": null, 901 | "metadata": {}, 902 | "outputs": [], 903 | "source": [ 904 | "# Raccourci: Xt = tf.fit_transform(X)\n", 905 | "tf.fit_transform(X)" 906 | ] 907 | }, 908 | { 909 | "cell_type": "code", 910 | "execution_count": null, 911 | "metadata": {}, 912 | "outputs": [], 913 | "source": [ 914 | "# NB. La standardisation préalable est indispensable pour certains algorithmes\n", 915 | "# notamment les SVM\n", 916 | "from sklearn.svm import SVC\n", 917 | "clf = SVC()\n", 918 | "# Calcul des scores (bien classés)\n", 919 | "print(\"Sans standardisation =\", clf.fit(X_train, y_train).score(X_test, y_test))\n", 920 | "print(\"Avec standardisation =\", clf.fit(tf.transform(X_train), y_train).score(tf.transform(X_test), y_test))" 921 | ] 922 | }, 923 | { 924 | "cell_type": "markdown", 925 | "metadata": {}, 926 | "source": [ 927 | "#### Sélection de variables par élimination pas à pas\n", 928 | "La proicédure `RFE` (*récursive feature selection*) supprime une à une les variables les moins significatives ou moins importantes au sens du critère du modèle utilisé; dans cet exemple, il s'agit des forêts aléatoires." 929 | ] 930 | }, 931 | { 932 | "cell_type": "code", 933 | "execution_count": null, 934 | "metadata": {}, 935 | "outputs": [], 936 | "source": [ 937 | "# Sélection de variables par élémination pas à pas\n", 938 | "from sklearn.feature_selection import RFE\n", 939 | "from sklearn.ensemble import RandomForestClassifier\n", 940 | "tf = RFE(RandomForestClassifier(), n_features_to_select=10, verbose=1)\n", 941 | "Xt = tf.fit_transform(X_train, y_train)\n", 942 | "print(\"Shape =\", Xt.shape)\n", 943 | "\n", 944 | "# Variables (pixels) sélectionnées\n", 945 | "plt.imshow(tf.get_support().reshape((8, 8)), interpolation=\"nearest\", cmap=plt.cm.Blues)\n", 946 | "plt.show()" 947 | ] 948 | }, 949 | { 950 | "cell_type": "markdown", 951 | "metadata": {}, 952 | "source": [ 953 | "#### Décomposition, factorisation, réduction de dimension\n", 954 | "Possibilité, par exemple, de récupérer les *q* premières composantes principales de l'ACP comme résultat d'une transformation." 955 | ] 956 | }, 957 | { 958 | "cell_type": "code", 959 | "execution_count": null, 960 | "metadata": {}, 961 | "outputs": [], 962 | "source": [ 963 | "# par ACP ou SVD\n", 964 | "from sklearn.decomposition import PCA\n", 965 | "tf = PCA(n_components=2)\n", 966 | "Xt_train = tf.fit_transform(X_train)" 967 | ] 968 | }, 969 | { 970 | "cell_type": "markdown", 971 | "metadata": {}, 972 | "source": [ 973 | "#### Fonction de transformation définie par l'utilisateur\n", 974 | "Une fonction de transformation ou *transformer* est définie et s'applique à un jeu de données avec la syntaxe ci-dessous." 975 | ] 976 | }, 977 | { 978 | "cell_type": "code", 979 | "execution_count": null, 980 | "metadata": {}, 981 | "outputs": [], 982 | "source": [ 983 | "from sklearn.preprocessing import FunctionTransformer\n", 984 | "def increment(X):\n", 985 | " return X + 1\n", 986 | "tf = FunctionTransformer(func=increment)\n", 987 | "Xt = tf.fit_transform(X)\n", 988 | "print(X[0])\n", 989 | "print(Xt[0])" 990 | ] 991 | }, 992 | { 993 | "cell_type": "markdown", 994 | "metadata": {}, 995 | "source": [ 996 | "### 6.4 *Pipelines*\n", 997 | "\n", 998 | "Des transformations sont chaînées en une séquence constituant un *pipeline*." 999 | ] 1000 | }, 1001 | { 1002 | "cell_type": "code", 1003 | "execution_count": null, 1004 | "metadata": {}, 1005 | "outputs": [], 1006 | "source": [ 1007 | "from sklearn.pipeline import make_pipeline\n", 1008 | "from sklearn.preprocessing import StandardScaler\n", 1009 | "from sklearn.feature_selection import RFE\n", 1010 | "#tf = RFE(RandomForestClassifier(), n_features_to_select=10)\n", 1011 | "# La succession de deux transformeurs constituent un transformeur\n", 1012 | "tf = make_pipeline(StandardScaler(), RFE(RandomForestClassifier(),n_features_to_select=10))\n", 1013 | "tf.fit(X_train, y_train)" 1014 | ] 1015 | }, 1016 | { 1017 | "cell_type": "code", 1018 | "execution_count": null, 1019 | "metadata": {}, 1020 | "outputs": [], 1021 | "source": [ 1022 | "Xt_train = tf.transform(X_train)\n", 1023 | "print(\"Mean =\", np.mean(Xt_train))\n", 1024 | "print(\"Shape =\", Xt_train.shape)" 1025 | ] 1026 | }, 1027 | { 1028 | "cell_type": "markdown", 1029 | "metadata": {}, 1030 | "source": [ 1031 | "Une chaîne de transformations suivi d'un classifieur construisent un nouveau classifieur" 1032 | ] 1033 | }, 1034 | { 1035 | "cell_type": "code", 1036 | "execution_count": null, 1037 | "metadata": {}, 1038 | "outputs": [], 1039 | "source": [ 1040 | "clf = make_pipeline(StandardScaler(), \n", 1041 | " RFE(RandomForestClassifier(), n_features_to_select=10), \n", 1042 | " RandomForestClassifier())\n", 1043 | "clf.fit(X_train, y_train)\n", 1044 | "print(clf.predict_proba(X_test)[:5])" 1045 | ] 1046 | }, 1047 | { 1048 | "cell_type": "code", 1049 | "execution_count": null, 1050 | "metadata": {}, 1051 | "outputs": [], 1052 | "source": [ 1053 | "# L'hyperparamètre est accessible\n", 1054 | "print(\"n_features =\", clf.get_params()[\"rfe__estimator__n_estimators\"])" 1055 | ] 1056 | }, 1057 | { 1058 | "cell_type": "markdown", 1059 | "metadata": {}, 1060 | "source": [ 1061 | "L'optimisation des paramètres par validation croisée est obtenue avec la même fonction mais peut prendre du temps si plusieurs paramètres sont cocernés! Le pipeline construit à titre illustratif n'est certainement pas optimal." 1062 | ] 1063 | }, 1064 | { 1065 | "cell_type": "code", 1066 | "execution_count": null, 1067 | "metadata": {}, 1068 | "outputs": [], 1069 | "source": [ 1070 | "grid = GridSearchCV(clf, param_grid={\"rfe__estimator__n_estimators\": [5, 10],\n", 1071 | " \"randomforestclassifier__max_features\": [0.1, 0.25, 0.5]})\n", 1072 | "grid.fit(X_train, y_train)\n", 1073 | "print(\"Valeurs optimales =\", grid.best_params_)" 1074 | ] 1075 | }, 1076 | { 1077 | "cell_type": "markdown", 1078 | "metadata": {}, 1079 | "source": [ 1080 | "### 6.5 Union de caractéristiques\n", 1081 | "\n", 1082 | "Des transformations sont appliquées en parallèle pour réunir en un seul ensemble des transformations des données." 1083 | ] 1084 | }, 1085 | { 1086 | "cell_type": "code", 1087 | "execution_count": null, 1088 | "metadata": {}, 1089 | "outputs": [], 1090 | "source": [ 1091 | "from sklearn.pipeline import make_union\n", 1092 | "from sklearn.decomposition import PCA, FastICA\n", 1093 | "tf = make_union(PCA(n_components=10), FastICA(n_components=10))\n", 1094 | "Xt_train = tf.fit_transform(X_train)\n", 1095 | "print(\"Shape =\", Xt_train.shape)" 1096 | ] 1097 | }, 1098 | { 1099 | "cell_type": "markdown", 1100 | "metadata": {}, 1101 | "source": [ 1102 | "### 6.6 Compositions emboîtées\n", 1103 | "\n", 1104 | "Comme des pipelines and des unions sont eux-mêmes des estimateurs, ils peuvent être composés dans une structure emboîtée pour construire des combinaisons complexes de modèles comme ceux remportant les concours de type [*kaggle](https://www.kaggle.com/).\n", 1105 | "\n", 1106 | "Les données initiales sont unies aux composantes de l'ACP, puis les variables les plus importantes au sens des forêts aléatoires sont sélectionnées avant de servir à l'apprentissage d'un réseau de neurones. Ce n'est sûrement pas une stratégie optimale !" 1107 | ] 1108 | }, 1109 | { 1110 | "cell_type": "code", 1111 | "execution_count": null, 1112 | "metadata": {}, 1113 | "outputs": [], 1114 | "source": [ 1115 | "from sklearn.ensemble import RandomForestClassifier\n", 1116 | "from sklearn.neural_network import MLPClassifier\n", 1117 | "\n", 1118 | "clf = make_pipeline(\n", 1119 | " # Build features\n", 1120 | " make_union(\n", 1121 | " FunctionTransformer(func=lambda X: X), PCA(),), \n", 1122 | " # Select the best features\n", 1123 | " RFE(RandomForestClassifier(), n_features_to_select=10),\n", 1124 | " # Train\n", 1125 | " MLPClassifier(max_iter=500)\n", 1126 | ")\n", 1127 | "\n", 1128 | "clf.fit(X_train, y_train)" 1129 | ] 1130 | }, 1131 | { 1132 | "cell_type": "markdown", 1133 | "metadata": {}, 1134 | "source": [ 1135 | "Effectivement la combinaison n'est pas optimale:" 1136 | ] 1137 | }, 1138 | { 1139 | "cell_type": "code", 1140 | "execution_count": null, 1141 | "metadata": {}, 1142 | "outputs": [], 1143 | "source": [ 1144 | "# erreur de test\n", 1145 | "1-clf.score(X_test,y_test)" 1146 | ] 1147 | } 1148 | ], 1149 | "metadata": { 1150 | "hide_input": false, 1151 | "kernelspec": { 1152 | "display_name": "Python 3", 1153 | "language": "python", 1154 | "name": "python3" 1155 | }, 1156 | "language_info": { 1157 | "codemirror_mode": { 1158 | "name": "ipython", 1159 | "version": 3 1160 | }, 1161 | "file_extension": ".py", 1162 | "mimetype": "text/x-python", 1163 | "name": "python", 1164 | "nbconvert_exporter": "python", 1165 | "pygments_lexer": "ipython3", 1166 | "version": "3.6.6" 1167 | }, 1168 | "toc": { 1169 | "nav_menu": {}, 1170 | "number_sections": true, 1171 | "sideBar": true, 1172 | "skip_h1_title": false, 1173 | "toc_cell": false, 1174 | "toc_position": {}, 1175 | "toc_section_display": "block", 1176 | "toc_window_display": false 1177 | } 1178 | }, 1179 | "nbformat": 4, 1180 | "nbformat_minor": 1 1181 | } 1182 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Philippe Besse 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## INSA | [*Mathématiques Appliquées*](http://www.math.insa-toulouse.fr/fr/index.html), [`Science des Données`](http://www.math.insa-toulouse.fr/fr/enseignement.html) 2 | 3 | # Python [pour Statistique et *Science des Données*](https://github.com/wikistat/Intro-Python) 4 | 5 | L'objectif de ces tutoriels, proposés sous forme de calepins ([*jupyter notebooks*](http://jupyter.org/)), est d'introduire les principaux concepts et fonctionnalités du langage [Python](https://www.python.org/) en insistant sur ceux indispensable au statisticien, maintenant *data scientist*. Syntaxe, objets et classes, fonctions graphiques, techniques de préparation (*munging* ou *wrangling*) des données, éventuellement massives (*big data*), puis leur analyse (*big data analytics*) en enchaînant phases d'[exploration](http://wikistat.fr/) et de [modélisation](http://wikistat.fr/) ou [apprentissage](http://wikistat.fr/) (machine / statistique). Des exemples plus détaillés sur des données spécifiques (en R et en python) sont proposés sur [wikistat.fr](http://wikistat.fr/) ainsi que dans les autres dépôts de ce site. Les méthodes sont exposées dans les vignettes de [wikistat](http://wikistat.fr/). 6 | 7 | Les documents sont des calepins (*notebooks*) au format .ipynb à télécharger et ouvrir dans *Jupyter*. Il suffit pour cela de cloner le dépôt dans son propre environnement [GitHub](https://github.com/) ou de télécharger l'archive zippée. 8 | 9 | ## Tutoriels d'initiation à [Python](https://www.python.org/) 10 | - [`Cal1-introPython`](https://github.com/wikistat/Intro-Python/blob/master/Cal1-introPython.ipynb) La syntaxe, les objects et classes de Pyhton. Initiation au langage. 11 | - [`Cal2-PythonPandas`](https://github.com/wikistat/Intro-Python/blob/master/Cal2-PythonPandas.ipynb). Trafic de données (*data munging*) avec pandas. 12 | - [`Cal3-PythonGraphes`](https://github.com/wikistat/Intro-Python/blob/master/Cal3-PythonGraphes.ipynb) Syntaxes de base pour des graphes en Python. 13 | - [`Cal4-PythonProg`](https://github.com/wikistat/Intro-Python/blob/master/Cal4-PythonProg.ipynb) Programmation, classes, objets, programmation fonctionnelle. 14 | - [`Cal5-PythonSklearnApprent`](https://github.com/wikistat/Intro-Python/blob/master/Cal5-PythonSklearnApprent.ipynb) Introduction à la modélisation Statistique et à l'apprentissage machine avec Scikit-learn; construction de *pipelines*. 15 | 16 | ## Pourquoi [Python](https://www.python.org/) 17 | 18 | Le langage [Python](https://www.python.org/) est développé et diffusé par la [*Python Software Foundation*](https://www.python.org/psf/) selon une licence [GPL-compatible](https://docs.python.org/3/license.html). À partir d'applications initialement de calcul scientifique (image, signal...), son utilisation s'est généralisée dans de nombreux domaines et notamment pour l'analyse statistique de données pouvant être volumineuses. Il est donc "libre", efficace en calcul numérique (librairie `NumPy`), orienté objet, propose de la programmation fonctionnelle... et bénéficie d'une communauté très active qui développe de nombreuses applications et librairies. 19 | 20 | L'objectif de ce tutoriel est d'introduire le langage Python3 et quelques librairies pour préparer puis commencer à analyser des données. Lorsque celles-ci sont trop volumineuses pour la taille du disque et distribuées sur les n\oe uds d'un `cluster` sous `Hadoop` c'est encore le langage Python (API PySpark) qui permet de passer à l'échelle en utilisant la technologie [*Spark*](https://spark.apache.org/) et éventuellement la librairie [MLlib](https://spark.apache.org/mllib/). 21 | 22 | De façon plus précise, Python et la librairie `pandas` offrent des outils efficaces, comme le découpage automatique en morceaux (`chunks`) adaptés à la taille de la mémoire vive ou encore l'accès à des données au format binaire HDF5 (librairie `Pytable`), pour lire (format `.csv` ou fixe), gérer, pré-traiter, trafiquer (en jargon : *data munging* ou *wrangling*), visualiser des données volumineuses. Néanmoins, la parallélisation des traitements pour des très volumineuses ou en flux donc distribuées sera sans doute plus efficace avec la technologie adaptée [*Spark*](https://spark.apache.org/). 23 | 24 | La version 3.6. de Python est celle actuellement la "plus récente". Le passage à la version 3 introduisit une [rupture de compatibilité](https://wiki.python.org/moin/Python2orPython3) par rapport à la version 2 qui est toujours en développement (2.7). Il peut rester nécessaire de pouvoir exécuter les deux versions selon les librairies utilisées et applications recherchées. La version 2.7 inclut des ajouts permettant des éléments de "rétro"-compatibilité avec la version 3. Pour l'usage rudimentaire de ces tutoriels, il semble que les deux versions soient compatibles; c'est la version 3.6 qui est utilisée. 25 | --------------------------------------------------------------------------------