Skip to content

Instantly share code, notes, and snippets.

@kartoch
Last active April 4, 2019 16:45
Show Gist options
  • Save kartoch/fc7478562f0d8ce6d805a575d0061d03 to your computer and use it in GitHub Desktop.
Save kartoch/fc7478562f0d8ce6d805a575d0061d03 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Projet Gas (2018-2019)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Présentation\n",
"\n",
"### Sujet\n",
"\n",
"Le but de ce projet est d'étudier un jeu de données en commençant par les étapes ETL (*Extract-Transform-Load*), l'exploration des données puis l'étude d'une ou plusieurs questions ouvertes."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dataset\n",
"\n",
"Les fichiers de données utilisés dans le cadre de ce projet sont ceux des tarifs des pompes à essence en France.\n",
"\n",
"Ils sont disponibles ici: https://www.prix-carburants.gouv.fr/rubrique/opendata/ \n",
"\n",
"Durant le TP en salle, le mieux est de faire une copie locale des fichiers sur mon compte dans le répertoire temporaire de la machine (/tmp)."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"!mkdir /tmp/gas"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"!cp ~jcartign/datasets/PrixCarburants_*.zip /tmp/gas"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Logiciels étudiés\n",
"\n",
"* Numpy et Pandas\n",
"* Seaborn et matplotlib\n",
"\n",
"Si vous avez fait votre installation avec anaconda ou miniconda, voici la commande pour installer ces logiciels:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"%conda install pytables fastparquet numpy seaborn matplotlib pandas"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Organisation du projet\n",
"\n",
"Le projet doit être organisé en une série de feuilles Jupyter numérotées.\n",
"\n",
"Il y a (au moins) 4 feuilles à rendre:\n",
"\n",
"* conversion des XML en CSV\n",
"* chargement des CSV dans pandas, typage des données et conversion dans le format parquet\n",
"* exploration des données\n",
"* étude d'une (ou plusieurs) questions ouvertes\n",
"\n",
"### Organisation des feuilles\n",
"\n",
"La règle de base est que l’ensemble de vos feuilles soit **réutilisable**, c’est-à-dire qu’une personne peut reprendre un projet et relancer l’exécution de chacune (et éventuellement aboutir au même résultat, mais le sujet de reproductibilité des expérimentations n’est pas couvert dans ce projet).\n",
"\n",
"Afin d'aider sur l'ordre d'exécution de vos feuilles, nommer les en commençant avec un numéro pour savoir dans quelle ordre elles doivent êtres exécutées, par exemple 01-csv-generation, 02-data-typing, etc.\n",
"\n",
"### Organisation d'une feuille\n",
"\n",
"Vos feuilles doivent êtres structurés comment suit\n",
"1. La première cellule doit être en markdown, avec un titre et un ou plusieurs paragraphes décrivant le but de la feuille.\n",
"2. La seconde cellule doit contenir les constantes qu’une personne utilisant votre feuille peut-être intéressée de modifier (chemin vers des datasets par exemple)."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"INPUT_PATH_TO_XML_FILES=\"/home/kartoch/Documents/datasets/gas/\"\n",
"OUTPUT_PATH_TO_CSV_FILES=\"/tmp/gas\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"3. La troisième cellule doit contenir l’ensemble des imports de la feuille suivi éventuellement de l’initialisation de certain composants (par exemple définition de la taille des graphiques générés par matplotlib ou seaborn)."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"import csv\n",
"import io\n",
"import xml.sax\n",
"import zipfile"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Structuration du code\n",
"\n",
"Quelques remarques sur comment structurer votre code\n",
"* Ne pas avoir plusieurs lignes par cellule (les résultats des lignes intermediaires ne sont pas affichés)\n",
"* Éviter dans la mesure du possible les commentaires dans votre code python, préférer une cellule markdown au dessus de votre code décrivant le comportement et/ou l'algorithme impliqué dans la cellule. Vous pouvez aussi ajouter de l'information pour un méthode sous forme de docstring"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def method_qui_fait_quelque_chose():\n",
" \"\"\"Cette méthode fait quelque chose\"\"\"\n",
" pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Utiliser des *backslash* pour éviter que votre code déborde sur plusieurs lignes"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['bonjour', 'les', 'amis']"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"bonjour les amis\"\\\n",
" .split(\" \")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Les chaînes de caractères peuvent être répartis sur plusieurs lignes"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"a = \"abc\" +\\\n",
" \"def\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* les paramètres peuvent êtres répartis sur plusieurs lignes"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'BLABLA'"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"blabla\".replace(\"bla\",\n",
" \"BLA\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# **Première feuille:** Conversion des XML en CSV\n",
"\n",
"La première feuille doit lire l'ensemble des XML représentant les données étudiés de manière hierarchique et sauvegarder ces données avec le format CSV (le format le plus commun pour des *datasets*). Il est possible de décompresser les fichiers zip à la volée ou de le faire manuellement avant l'exécution de la feuille.\n",
"\n",
"### Lecture des fichiers zip\n",
"\n",
"Soit un zip accédé par l'object fichier retourné par `io.BytesIo` (un module simulant les accès à un fichier binaire representé par une chaîne de caractère), voici comment ouvrir ce fichier zip et lire le contenu du fichier à l'intérieur:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"inside_filename: coucou.txt\n",
"inside_data: b'coucou\\n'\n"
]
}
],
"source": [
"f = io.BytesIO(b'\\x50\\x4b\\x03\\x04\\x0a\\x00\\x00\\x00\\x00\\x00\\x8f\\x61\\x84\\x4e\\x6b\\xeb\\x27\\x37\\x07\\x00'\n",
" b'\\x00\\x00\\x07\\x00\\x00\\x00\\x0a\\x00\\x1c\\x00\\x63\\x6f\\x75\\x63\\x6f\\x75\\x2e\\x74\\x78\\x74'\n",
" b'\\x55\\x54\\x09\\x00\\x03\\x8d\\xd8\\xa5\\x5c\\x92\\xd8\\xa5\\x5c\\x75\\x78\\x0b\\x00\\x01\\x04\\xe9'\n",
" b'\\x03\\x00\\x00\\x04\\x64\\x00\\x00\\x00\\x63\\x6f\\x75\\x63\\x6f\\x75\\x0a\\x50\\x4b\\x01\\x02\\x1e'\n",
" b'\\x03\\x0a\\x00\\x00\\x00\\x00\\x00\\x8f\\x61\\x84\\x4e\\x6b\\xeb\\x27\\x37\\x07\\x00\\x00\\x00\\x07'\n",
" b'\\x00\\x00\\x00\\x0a\\x00\\x18\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\xa4\\x81\\x00\\x00\\x00'\n",
" b'\\x00\\x63\\x6f\\x75\\x63\\x6f\\x75\\x2e\\x74\\x78\\x74\\x55\\x54\\x05\\x00\\x03\\x8d\\xd8\\xa5\\x5c'\n",
" b'\\x75\\x78\\x0b\\x00\\x01\\x04\\xe9\\x03\\x00\\x00\\x04\\x64\\x00\\x00\\x00\\x50\\x4b\\x05\\x06\\x00'\n",
" b'\\x00\\x00\\x00\\x01\\x00\\x01\\x00\\x50\\x00\\x00\\x00\\x4b\\x00\\x00\\x00\\x00\\x00')\n",
"\n",
"zip_ref = zipfile.ZipFile(f, 'r')\n",
"[inside_filename] = zip_ref.namelist()\n",
"print(\"inside_filename: \" + inside_filename)\n",
"inside_data = zip_ref.open(inside_filename).readline()\n",
"print(\"inside_data: \" + str(inside_data))\n",
"zip_ref.close()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Lecture des fichiers XML\n",
"\n",
"Il existe plusieurs méthodes pour lire un xml:\n",
"1. xml.etre.ElementTrree: mécanisme simple représentant l'intégralité de la structure XML en mémoire\n",
"2. xml.sax: une implémentation de SAX2 permettant de définir les actions à prendre lors du traîtement d'un fichier XML\n",
"3. lxml: non couvert dans ce cours\n",
"\n",
"Dans le cadre de ce projet, le premier n'est pas utilisable au vu de la taille des données chargées. L'approche SAX2 est décrite ci-dessous.\n",
"\n",
"Le principe de SAX2 est de lire le fichier tout en appelant à chaque entité une fonction fournie par le programmeur.\n",
"\n",
"Soit par exemple le fichier suivant (émulé grace au module `io.StringIO` simulant l'accès à un fichier texte): "
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"my_xml = io.StringIO('<abc id=\"toto\">'\n",
" '<cool fih=\"123\" koka=\"kola\">trop</cool>'\n",
" '<def>méthodes</def>'\n",
" '</abc>')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Le programmeur implémente les méthodes nécessaires dans la classe suivante:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"class StreamHandler(xml.sax.handler.ContentHandler):\n",
"\n",
" def startElement(self, name, attrs):\n",
" \"\"\"Appelé à chaque tag ouvrant, par exemple <coucou>. Les attributs du tag sont \n",
" disponibles sous forme de dictionnaire.\n",
" \"\"\"\n",
" print(\"startElement \" + name + \" attrs:\" + str(dict(attrs.items())))\n",
"\n",
" def characters(self, content):\n",
" \"\"\"Appelé à chaque texte entre les tags, par exemple pour \n",
" <coucou>ICI</coucou>AUSSI<tralala/> la méthode sera appelé \n",
" pour ICI puis pour AUSSI\n",
" \"\"\"\n",
" print(\"characters \" + content)\n",
" \n",
" def endElement(self, name):\n",
" \"\"\"Appelé à chaque tag fermant, par exemple </coucou>\"\"\"\n",
" print(\"endElement \" + name)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"startElement abc attrs:{'id': 'toto'}\n",
"startElement cool attrs:{'fih': '123', 'koka': 'kola'}\n",
"characters trop\n",
"endElement cool\n",
"startElement def attrs:{}\n",
"characters méthodes\n",
"endElement def\n",
"endElement abc\n"
]
}
],
"source": [
"parser = xml.sax.make_parser()\n",
"handler = StreamHandler()\n",
"parser.setContentHandler(handler)\n",
"parser.parse(my_xml)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Attention:** selon l'encodage des caractères, la méthode `characters` peut-être appelé plusieurs fois sur une seule chaîne."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Écriture des CSV\n",
"\n",
"Voici un exemple d'utilisation du module CSV disponible dans la librarie standard de Python."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a,b\n",
"blabla,tagada\n",
"\n"
]
}
],
"source": [
"csv_file = io.StringIO()\n",
"csv_writer = csv.DictWriter(csv_file, fieldnames=['a','b'])\n",
"csv_writer.writeheader()\n",
"csv_writer.writerow({'a': \"blabla\", 'b': \"tagada\"})\n",
"print(csv_file.getvalue())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# **Seconde feuille:** Typage des données\n",
"\n",
"La seconde feuille commence par ouvrir les fichiers CSV avec pandas. Le sujet de pandas ayant été couvert auparavant, nous ne nous interesserons qu'au problème de conversion des données temporelles."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Découverte graphique des données\n",
"\n",
"## Rendu graphique\n",
"\n",
"Il existe plusieurs bibliothèque pour l'exploration des données. Nous utiliserons ici seaborn et pyplot.\n",
"\n",
"**TODO**\n",
"\n",
"## Questions à répondre\n",
"* Première exploration de la tendance des prix: Moyenne des prix par rapport au temps pour chaque carburant (nécessité d'utiliser [le mécansisme de sliding window de pandas](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html) pour affiner les courbes)\n",
"* Nombre de MAJ par mois (voir l'usage de [Grouper](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Grouper.html) comme option de groupBy)\n",
"* Fréquence des mise-à-jour par proportion de stations \n",
"* Proportion de chaque services (avec un barplot)\n",
"* Est ce que les informations sur les PDV évoluent chaque année ?\n",
"* Variation du nombre de PDV // année\n",
"* Scatterplot du prix en fonction du temps pour chaque carburant"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment