{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "intro_to_pandas.ipynb", "version": "0.3.2", "views": {}, "default_view": {}, "collapsed_sections": [ "JndnmDMp66FL", "YHIWvc9Ms-Ll", "TJffr5_Jwqvd" ] } }, "cells": [ { "metadata": { "id": "JndnmDMp66FL", "colab_type": "text" }, "source": [ "#### Copyright 2017 Google LLC." ], "cell_type": "markdown" }, { "metadata": { "id": "hMqWDc_m6rUC", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } }, "cellView": "both" }, "source": [ "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "rHLcriKWLRe4", "colab_type": "text" }, "source": [ " # Présentation rapide de Pandas"]}, { "metadata": { "id": "QvJBqX8_Bctk", "colab_type": "text" }, "cell_type": "markdown", "source": [ "**Objectifs d'apprentissage :**\n", " * Introduction aux structures de données `DataFrame` et `Series` de la bibliothèque *Pandas*\n", " * Accéder aux données et les manipuler dans une structure `DataFrame` et `Series`\n", " * Importer des données d'un fichier CSV dans un `DataFrame` *Pandas*\n", " * Réindexer un `DataFrame` pour mélanger les données" ] }, { "cell_type": "markdown", "metadata": { "id": "TIFJ83ZTBctl", "colab_type": "text" }, "source": [ " [*Pandas*](http://pandas.pydata.org/) est une API d'analyse de données orientée colonnes. C'est un excellent outil pour manipuler et analyser des données d'entrée. Beaucoup de frameworks d'apprentissage automatique acceptent les structures de données *Pandas* en entrée.\n", "Il faudrait des pages et des pages pour présenter de manière exhaustive l'API *Pandas*, mais les concepts fondamentaux sont relativement simples. Aussi, nous avons décidé de vous les exposer ci-dessous. Pour une description plus complète, vous pouvez consulter le [site de documentation de *Pandas*](http://pandas.pydata.org/pandas-docs/stable/index.html), sur lequel vous trouverez de multiples informations ainsi que de nombreux didacticiels."]}, { "cell_type": "markdown", "metadata": { "id": "s_JOISVgmn9v", "colab_type": "text" }, "source": [ " ## Concepts de base\n", "\n", "La ligne de code suivante permet d'importer l'API *pandas* et d'afficher sa version :"]}, { "metadata": { "id": "aSRYu62xUi3g", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "import pandas as pd\n", "pd.__version__" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "daQreKXIUslr", "colab_type": "text" }, "source": [ " On distingue deux grandes catégories de structures de données *Pandas* :\n", "\n", " * Le **`DataFrame`**, un tableau relationnel de données, avec des lignes et des colonnes étiquetées\n", " * La **`Series`**, constituée d'une seule colonne. Un `DataFrame` contient une ou plusieurs `Series`, chacune étant étiquetée.\n", "\n", "Le DataFrame est une abstraction fréquemment utilisée pour manipuler des données. [Spark](https://spark.apache.org/) et [R](https://www.r-project.org/about.html) proposent des implémentations similaires."]}, { "cell_type": "markdown", "metadata": { "id": "fjnAk1xcU0yc", "colab_type": "text" }, "source": [ " Pour créer une `Series`, vous pouvez notamment créer un objet `Series`. Par exemple :"]}, { "metadata": { "id": "DFZ42Uq7UFDj", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "pd.Series(['San Francisco', 'San Jose', 'Sacramento'])" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "U5ouUp1cU6pC", "colab_type": "text" }, "source": [ " Il est possible de créer des objets `DataFrame` en transmettant un `dictionnaire` qui met en correspondance les noms de colonnes (des `chaînes de caractères`) avec leur `Series` respective. Lorsque la longueur de la `Series` ne correspond pas, les valeurs manquantes sont remplacées par des valeurs [NA/NaN](http://pandas.pydata.org/pandas-docs/stable/missing_data.html) spéciales. Exemple :"]}, { "metadata": { "id": "avgr6GfiUh8t", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "city_names = pd.Series(['San Francisco', 'San Jose', 'Sacramento'])\n", "population = pd.Series([852469, 1015785, 485199])\n", "\n", "pd.DataFrame({ 'City name': city_names, 'Population': population })" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "oa5wfZT7VHJl", "colab_type": "text" }, "source": [ " Le plus souvent, vous chargez un fichier entier dans un `DataFrame`. Dans l'exemple qui suit, le fichier chargé contient des données immobilières pour la Californie. Exécutez la cellule suivante pour charger les données et définir les caractéristiques :"]}, { "metadata": { "id": "av6RYOraVG1V", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "california_housing_dataframe = pd.read_csv(\"https://storage.googleapis.com/mledu-datasets/california_housing_train.csv\", sep=\",\")\n", "california_housing_dataframe.describe()" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "WrkBjfz5kEQu", "colab_type": "text" }, "source": [ " Dans l'exemple ci-dessus, la méthode `DataFrame.describe` permet d'afficher des statistiques intéressantes concernant un `DataFrame`. La fonction `DataFrame.head` est également utile pour afficher les premiers enregistrements d'un `DataFrame` :"]}, { "metadata": { "id": "s3ND3bgOkB5k", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "california_housing_dataframe.head()" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "w9-Es5Y6laGd", "colab_type": "text" }, "source": [ " Autre fonction puissante de *Pandas* : la représentation graphique. Avec `DataFrame.hist`, par exemple, vous pouvez vérifier rapidement la façon dont les valeurs d'une colonne sont distribuées :"]}, { "metadata": { "id": "nqndFVXVlbPN", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "california_housing_dataframe.hist('housing_median_age')" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "XtYZ7114n3b-", "colab_type": "text" }, "source": [ " ## Accéder aux données\n", "\n", "L'accès aux données d'un `DataFrame` s'effectue au moyen d'opérations de liste ou de dictionnaire Python courantes :"]}, { "metadata": { "id": "_TFm7-looBFF", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities = pd.DataFrame({ 'City name': city_names, 'Population': population })\n", "print type(cities['City name'])\n", "cities['City name']" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "metadata": { "id": "V5L6xacLoxyv", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "print type(cities['City name'][1])\n", "cities['City name'][1]" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "metadata": { "id": "gcYX1tBPugZl", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "print type(cities[0:2])\n", "cities[0:2]" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "65g1ZdGVjXsQ", "colab_type": "text" }, "source": [ " *Pandas* propose en outre une API extrêmement riche, avec des fonctions avancées d'[indexation et de sélection](http://pandas.pydata.org/pandas-docs/stable/indexing.html), que nous ne pouvons malheureusement pas aborder ici dans le détail."]}, { "cell_type": "markdown", "metadata": { "id": "RM1iaD-ka3Y1", "colab_type": "text" }, "source": [ " ## Manipuler des données\n", "\n", "Il est possible d'effectuer des opérations arithmétiques de base de Python sur les `Series`. Par exemple :"]}, { "metadata": { "id": "XWmyCFJ5bOv-", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "population / 1000." ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "TQzIVnbnmWGM", "colab_type": "text" }, "source": [ " [NumPy](http://www.numpy.org/) est un kit d'outils de calculs scientifiques populaire. Les `Series` *Pandas* peuvent faire office d'arguments pour la plupart des fonctions NumPy :"]}, { "metadata": { "id": "ko6pLK6JmkYP", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "import numpy as np\n", "\n", "np.log(population)" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "xmxFuQmurr6d", "colab_type": "text" }, "source": [ " La méthode `Series.apply` convient pour les transformations à une colonne plus complexes. Comme la [fonction `map`](https://docs.python.org/2/library/functions.html#map) de Python, elle accepte en argument une [fonction `lambda`](https://docs.python.org/2/tutorial/controlflow.html#lambda-expressions), appliquée à chaque valeur.\n", "\n", "L'exemple ci-dessous permet de créer une `Series` signalant si la `population` dépasse ou non un million d'habitants :"]}, { "metadata": { "id": "Fc1DvPAbstjI", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "population.apply(lambda val: val > 1000000)" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "ZeYYLoV9b9fB", "colab_type": "text" }, "source": [ " \n", "La modification des `DataFrames` est également très simple. Ainsi, le code suivant permet d'ajouter deux `Series` à un `DataFrame` existant :"]}, { "metadata": { "id": "0gCEX99Hb8LR", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities['Area square miles'] = pd.Series([46.87, 176.53, 97.92])\n", "cities['Population density'] = cities['Population'] / cities['Area square miles']\n", "cities" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "6qh63m-ayb-c", "colab_type": "text" }, "source": [ " ## Exercice n° 1\n", "\n", "Modifiez le tableau `cities` en ajoutant une colonne booléenne qui prend la valeur True si et seulement si les *deux* conditions suivantes sont remplies :\n", "\n", " * La ville porte le nom d'un saint.\n", " * La ville s'étend sur plus de 50 miles carrés.\n", "\n", "**Remarque** : Pour combiner des `Series` booléennes, utilisez des opérateurs de bits, pas les opérateurs booléens classiques. Par exemple, pour le *ET logique*, utilisez `&` au lieu de `and`.\n", "\n", "**Astuce** : En espagnol, \"San\" signifie \"saint\"."]}, { "metadata": { "id": "zCOn8ftSyddH", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "# Your code here" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "YHIWvc9Ms-Ll", "colab_type": "text" }, "source": [ " ### Solution\n", "\n", "Cliquez ci-dessous pour afficher la solution."]}, { "metadata": { "id": "T5OlrqtdtCIb", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities['Is wide and has saint name'] = (cities['Area square miles'] > 50) & cities['City name'].apply(lambda name: name.startswith('San'))\n", "cities" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "f-xAOJeMiXFB", "colab_type": "text" }, "source": [ " ## Index\n", "Les objets `Series` et `DataFrame` définissent également une propriété `index`, qui affecte un identifiant à chaque élément d'une `Series` ou chaque ligne d'un `DataFrame`. \n", "\n", "Par défaut, *Pandas* affecte les valeurs d'index en suivant l'ordre des données source. Ces valeurs ne varient pas par la suite ; elles restent inchangées en cas de réagencement des données."]}, { "metadata": { "id": "2684gsWNinq9", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "city_names.index" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "metadata": { "id": "F_qPe2TBjfWd", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities.index" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "hp2oWY9Slo_h", "colab_type": "text" }, "source": [ " Appelez `DataFrame.reindex` pour réorganiser manuellement les lignes. Le code suivant, par exemple, revient à trier les données par nom de ville :"]}, { "metadata": { "id": "sN0zUzSAj-U1", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities.reindex([2, 0, 1])" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "-GQFz8NZuS06", "colab_type": "text" }, "source": [ " La réindexation est un excellent moyen de mélanger (organiser aléatoirement) les données d'un `DataFrame`. Dans l'exemple ci-dessous, l'index de type tableau est transmis à la fonction NumPy `random.permutation`, qui mélange les valeurs. En appelant `reindex` avec ce tableau mélangé, nous mélangeons également les lignes du `DataFrame`.\n", "Exécutez plusieurs fois la cellule suivante !"]}, { "metadata": { "id": "mF8GC0k8uYhz", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities.reindex(np.random.permutation(cities.index))" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "fSso35fQmGKb", "colab_type": "text" }, "source": [ " Pour en savoir plus, consultez la [documentation relative aux index](http://pandas.pydata.org/pandas-docs/stable/indexing.html#index-objects)."]}, { "cell_type": "markdown", "metadata": { "id": "8UngIdVhz8C0", "colab_type": "text" }, "source": [ " ## Exercice n° 2\n", "\n", "La méthode `reindex` autorise les valeurs d'index autres que celles associées au `DataFrame` d'origine. Voyez ce qu'il se passe lorsque vous utilisez ce type de valeurs ! Pourquoi est-ce autorisé à votre avis ?"]}, { "metadata": { "id": "PN55GrDX0jzO", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "# Your code here" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "TJffr5_Jwqvd", "colab_type": "text" }, "source": [ " ### Solution\n", "\n", "Cliquez ci-dessous pour afficher la solution."]}, { "cell_type": "markdown", "metadata": { "id": "8oSvi2QWwuDH", "colab_type": "text" }, "source": [ " Lorsque le tableau d'entrée `reindex` contient des valeurs d'index ne faisant pas partie de la liste des index du `DataFrame` d'origine, `reindex` ajoute des lignes pour ces index \\'manquants\\' et insère la valeur `NaN` dans les colonnes correspondantes :"]}, { "metadata": { "id": "yBdkucKCwy4x", "colab_type": "code", "colab": { "autoexec": { "startup": false, "wait_interval": 0 } } }, "source": [ "cities.reindex([0, 4, 5, 2])" ], "cell_type": "code", "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "2l82PhPbwz7g", "colab_type": "text" }, "source": [ " Ce comportement est souhaitable, car les index sont souvent des chaînes de caractères extraites des données réelles. La [documentation *Pandas* sur la fonction `reindex`](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.reindex.html) inclut un exemple avec des noms de navigateurs comme valeurs d'index).\n", "\n", "Dans ce cas, autoriser les index \\'manquants\\' facilite la réindexation à l'aide d'une liste externe, car vous n'avez pas à vous préoccuper de l'intégrité des données d'entrée."]} ] }