Utiliser des notebooks Jupyter Lab en contexte Django

22 mai 2023 14:49 dans data-pipelines / publications

Python dispose des meilleurs outils de data science. Nous allons utiliser ces outils dans le contexte d'un projet Django pour manipuler et analyser des données. Installation du stack Détail des librairies utilisées: Jupyter lab pour les notebooks Django extensions pour faciliter le lancement des notebooks Polars pour les dataframes et…

Slider Image

Python dispose des meilleurs outils de data science. Nous allons utiliser ces outils dans le contexte d'un projet Django pour manipuler et analyser des données.

Installation du stack

Détail des librairies utilisées:

Le code des exemples mentionnés dans cet article est disponible sur le repository django-datastack

Installons les dépendances nécessaires:


              pip install django-extensions jupyterlab dataspace
            

Ajouter "django_extensions" dans le setting INSTALLED_APPS

Configuration des notebooks

Créons un dossier notebooks dans un projet Django et ajoutons y un fichier d'initialisation:


            mkdir notebooks
            cd notebooks
            touch __init__.py
          

Le nouveau fichier __init__.py contient des éléments de configuration pour lancer le notebook en contexte Django:


            import sys
            from pathlib import Path
            import os

            os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
            file = Path(__file__).resolve()
            parent, root = file.parent, file.parents[1]
            sys.path.append(str(root))
          

Lancement

Lançons les notebooks:


            python manage.py shell_plus --lab
          

Une fenêtre va s'ouvrir automatiquement dans le navigateur. Créons un premier notebook en séléctionnant "Notebooks / Django Shell Plus" et vérifions que nous pouvons effectuer des queries orm:


            %run __init__
            from django.contrib.auth.models import User

            User.objects.all()
          

La première ligne est requise pour initialiser l'environnement à partir du script que nous avons mis en place précédemment.

Insérer des données depuis des fichiers csv

Maintenant que notre environnement notebook est correctement configuré nous allons en tirer profit pour injecter des données dans la base Django à partir de fichiers csv

Charger des données

Nous utiliserons un dataset de market data historique détaillant les échanges Bitcoin/USDT sur différents exchanges. Créons une app Trades contenant un modèle Trade simple:


            from django.db import models

            SIDE = [("buy", "buy"), ("sell", "sell")]


            class Trade(models.Model):
                date = models.DateTimeField()
                price = models.FloatField()
                quantity = models.FloatField()
                market = models.CharField(max_length=255)
                side = models.CharField(max_length=4, choices=SIDE)

                def __str__(self) -> str:
                    return f"${self.market} ${self.date}"
          

Créons un notebook et chargeons un fichier csv:


            %run __init__
            import dataspace
            from trades.models import Trade

            ds = dataspace.from_csv("data/BTC-USDT-1min.csv")
            ds.show()
          

Note: nous utilisons ici une fonction de la librairie Dataspace. Il est également possible d'utiliser d'autres librairies plus classiques pour charger le csv : Pandas ou Polars notamment.

Insertion simple

Les données étant peu volumineuses (5400 datapoints environ) nous allons les injecter en db avec des queries insert simples en itérant sur la dataframe:


            for row in ds.df.rows(named=True):
                Trade.objects.create(
                  date=row["mktTS"],
                  price=row["px"],
                  quantity=row["qty"],
                  side=row["side"].lower(),
                  market=row["Source"]
            )
            print("ok")
          

Vérifions maintenant que les données ont bien été insérées avec un query select:


            qs = Trade.objects.all()
            ds = dataspace.from_django(qs)
            ds.show()
          

Batch insert

Injectons maintenant une quantité de données plus importante et voyons comment utiliser le batch insert. Nous allons charger deux fichiers csv et les fusionner dans une dataframe avant de lancer les queries d'insertion:


            %run __init__
            import polars as pl
            import dataspace
            from trades.models import Trade

            ds = dataspace.from_csv("data/bitcoin1.csv")
            ds2 = dataspace.from_csv("data/bitcoin2.csv")
            ds.df = pl.concat([ds.df, ds2.df], rechunk=True)
            ds.show()
          

Nous allons injecter les données en base par tranche en utilisant bulk_create:


            batch_size = 999
            objs = []
            i = 0
            # iterate on the data
            for row in ds.df.rows(named=True):
                t = Trade(date=row["mktTS"],price=row["px"],quantity=row["qty"],side=row["side"].lower(),market=row["Source"])
                objs.append(t)
                i+=1
                if (i==batch_size):
                    # print("saving batch")
                    Trade.objects.bulk_create(objs)
                    objs = []
                    i = 0
            # save the remaining objects
            if (len(objs)>0):
                Trade.objects.bulk_create(objs)
                
            print("Ok, got", Trade.objects.all().count(), "Trade objects in database")
          

Pour plus d'efficacité et de confort il est possible d'ajouter une progress bar: voir le notebook exemple.

Workflow git

Pour une organisation optimale il est conseillé de ne pas partager les fichiers notebooks .ipynb dans un repository: ils sont trop volumineux. La solution consiste à utiliser Jupytext pour créer des fichiers .py mirroir et les partager.


            pip install jupytext
          

Pour pairer un notebook: dans le menu "View" séléctionner "Activate Command Palette", et séléctionner "Pair Notebook with percent Script":

Ceci va créer un fichier .py du même nom que le notebook en mirroir. Ajouter *.ipynb dans le .gitignore et committer uniquement les fichiers python.

Pour ouvrir un fichier python en temps que notebook faire un clic droit dessus et sélectionner "Open with" > "Notebook".

Et ensuite ?

Maintenant la donnée en base il est facile de lancer des queries Django classiques et charger des dataframes. Nous verrons dans un prochain article comment utiliser un stack de data analytics pour prototyper un dashboard avec des charts.

Exemples et notebooks:

Repository: django-datastack

Notebooks exemple: