Les nouveaux modes d’accès aux données : le format Parquet et les données sur le cloud

Tutoriel
Avancé
Auteur·rice

Lino Galiana

Date de publication

2024-12-07

Résumé

Dans les entreprises et administrations, un nombre croissant d’infrastructure se basent sur des clouds, qui sont des sessions non persistentes où les données ne sont pas stockées dans les mêmes serveurs que les machines qui exécutent du code. L’une des technologies dominantes dans le domaine est un système de stockage nommé S3, développé par Amazon.

Python, à travers plusieurs packages (notamment boto3, s3fs ou pyarrow), permet d’utiliser ce système de stockage distant comme si on accédait à des fichiers depuis son poste personnel. Cette révolution est étroitement associée à l’émergence du format de données Apache Parquet, format utilisable en Python par le biais du package pyarrow ou avec Spark et présentant de nombreux avantages pour l’analyse de données (vitesse d’import, possibilité de traiter des données plus volumineuses que la RAM…)

Pour essayer les exemples présents dans ce tutoriel :
View on GitHub Onyxia Onyxia Open In Colab

Ce chapitre est une introduction à la question du stockage des données et aux innovations récentes dans ce domaine. L’objectif est d’abord de présenter les avantages du format Parquet et la manière dont on peut utiliser les librairies pyarrow ou duckdb pour traiter de manière efficace des données volumineuses au format Parquet. Ensuite, on présentera la manière dont ce format parquet s’intègre bien avec des systèmes de stockage cloud, qui tendent à devenir la norme dans le monde de la data science.

1 Elements de contexte

1.1 Principe du stockage de la donnée

Pour comprendre les apports du format Parquet, il est nécessaire de faire un détour pour comprendre la manière dont une information est stockée et accessible à un langage de traitement de la donnée.

Il existe deux approches dans le monde du stockage de la donnée. La première est celle de la base de données relationnelle. La seconde est le principe du fichier. La différence entre les deux est dans la manière dont l’accès aux données est organisé.

1.2 Les fichiers

Dans un fichier, les données sont organisées selon un certain format et le logiciel de traitement de la donnée va aller chercher et structurer l’information en fonction de ce format. Par exemple, dans un fichier .csv, les différentes informations seront stockées au même niveau avec un caractère pour les séparer (la virgule , dans les .csv anglosaxons, le point virgule dans les .csv français, la tabulation dans les .tsv). Le fichier suivant

nom ; profession 
Astérix ; 
Obélix ; Tailleur de menhir ;
Assurancetourix ; Barde

sera ainsi organisé naturellement sous forme tabulée par Python

nom profession
0 Astérix
1 Obélix Tailleur de menhir
2 Assurancetourix Barde

A propos des fichiers de ce type, on parle de fichiers plats car les enregistrements relatifs à une observation sont stockés ensemble, sans hiérarchie.

Certains formats de données vont permettre d’organiser les informations de manière différente. Par exemple, le format JSON va hiérarchiser différemment la même information [^1]:

[
  {
    "nom": "Astérix"
  },
  {
    "nom": "Obélix",
    "profession": "Tailleur de menhir"
  },
  {
    "nom": "Assurancetourix",
    "profession": "Barde"
  }
]
Caution

La différence entre le CSV et le format JSON va au-delà d’un simple “formattage” des données.

Par sa nature non tabulaire, le format JSON permet des mises à jour beaucoup plus facile de la donnée dans les entrepôts de données.

Par exemple, un site web qui collecte de nouvelles données n’aura pas à mettre à jour l’ensemble de ses enregistrements antérieurs pour stocker la nouvelle donnée (par exemple pour indiquer que pour tel ou tel client cette donnée n’a pas été collectée) mais pourra la stocker dans un nouvel item. Ce sera à l’outil de requête (Python ou un autre outil) de créer une relation entre les enregistrements stockés à des endroits différents.

Ce type d’approche flexible est l’un des fondements de l’approche NoSQL, sur laquelle nous allons revenir, qui a permis l’émergence de technologies au coeur de l’écosystème actuel du big-data comme Hadoop ou ElasticSearch.

Cette fois, quand on n’a pas d’information, on ne se retrouve pas avec nos deux séparateurs accolés (cf. la ligne “Astérix”) mais l’information n’est tout simplement pas collectée.

Note

Il se peut très bien que l’information sur une observation soit disséminée dans plusieurs fichiers dont les formats diffèrent.

Par exemple, dans le domaine des données géographiques, lorsqu’une donnée est disponible sous format de fichier(s), elle peut l’être de deux manières!

  • Soit la donnée est stockée dans un seul fichier qui mélange contours géographiques et valeurs attributaires (la valeur associée à cette observation géographique, par exemple le taux d’abstention). Ce principe est celui du geojson.
  • Soit la donnée est stockée dans plusieurs fichiers qui sont spécialisés : un fichier va stocker les contours géographiques, l’autre les données attributaires et d’autres fichiers des informations annexes (comme le système de projection). Ce principe est celui du shapefile. C’est alors le logiciel qui requête les données (Python par exemple) qui saura où aller chercher l’information dans les différents fichiers et associer celle-ci de manière cohérente.

Un concept supplémentaire dans le monde du fichier est celui du file system. Le file system est le système de localisation et de nommage des fichiers. Pour simplifier, le file system est la manière dont votre ordinateur saura retrouver, dans son système de stockage, les bits présents dans tel ou tel fichier appartenant à tel ou tel dossier.

1.3 Les bases de données

La logique des bases de données est différente. Elle est plus systémique. Un système de gestion de base de données (Database Management System) est un logiciel qui gère à la fois le stockage d’un ensemble de données reliée, permet de mettre à jour celle-ci (ajout ou suppression d’informations, modification des caractéristiques d’une table…) et qui gère également les modalités d’accès à la donnée (type de requête, utilisateurs ayant les droits en lecture ou en écriture…).

La relation entre les entités présentes dans une base de données prend généralement la forme d’un schéma en étoile. Une base va centraliser les informations disponibles qui seront ensuite détaillées dans des tables dédiées.

Source: La documentation Databricks sur le schéma en étoile

Le logiciel associé à la base de données fera ensuite le lien entre ces tables à partir de requêtes SQL. L’un des logiciels les plus efficaces dans ce domaine est PostgreSQL. Python est tout à fait utilisable pour passer une requête SQL à un gestionnaire de base de données. Les packages sqlalchemy et psycopg2 peuvent servir à utiliser PostgreSQL pour requêter une base de donnée ou la mettre à jour.

La logique de la base de données est donc très différente de celle du fichier. Ces derniers sont beaucoup plus légers pour plusieurs raisons. D’abord, parce qu’ils sont moins adhérents à un logiciel gestionnaire. Là où le fichier ne nécessite, pour la gestion, qu’un file system, installé par défaut sur tout système d’exploitation, une base de données va nécessiter un logiciel spécialisé. L’inconvénient de l’approche fichier, sous sa forme standard, est qu’elle ne permet pas une gestion fine des droits d’accès et amène généralement à une duplication de la donnée pour éviter que la source initiale soit ré-écrite (involontairement ou de manière intentionnelle par un utilisateur malveillant). Résoudre ce problème est l’une des innovations des systèmes cloud, sur lesquelles nous reviendrons en évoquant le système S3. Un deuxième inconvénient de l’approche base de données par rapport à l’approche fichier, pour un utilisateur de Python, est que les premiers nécessitent l’intermédiation du logiciel de gestion de base de données là où, dans le second cas, on va se contenter d’une librairie, donc un système beaucoup plus léger, qui sait comment transformer la donnée brute en DataFrame. Pour ces raisons, entre autres, les bases de données sont donc moins à la mode dans l’écosystème récent de la data science que les fichiers.

2 Le format Parquet

Le format CSV a rencontré un grand succès par sa simplicité : il est lisible par un humain (un bloc-note suffit pour l’ouvrir et apercevoir les premières lignes), sa nature plate lui permet de bien correspondre au concept de données tabulées sans hiérarchie qui peuvent être rapidement valorisées, il est universel (il n’est pas adhérent à un logiciel). Cependant, le CSV présente plusieurs inconvénients qui justifient l’émergence d’un format concurrent :

  • le CSV est un format lourd car les informations ne sont pas compressées (ce qui le rend lisible facilement depuis un bloc-note) mais aussi parce que toutes les données sont stockées de la même manière. C’est la librairie faisant l’import qui va essayer d’optimiser le typage des données pour trouver le typage qui utilise le moins de mémoire possible sans altération de l’information. En effet, si pandas détermine qu’une colonne présente les valeurs 6 ; 5 ; 0, il va privilégier l’utilisation du type int au type double qui sera lui même préféré au type object (objets de type données textuelles). Cependant, pour faire cela, pandas va devoir scanner un nombre suffisant de valeurs, ce qui demande du temps et expose à des erreurs (en se fondant sur trop peu de valeurs, on peut se tromper de typage) ;
  • le stockage étant orienté ligne, accéder à une information donnée dans un CSV implique de le lire le fichier en entier, sélectionner la ou les colonnes d’intérêt et ensuite les lignes désirées. Par exemple, si on désire connaître uniquement la profession de la deuxième ligne dans l’exemple plus haut :point_up:, un algorithme de recherche devra: prendre le fichier, déterminer que la profession est la deuxième colonne, et ensuite aller chercher la deuxième ligne dans cette colonne. Si on désire accéder à un sous-ensemble de lignes dont les indices sont connus, le CSV est intéressant. Cependant, si on désire accéder à un sous-ensemble de colonnes dans un fichier (ce qui est un cas d’usage plus fréquent pour les data scientists), alors le CSV n’est pas le format le plus approprié ;
  • mettre à jour la donnée est coûteux car cela implique de réécrire l’ensemble du fichier. Par exemple, si après une première analyse de la donnée, on désire ajouter une colonne, on ne peut accoler ces nouvelles informations à celles déjà existantes, il est nécessaire de réécrire l’ensemble du fichier. Pour reprendre l’exemple de nos gaulois préférés, si on veut ajouter une colonne cheveux entre les deux déjà existantes, il faudra changer totalement le fichier:
"""
nom ; cheveux ; profession
Astérix; blond; ; 
Obélix; roux; Tailleur de menhir
Assurancetourix; blond; Barde
"""
Note

La plupart des logiciels d’analyse de données proposent un format de fichier pour sauvegarder des bases de données. On peut citer le .pickle (Python), le .rda ou .RData (R), le .dta (Stata) ou le .sas7bdat (SAS). L’utilisation de ces formats est problématique car cela revient à se lier les mains pour l’analyse ultérieure des données, surtout lorsqu’il s’agit d’un format propriétaire (comme avec SAS ou Stata).

Par exemple, Python ne sait pas nativement lire un .sas7bdat. Il existe des librairies pour le faire (notamment Pandas) mais le format étant propriétaire, les développeurs de la librairie ont dû tâtonner et on n’est ainsi jamais assuré qu’il n’y ait pas d’altération de la donnée.

Malgré tous les inconvénients du .csv listés plus haut, il présente l’immense avantage, par rapport à ces formats, de l’universalité. Il vaut ainsi mieux privilégier un .csv à ces formats pour le stockage de la donnée. Ceci dit, comme vise à le montrer ce chapitre, il vaut mieux privilégier le format parquet au CSV.

Pour répondre à ces limites du CSV, le format parquet, qui est un projet open-source Apache, a émergé. La première différence entre le format parquet et le CSV est que le premier repose sur un stockage orienté colonne là où le second est orienté ligne. Pour comprendre la différence, voici un exemple issu du blog d’upsolver:

Dans notre exemple précédent, cela donnera une information prenant la forme suivante (ignorez l’élément pyarrow.Table, nous reviendrons dessus) :

pyarrow.Table
nom : string
profession: string
----
nom : [["Astérix ","Obélix ","Assurancetourix "]]
profession: [["","Tailleur de menhir","Barde"]]

Pour reprendre l’exemple fil rouge :point_up:, il sera ainsi beaucoup plus facile de récupérer la deuxième ligne de la colonne profession: on ne considère que le vecteur profession et on récupère la deuxième valeur. Le requêtage d’échantillon de données ne nécessite donc pas l’import de l’ensemble des données. A cela s’ajoute des fonctionnalités supplémentaires des librairies d’import de données parquet (par exemple pyarrow ou spark) qui vont faciliter des recherches complexes basées, par exemple, sur des requêtes de type SQL, ou permettant l’utilisation de données plus volumineuses que la RAM.

Le format parquet présente d’autres avantages par rapport au CSV:

  • Le format parquet est (très) compressé, ce qui réduit la volumétrie des données sur disque ;
  • Des métadonnées, notamment le typage des variables, sont stockées en complément dans le fichier. Cette partie, nommée le footer du fichier parquet, permet que l’import des données soit optimisé sans risque d’altération de celle-ci. Pour un producteur de données, c’est une manière d’assurer la qualité des données. Par exemple, un fournisseur de données de type code-barre sera certain que les données 000012 ne seront pas considérées identiques à un code-barre 12.
  • Il est possible de partitionner un jeu de données en fonction de différents niveaux (par exemple des niveaux géographiques) en une arborescence de fichiers parquet. Cela permet de travailler sur un échantillon pour facilement passer à l’échelle ensuite. Par exemple, une structure partitionnée, empruntée à la documentation Spark peut prendre la forme suivante :
path
└── to
    └── table
        ├── gender=male
        │   ├── ...
        │   │
        │   ├── country=US
        │   │   └── data.parquet
        │   ├── country=CN
        │   │   └── data.parquet
        │   └── ...
        └── gender=female
            ├── ...
            │
            ├── country=US
            │   └── data.parquet
            ├── country=CN
            │   └── data.parquet
            └── ...

Qu’on lise un ou plusieurs fichiers, on finira avec le schéma suivant :

root
|-- name: string (nullable = true)
|-- age: long (nullable = true)
|-- gender: string (nullable = true)
|-- country: string (nullable = true)

Ces différents avantages expliquent le succès du format parquet dans le monde du big-data. Le paragraphe suivant, extrait du post d’upsolver déjà cité, résume bien l’intérêt:

Complex data such as logs and event streams would need to be represented as a table with hundreds or thousands of columns, and many millions of rows. Storing this table in a row based format such as CSV would mean:

  • Queries will take longer to run since more data needs to be scanned, rather than only querying the subset of columns we need to answer a query (which typically requires aggregating based on dimension or category)
  • Storage will be more costly since CSVs are not compressed as efficiently as Parquet

Cependant, Parquet ne devrait pas intéresser que les producteurs ou utilisateurs de données big-data. C’est l’ensemble des producteurs de données qui bénéficient des fonctionalités de Parquet.

Pour en savoir plus sur Arrow, des éléments supplémentaires sur Parquet sont disponibles sur ce très bon post de blog d’upsolver et sur la page officielle du projet Parquet.

2.1 Lire un parquet en Python: la librairie pyarrow

La librairie pyarrow permet la lecture et l’écriture de fichiers parquet avec Python1. Elle repose sur un type particulier de dataframe, le pyarrow.Table qui peut être utilisé en substitut ou en complément du DataFrame de pandas. Il est recommandé de régulièrement consulter la documentation officielle de pyarrow concernant la lecture et écriture de fichiers et celle relative aux manipulations de données.

Pour illustrer les fonctionalités de pyarrow, repartons de notre CSV initial que nous allons enrichir d’une nouvelle variable numérique et que nous allons convertir en objet pyarrow avant de l’écrire au format parquet:

import pandas as pd
from io import StringIO
import pyarrow as pa
import pyarrow.parquet as pq

s = """
nom;cheveux;profession
Astérix;blond;
Obélix;roux;Tailleur de menhir
Assurancetourix;blond;Barde
"""

source = StringIO(s)

df = pd.read_csv(source, sep=";", index_col=False)
df["taille"] = [155, 190, 175]
table = pa.Table.from_pandas(df)

table

pq.write_table(table, "example.parquet")
Tip

L’utilisation des noms pa pour pyarrow et pq pour pyarrow.parquet est une convention communautaire qu’il est recommandé de suivre.

Pour importer et traiter ces données, on peut conserver les données sous le format pyarrow.Table ou transformer en pandas.DataFrame. La deuxième option est plus lente mais présente l’avantage de permettre ensuite d’appliquer toutes les manipulations offertes par l’écosystème pandas qui est généralement mieux connu que celui d’Arrow.

Supposons qu’on ne s’intéresse qu’à la taille et à la couleur de cheveux de nos gaulois. Il n’est pas nécessaire d’importer l’ensemble de la base, cela ferait perdre du temps pour rien. On appelle cette approche le column pruning qui consiste à ne parcourir, dans le fichier, que les colonnes qui nous intéressent. Du fait du stockage orienté colonne du parquet, il suffit de ne considérer que les blocs qui nous intéressent (alors qu’avec un CSV il faudrait scanner tout le fichier avant de pouvoir éliminer certaines colonnes). Ce principe du column pruning se matérialise avec l’argument columns dans parquet.

Ensuite, avec pyarrow, on pourra utiliser pyarrow.compute pour effectuer des opérations directement sur une table Arrow :

import pyarrow.compute as pc

table = pq.read_table("example.parquet", columns=["taille", "cheveux"])

(table.group_by("cheveux").aggregate([("taille", "mean")]))

La manière équivalente de procéder en passant par l’intermédiaire de pandas est

table = pq.read_table("example.parquet", columns=["taille", "cheveux"])

(table.to_pandas().groupby("cheveux")["taille"].mean())
cheveux
blond    165.0
roux     190.0
Name: taille, dtype: float64

Ici, comme les données sont peu volumineuses, deux des avantages du parquet par rapport au CSV (données moins volumineuses et vitesse de l’import) ne s’appliquent pas vraiment.

Note

Un autre principe d’optimisation de la performance qui est au coeur de la librairie Arrow est le filter pushdown (ou predicate pushdown).

Quand on exécute un filtre de sélection de ligne juste après avoir chargé un jeu de données, Arrow va essayer de le mettre en oeuvre lors de l’étape de lecture et non après. Autrement dit, Arrow va modifier le plan d’exécution pour pousser le filtre en amont de la séquence d’exécution afin de ne pas essayer de lire les lignes inutiles.

3 Les données sur le cloud

Si les fichiers parquet sont une solution avantageuse pour les data scientists, ils ne résolvent pas tous les inconvénients de l’approche fichier. En particulier, la question de la duplication des données pour la mise à disposition sécurisée des sources n’est pas résolue. Pour que l’utilisateur B n’altère pas les données de l’utilisateur A, il est nécessaire qu’ils travaillent sur deux fichiers différents, dont l’un peut être une copie de l’autre.

La mise à disposition de données dans les systèmes de stockage cloud est une réponse à ce problème. Les data lake qui se sont développés dans les institutions et entreprises utilisatrices de données

Le principe d’un stockage cloud est le même que celui d’une Dropbox ou d’un Drive mais adapté à l’analyse de données. Un utilisateur de données accède à un fichier stocké sur un serveur distant comme s’il était dans son file system local2. Donc, du point de vue de l’utilisateur Python, il n’y a pas de différence fondamentale. Cependant, les données ne sont pas hebergées dans un dossier local (par exemple Mes Documents/monsuperfichier) mais sur un serveur distant auquel l’utilisateur de Python accède à travers un échange réseau.

Dans l’univers du cloud, la hiérarchisation des données dans des dossiers et des fichiers bien rangés est d’ailleurs moins importante que dans le monde du file system local. Lorsque vous essayez de retrouver un fichier dans votre arborescence de fichiers, vous utilisez parfois la barre de recherche de votre explorateur de fichiers, avec des résultats mitigés3. Dans le monde du cloud, les fichiers sont parfois accumulés de manière plus chaotique car les outils de recherche sont plus efficaces4.

En ce qui concerne la sécurité des données, la gestion des droits de lecture et écriture peut être fine: on peut autoriser certains utilisateurs uniquement à la lecture, d’autres peuvent avoir les droits d’écriture pour modifier les données. Cela permet de concilier les avantages des bases de données (la sécurisation des données) avec ceux des fichiers.

3.1 Qu’est-ce que le système de stockage S3 ?

Dans les entreprises et administrations, un nombre croissant de données sont disponibles depuis un système de stockage nommé S3. Le système S3 (Simple Storage System) est un système de stockage développé par Amazon et qui est maintenant devenu une référence pour le stockage en ligne. Il s’agit d’une architecture à la fois sécurisée (données cryptées, accès restreints) et performante.

Le concept central du système S3 est le bucket. Un bucket est un espace (privé ou partagé) où on peut stocker une arborescence de fichiers. Pour accéder aux fichiers figurant dans un bucket privé, il faut des jetons d’accès (l’équivalent d’un mot de passe) reconnus par le serveur de stockage. On peut alors lire et écrire dans le bucket.

Note

Les exemples suivants seront réplicables pour les utilisateurs de la plateforme SSP Cloud

Pour essayer les exemples présents dans ce tutoriel :
View on GitHub Onyxia Onyxia Open In Colab

Ils peuvent également l’être pour des utilisateurs ayant un accès à AWS, il suffit de changer l’URL du endpoint présenté ci-dessous.

3.2 Comment faire avec Python ?

3.2.1 Les librairies principales

L’interaction entre ce système distant de fichiers et une session locale de Python est possible grâce à des API. Les deux principales librairies sont les suivantes :

  • boto3, une librairie créée et maintenue par Amazon ;
  • s3fs, une librairie qui permet d’interagir avec les fichiers stockés à l’instar d’un filesystem classique.

La librairie pyarrow que nous avons déjà présentée permet également de traiter des données stockées sur le cloud comme si elles étaient sur le serveur local. C’est extrêmement pratique et permet de fiabiliser la lecture ou l’écriture de fichiers dans une architecture cloud. Un exemple, assez court, est disponible dans la documentation officielle

Il existe également d’autres librairies permettant de gérer des pipelines de données (chapitre à venir) de manière quasi indifférente entre une architecture locale et une architecture cloud. Parmi celles-ci, nous présenterons quelques exemples avec snakemake. En arrière-plan, snakemake va utiliser boto3 pour communiquer avec le système de stockage.

Enfin, selon le même principe du comme si les données étaient en local, il existe l’outil en ligne de commande mc (Minio Client) qui permet de gérer par des lignes de commande Linux les dépôts distants comme s’ils étaient locaux.

Toutes ces librairies offrent la possibilité de se connecter depuis Python, à un dépôt de fichiers distant, de lister les fichiers disponibles dans un bucket, d’en télécharger un ou plusieurs ou de faire de l’upload Nous allons présenter quelques-unes des opérations les plus fréquentes, en mode cheatsheet.

3.2.2 Connexion à un bucket

Par la suite, on va utiliser des alias pour les trois valeurs suivantes, qui servent à s’authentifier.

key_id = "MY_KEY_ID"
access_key = "MY_ACCESS_KEY"
token = "MY_TOKEN"

Ces valeurs peuvent être également disponibles dans les variables d’environnement de Python. Comme il s’agit d’une information d’authentification personnelle, il ne faut pas stocker les vraies valeurs de ces variables dans un projet, sous peine de partager des traits d’identité sans le vouloir lors d’un partage de code.

boto3 👇

Avec boto3, on créé d’abord un client puis on exécute des requêtes dessus. Pour initialiser un client, il suffit, en supposant que l’url du dépôt S3 est "https://minio.lab.sspcloud.fr", de faire:

import boto3

s3 = boto3.client("s3", endpoint_url="https://minio.lab.sspcloud.fr")
S3FS 👇

La logique est identique avec s3fs.

Si on a des jetons d’accès à jour et dans les variables d’environnement adéquates:

import s3fs

fs = s3fs.S3FileSystem(client_kwargs={"endpoint_url": "https://minio.lab.sspcloud.fr"})
Arrow 👇

La logique d’Arrow est proche de celle de s3fs. Seuls les noms d’arguments changent

Si on a des jetons d’accès à jour et dans les variables d’environnement adéquates:

from pyarrow import fs

s3 = fs.S3FileSystem(endpoint_override="http://" + "minio.lab.sspcloud.fr")
Snakemake 👇

La logique de Snakemake est, quant à elle, plus proche de celle de boto3. Seuls les noms d’arguments changent

Si on a des jetons d’accès à jour et dans les variables d’environnement adéquates:

from snakemake.remote.S3 import RemoteProvider as S3RemoteProvider

S3 = S3RemoteProvider(host="https://" + os.getenv("AWS_S3_ENDPOINT"))

Il se peut que la connexion à ce stade soit refusée (HTTP error 403). Cela peut provenir d’une erreur dans l’URL utilisé. Cependant, cela reflète plus généralement des paramètres d’authentification erronés.

boto3 👇

Les paramètres d’authentification sont des arguments supplémentaires:

import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://minio.lab.sspcloud.fr",
    aws_access_key_id=key_id,
    aws_secret_access_key=access_key,
    aws_session_token=token,
)
S3FS 👇

La logique est la même, seuls les noms d’arguments diffèrent

import s3fs

fs = s3fs.S3FileSystem(
    client_kwargs={"endpoint_url": "https://" + "minio.lab.sspcloud.fr"},
    key=key_id,
    secret=access_key,
    token=token,
)
Arrow 👇

Tout est en argument cette fois:

from pyarrow import fs

s3 = fs.S3FileSystem(
    access_key=key_id,
    secret_key=access_key,
    session_token=token,
    endpoint_override="https://" + "minio.lab.sspcloud.fr",
    scheme="https",
)
Snakemake 👇

La logique est la même, seuls les noms d’arguments diffèrent

from snakemake.remote.S3 import RemoteProvider as S3RemoteProvider

S3 = S3RemoteProvider(
    host="https://" + os.getenv("AWS_S3_ENDPOINT"),
    access_key_id=key_id,
    secret_access_key=access_key,
)
Note

Sur le SSP Cloud, les jetons d’accès au stockage S3 sont injectés automatiquement dans les services lors de leur création. Ils sont ensuite valides pour une durée de 7 jours. Si l’icône du service passe du vert au rouge, cela signifie que ces jetons sont périmés, il faut donc sauvegarder son code / ses données et reprendre depuis un nouveau service.

3.2.3 Lister les fichiers

S’il n’y a pas d’erreur à ce stade, c’est que la connexion est bien effective. Pour le vérifier, on peut essayer de faire la liste des fichiers disponibles dans un bucket auquel on désire accéder.

Par exemple, on peut vouloir tester l’accès aux bases FILOSOFI (données de revenu localisées disponibles sur https://www.insee.fr) au sein du bucket donnees-insee.

boto3 👇

Pour cela, la méthode list_objects offre toutes les options nécessaires:

import boto3

s3 = boto3.client("s3", endpoint_url="https://minio.lab.sspcloud.fr")
for key in s3.list_objects(Bucket="donnees-insee", Prefix="diffusion/FILOSOFI")[
    "Contents"
]:
    print(key["Key"])
S3FS 👇

Pour lister les fichiers, c’est la méthode ls (celle-ci ne liste pas par défaut les fichiers de manière récursive comme boto3):

import s3fs

fs = s3fs.S3FileSystem(client_kwargs={"endpoint_url": "https://minio.lab.sspcloud.fr"})
fs.ls("donnees-insee/diffusion")
Arrow 👇
from pyarrow import fs

s3 = fs.S3FileSystem(endpoint_override="https://" + "minio.lab.sspcloud.fr")
s3.get_file_info(fs.FileSelector("donnees-insee/diffusion", recursive=True))
mc 👇
mc ls -r

3.2.4 Télécharger un fichier depuis S3 pour l’enregistrer en local

Cette méthode n’est en général pas recommandée car, comme on va le voir par la suite, il est possible de lire à la volée des fichiers. Cependant, télécharger un fichier depuis le cloud pour l’écrire sur le disque local peut parfois être utile (par exemple, lorsqu’il est nécessaire de dézipper un fichier).

boto3 👇

On utilise cette fois la méthode download_file

import boto3

s3 = boto3.client("s3", endpoint_url="https://minio.lab.sspcloud.fr")
s3.download_file(
    "donnees-insee", "diffusion/FILOSOFI/2014/FILOSOFI_COM.csv", "data.csv"
)
S3FS 👇
import s3fs

fs = s3fs.S3FileSystem(client_kwargs={"endpoint_url": "https://minio.lab.sspcloud.fr"})
fs.download("donnees-insee/diffusion/FILOSOFI/2014/FILOSOFI_COM.csv", "test.csv")
Snakemake 👇
from snakemake.remote.S3 import RemoteProvider as S3RemoteProvider
S3 = S3RemoteProvider(host = "https://" + os.getenv('AWS_S3_ENDPOINT'))
bucket = "mon-bucket"

rule ma_super_regle_s3:
    input:
        fichier = S3.remote(f'{bucket}/moninput.csv')
    output:
        fichier='mon_dossier_local/monoutput.csv'
    run:
        shell("cp {input[0]} {output[0]}")
mc 👇
mc cp "donnees-insee/FILOSOFI/2014/FILOSOFI_COM.csv" 'data.csv'

3.2.5 Lire un fichier directement

La méthode précédente n’est pas optimale. En effet, l’un des intérêts des API est qu’on peut traiter un fichier sur S3 comme s’il s’agissait d’un fichier sur son PC. Cela est d’ailleurs une manière plus sécurisée de procéder puisqu’on lit les données à la volée, sans les écrire dans un filesystem local.

boto3 👇
import boto3

s3 = boto3.client("s3", endpoint_url="https://minio.lab.sspcloud.fr")
obj = s3.get_object(
    Bucket="donnees-insee", Key="diffusion/FILOSOFI/2014/FILOSOFI_COM.csv"
)
df = pd.read_csv(obj["Body"], sep=";")
df.head(2)
S3FS 👇

Le code suivant devrait permettre d’effectuer la même opération avec s3fs

import pandas as pd
import s3fs

fs = s3fs.S3FileSystem(client_kwargs={"endpoint_url": "https://minio.lab.sspcloud.fr"})
df = pd.read_csv(
    fs.open(
        "{}/{}".format("donnees-insee", "diffusion/FILOSOFI/2014/FILOSOFI_COM.csv"),
        mode="rb",
    ),
    sep=";",
)

df.head(2)
Snakemake 👇
from snakemake.remote.S3 import RemoteProvider as S3RemoteProvider
S3 = S3RemoteProvider(host = "https://" + os.getenv('AWS_S3_ENDPOINT'))
bucket = "mon-bucket"

rule ma_super_regle_s3:
    input:
        fichier = S3.remote(f'{bucket}/moninput.csv')
    run:
        import pandas as pd
        df = pd.read_csv(input.fichier)
        # PLUS D'OPERATIONS
Arrow 👇

Arrow est une librairie qui permet de lire des CSV. Il est néanmoins beaucoup plus pratique d’utiliser le format parquet avec arrow. Dans un premier temps, on configure le filesystem avec les fonctionalités d’Arrow (cf. précédemment).

from pyarrow import fs

s3 = fs.S3FileSystem(endpoint_override="http://" + "minio.lab.sspcloud.fr")

Pour lire un csv, on fera:

from pyarrow import fs
from pyarrow import csv

s3 = fs.S3FileSystem(endpoint_override="https://" + "minio.lab.sspcloud.fr")

with s3.open_input_file(
    "donnees-insee/diffusion/FILOSOFI/2014/FILOSOFI_COM.csv"
) as file:
    df = csv.read_csv(file, parse_options=csv.ParseOptions(delimiter=";")).to_pandas()

Pour un fichier au format parquet, la démarche est plus simple grâce à l’argument filesystem dans pyarrow.parquet.ParquetDataset :

import pyarrow.parquet as pq

# bucket = ""
# parquet_file=""
df = (
    pq.ParquetDataset(f"{bucket}/{parquet_file}", filesystem=s3)
    .read_pandas()
    .to_pandas()
)

3.2.6 Uploader un fichier

boto3 👇
s3.upload_file(file_name, bucket, object_name)
S3FS 👇
fs.put(filepath, f"{bucket}/{object_name}", recursive=True)
Arrow 👇

Supposons que df soit un pd.DataFrame Dans un système local, on convertirait en table Arrow puis on écrirait en parquet (voir la documentation officielle). Quand on est sur un système S3, il s’agit seulement d’ajouter notre connexion à S3 dans l’argument filesystem (voir la page sur ce sujet dans la documentation Arrow)

import pyarrow as pa
import pyarrow.parquet as pq

table = pa.Table.from_pandas(df)
pq.write_table(table, f"{bucket}/{path}", filesystem=s3)
Snakemake 👇
from snakemake.remote.S3 import RemoteProvider as S3RemoteProvider
S3 = S3RemoteProvider(host = "https://" + os.getenv('AWS_S3_ENDPOINT'))
bucket = "mon-bucket"

rule ma_super_regle_s3:
    input:
        fichier='mon_dossier_local/moninput.csv'
    output:
        fichier=S3.remote(f'{bucket}/monoutput.csv')
    run:
        shell("cp output.fichier input.fichier")
mc 👇
mc cp 'data.csv' "MONBUCKET/monoutput.csv"

3.3 Cas pratique : stocker les données de son projet sur le SSP Cloud

Une composante essentielle de l’évaluation des projets Python est la reproductibilité, i.e. la possibilité de retrouver les mêmes résultats à partir des mêmes données d’entrée et du même code. Dans la mesure du possible, il faut donc que votre rendu final parte des données brutes utilisées comme source dans votre projet. Si les fichiers de données source sont accessibles via une URL publique par exemple, il est idéal de les importer directement à partir de cette URL au début de votre projet (voir le TP Pandas pour un exemple d’un tel import via Pandas).

En pratique, cela n’est pas toujours possible. Peut-être que vos données ne sont pas directement publiquement accessibles, ou bien sont disponibles sous des formats complexes qui demandent des pré-traitements avant d’être exploitables dans un format de donnée standard. Peut-être que vos données résultent d’une phase de récupération automatisée via une API ou du webscraping, auquel cas l’étape de récupération peut prendre du temps à reproduire. Par ailleurs, les sites internet évoluent fréquemment dans le temps, il est donc préférable de “figer” les données une fois l’étape de récupération effectuée. De la même façon, même s’il ne s’agit pas de données source, vous pouvez vouloir entraîner des modèles et stocker leur version entraînée, car cette étape peut également être chronophage.

Dans toutes ces situations, il est nécessaire de pouvoir stocker des données (ou des modèles). Votre dépôt Git n’est pas le lieu adapté pour le stockage de fichiers volumineux. Un projet Python bien construit est modulaire: il sépare le stockage du code (Git), d’éléments de configuration (par exemple des jetons d’API qui ne doivent pas être dans le code) et du stockage des données. Ce principe

Là où Git est fait pour stocker du code, on utilise des solutions adaptées pour le stockage de fichiers. De nombreuses solutions existent pour ce faire. Sur le SSP Cloud, on propose MinIO, une implémentation open-source du stockage S3 présenté plus haut. Ce court tutoriel vise à présenter une utilisation standard dans le cadre de vos projets.

Warning

Quelle que soit la solution de stockage retenue pour vos données/modèles, le code ayant servir à produire ces objets doit impérativement figurer dans votre dépôt de projet.

3.3.1 Partager des fichiers sur le SSP Cloud

Comme expliqué plus haut, on stocke les fichiers sur S3 dans un bucket. Sur le SSP Cloud, un bucket est créé automatiquement lors de votre création de compte, avec le même nom que votre compte SSP Cloud. L’interface Mes Fichiers vous permet d’y accéder de manière visuelle, d’y importer des fichiers, de les télécharger, etc.

Dans ce tutoriel, nous allons plutôt y accéder de manière programmatique, via du code Python. Le package s3fs permet de requêter votre bucket à la manière d’un filesystem classique. Par exemple, vous pouvez lister les fichiers disponibles sur votre bucket avec la commande suivante :

import s3fs

fs = s3fs.S3FileSystem(client_kwargs={"endpoint_url": "https://minio.lab.sspcloud.fr"})

MY_BUCKET = "mon_nom_utilisateur_sspcloud"
fs.ls(MY_BUCKET)

Si vous n’avez jamais ajouté de fichier sur MinIO, votre bucket est vide, cette commande devrait donc renvoyer une liste vide. On va donc ajouter un premier dossier pour voir la différence.

Par défaut, un bucket vous est personnel, c’est à dire que les données qui s’y trouvent ne peuvent être lues ou modifiées que par vous. Dans le cadre de votre projet, vous aurez envie de partager ces fichiers avec les membres de votre groupe pour développer de manière collaborative. Mais pas seulement ! Il faudra également que vos correcteurs puissent accéder à ces fichiers pour reproduire vos analyses.

Il existe différentes possibilités de rendre des fichiers plus ou moins publics sur MinIO. La plus simple, et celle que nous vous recommandons, est de créer un dossier diffusion à la racine de votre bucket. Sur le SSP Cloud, tous les fichiers qui se situent dans un dossier diffusion sont accessibles en lecture à l’ensemble des utilisateurs authentifiés. Utilisez l’interface Mes Fichiers pour créer un dossier diffusion à la racine de votre bucket. Si tout a bien fonctionné, la commande Python ci-dessus devrait désormais afficher le chemin mon_nom_utilisateur_sspcloud/diffusion.

Note

Le stockage cloud favorise le travail collaboratif !

Plutôt que chaque membre du projet travaille avec ses propres fichiers sur son ordinateur, ce qui implique une synchronisation fréquente entre membres du groupe et limite la reproductibilité du fait des risques d’erreur, les fichiers sont mis sur un dépôt central, que chaque membre du groupe peut ensuite requêter.

Pour cela, il faut simplement s’accorder au sein du groupe pour utiliser le bucket d’un des membres du projet, et s’assurer que les autres membres du groupe peuvent accéder aux données, en les mettant dans le dossier diffusion du bucket choisi.

3.3.2 Récupération et stockage de données

Maintenant que nous savons où mettre nos données sur MinIO, regardons comment le faire en pratique depuis Python.

3.3.2.1 Cas d’un Dataframe

Reprenons un exemple issu du cours sur les API pour simuler une étape de récupération de données coûteuse en temps.

import requests
import pandas as pd

api_query < -"https://koumoul.com/data-fair/api/v1/datasets/dpe-france/lines?format=json&q_mode=simple&qs=code_insee_commune_actualise%3A%2201450%22&size=100&select=%2A&sampling=neighbors"
response_json = requests.get(url_api).json()
df_dpe = pd.json_normalize(response_json["results"])

df_dpe.head(2)

Cette requête nous permet de récupérer un DataFrame Pandas, dont les deux premières lignes sont imprimées ci-dessus. Dans notre cas le processus est volontairement simpliste, mais on peut imaginer que de nombreuses étapes de requêtage / préparation de la données sont nécessaires pour aboutir à un dataframe exploitable dans la suite du projet, et que ce processus est coûteux en temps. On va donc stocker ces données “intermédiaires” sur MinIO afin de pouvoir exécuter la suite du projet sans devoir refaire tourner tout le code qui les a produites.

On peut utiliser les fonctions d’export de Pandas, qui permettent d’exporter dans différents formats de données. Vu qu’on est dans le cloud, une étape supplémentaire est nécessaire : on ouvre une connexion vers MinIO, puis on exporte notre dataframe.

MY_BUCKET = "mon_nom_utilisateur_sspcloud"
FILE_PATH_OUT_S3 = f"{MY_BUCKET}/diffusion/df_dpe.csv"

with fs.open(FILE_PATH_OUT_S3, "w") as file_out:
    df_dpe.to_csv(file_out)

On peut vérifier que notre fichier a bien été uploadé via l’interface Mes Fichiers ou bien directement en Python en interrogeant le contenu du dossier diffusion de notre bucket :

fs.ls(f"{MY_BUCKET}/diffusion")

On pourrait tout aussi simplement exporter notre dataset en Parquet, pour limiter l’espace de stockage et maximiser les performances à la lecture. Attention : vu que Parquet est un format compressé, il faut préciser qu’on écrit un fichier binaire : le mode d’ouverture du fichier passé à la fonction fs.open passe de w (write) à wb (write binary).

FILE_PATH_OUT_S3 = f"{MY_BUCKET}/diffusion/df_dpe.parquet"

with fs.open(FILE_PATH_OUT_S3, "wb") as file_out:
    df_dpe.to_parquet(file_out)

3.3.2.2 Cas de fichiers

Dans la partie précédente, on était dans le cas “simple” d’un dataframe, ce qui nous permettait d’utiliser directement les fonctions d’export de Pandas. Maintenant, imaginons qu’on ait plusieurs fichiers d’entrée, pouvant chacun avoir des formats différents. Un cas typique de tels fichiers sont les fichiers ShapeFile, qui sont des fichiers de données géographiques, et se présentent sous forme d’une combinaison de fichiers (cf. chapitre sur GeoPandas). Commençons par récupérer un fichier .shp pour voir sa structure.

On récupère ci-dessous les contours des départements français, sous la forme d’une archive .zip qu’on va décompresser en local dans un dossier departements_fr.

import io
import os
import requests
import zipfile

# Import et décompression
contours_url = (
    "https://www.data.gouv.fr/fr/datasets/r/eb36371a-761d-44a8-93ec-3d728bec17ce"
)
response = requests.get(contours_url, stream=True)
zipfile = zipfile.ZipFile(io.BytesIO(response.content))
zipfile.extractall("departements_fr")

# Vérification du dossier (local, pas sur S3)
os.listdir("departements_fr")

Vu qu’il s’agit cette fois de fichiers locaux et non d’un dataframe Pandas, on doit utiliser le package s3fs pour transférer les fichiers du filesystem local au filesystem distant (MinIO). Grâce à la commande put, on peut copier en une seule commande le dossier sur MinIO. Attention à bien spécifier le paramètre recursive=True, qui permet de copier à la fois un dossier et son contenu.

fs.put("departements_fr/", f"{MY_BUCKET}/diffusion/departements_fr/", recursive=True)

Vérifions que le dossier a bien été copié :

fs.ls(f"{MY_BUCKET}/diffusion/departements_fr")

Si tout a bien fonctionné, la commande ci-dessus devrait renvoyer une liste contenant les chemins sur MinIO des différents fichiers (.shp, .shx, .prj, etc.) constitutifs du ShapeFile des départements.

3.3.3 Utilisation des données

En sens inverse, pour récupérer les fichiers depuis MinIO dans une session Python, les commandes sont symétriques.

3.3.3.1 Cas d’un dataframe

Attention à bien passer cette fois le paramètre r (read, pour lecture) et non plus w (write, pour écriture) à la fonction fs.open afin de ne pas écraser le fichier !

MY_BUCKET = "mon_nom_utilisateur_sspcloud"
FILE_PATH_S3 = f"{MY_BUCKET}/diffusion/df_dpe.csv"

# Import
with fs.open(FILE_PATH_S3, "r") as file_in:
    df_dpe = pd.read_csv(file_in)

# Vérification
df_dpe.head(2)

De même, si le fichier est en Parquet (en n’oubliant pas de passer de r à rb pour tenir compte de la compression) :

MY_BUCKET = "mon_nom_utilisateur_sspcloud"
FILE_PATH_S3 = f"{MY_BUCKET}/diffusion/df_dpe.parquet"

# Import
with fs.open(FILE_PATH_S3, "rb") as file_in:
    df_dpe = pd.read_parquet(file_in)

# Vérification
df_dpe.head(2)

3.3.3.2 Cas de fichiers

Dans le cas de fichiers, on va devoir dans un premier temps rapatrier les fichiers de MinIO vers la machine local (en l’occurence, le service ouvert sur le SSP Cloud).

# Récupération des fichiers depuis MinIO vers la machine locale
fs.get(f"{MY_BUCKET}/diffusion/departements_fr/", "departements_fr/", recursive=True)

Puis on les importe classiquement depuis Python avec le package approprié. Dans le cas des ShapeFile, où les différents fichiers sont en fait des parties d’un seul et même fichier, une seule commande permet de les importer après les avoir rappatriés.

import geopandas as gpd

df_dep = gpd.read_file("departements_fr")
df_dep.head(2)

3.4 Pour aller plus loin

Informations additionnelles

environment files have been tested on.

Python version used:

Package Version
affine 2.4.0
aiobotocore 2.15.1
aiohappyeyeballs 2.4.3
aiohttp 3.10.8
aioitertools 0.12.0
aiosignal 1.3.1
alembic 1.13.3
altair 5.4.1
aniso8601 9.0.1
annotated-types 0.7.0
anyio 4.7.0
appdirs 1.4.4
archspec 0.2.3
asttokens 2.4.1
attrs 24.2.0
babel 2.16.0
bcrypt 4.2.0
beautifulsoup4 4.12.3
black 24.8.0
blinker 1.8.2
blis 0.7.11
bokeh 3.5.2
boltons 24.0.0
boto3 1.35.23
botocore 1.35.23
branca 0.7.2
Brotli 1.1.0
bs4 0.0.2
cachetools 5.5.0
cartiflette 0.0.2
Cartopy 0.24.1
catalogue 2.0.10
cattrs 24.1.2
certifi 2024.8.30
cffi 1.17.1
charset-normalizer 3.3.2
chromedriver-autoinstaller 0.6.4
click 8.1.7
click-plugins 1.1.1
cligj 0.7.2
cloudpathlib 0.20.0
cloudpickle 3.0.0
colorama 0.4.6
comm 0.2.2
commonmark 0.9.1
conda 24.9.1
conda-libmamba-solver 24.7.0
conda-package-handling 2.3.0
conda_package_streaming 0.10.0
confection 0.1.5
contextily 1.6.2
contourpy 1.3.0
cryptography 43.0.1
cycler 0.12.1
cymem 2.0.10
cytoolz 1.0.0
dask 2024.9.1
dask-expr 1.1.15
databricks-sdk 0.33.0
dataclasses-json 0.6.7
debugpy 1.8.6
decorator 5.1.1
Deprecated 1.2.14
diskcache 5.6.3
distributed 2024.9.1
distro 1.9.0
docker 7.1.0
duckdb 0.10.1
en-core-web-sm 3.7.1
entrypoints 0.4
et_xmlfile 2.0.0
exceptiongroup 1.2.2
executing 2.1.0
fastexcel 0.11.6
fastjsonschema 2.21.1
fiona 1.10.1
Flask 3.0.3
folium 0.17.0
fontawesomefree 6.6.0
fonttools 4.54.1
fr-core-news-sm 3.7.0
frozendict 2.4.4
frozenlist 1.4.1
fsspec 2023.12.2
gensim 4.3.2
geographiclib 2.0
geopandas 1.0.1
geoplot 0.5.1
geopy 2.4.1
gitdb 4.0.11
GitPython 3.1.43
google-auth 2.35.0
graphene 3.3
graphql-core 3.2.4
graphql-relay 3.2.0
graphviz 0.20.3
great-tables 0.12.0
greenlet 3.1.1
gunicorn 22.0.0
h11 0.14.0
h2 4.1.0
hpack 4.0.0
htmltools 0.6.0
httpcore 1.0.7
httpx 0.28.1
httpx-sse 0.4.0
hyperframe 6.0.1
idna 3.10
imageio 2.36.1
importlib_metadata 8.5.0
importlib_resources 6.4.5
inflate64 1.0.0
ipykernel 6.29.5
ipython 8.28.0
itsdangerous 2.2.0
jedi 0.19.1
Jinja2 3.1.4
jmespath 1.0.1
joblib 1.4.2
jsonpatch 1.33
jsonpointer 3.0.0
jsonschema 4.23.0
jsonschema-specifications 2024.10.1
jupyter-cache 1.0.0
jupyter_client 8.6.3
jupyter_core 5.7.2
kaleido 0.2.1
kiwisolver 1.4.7
langchain 0.3.10
langchain-community 0.3.9
langchain-core 0.3.22
langchain-text-splitters 0.3.2
langcodes 3.5.0
langsmith 0.1.147
language_data 1.3.0
lazy_loader 0.4
libmambapy 1.5.9
locket 1.0.0
lxml 5.3.0
lz4 4.3.3
Mako 1.3.5
mamba 1.5.9
mapclassify 2.8.1
marisa-trie 1.2.1
Markdown 3.6
markdown-it-py 3.0.0
MarkupSafe 2.1.5
marshmallow 3.23.1
matplotlib 3.9.2
matplotlib-inline 0.1.7
mdurl 0.1.2
menuinst 2.1.2
mercantile 1.2.1
mizani 0.11.4
mlflow 2.16.2
mlflow-skinny 2.16.2
msgpack 1.1.0
multidict 6.1.0
multivolumefile 0.2.3
munkres 1.1.4
murmurhash 1.0.11
mypy-extensions 1.0.0
narwhals 1.16.0
nbclient 0.10.0
nbformat 5.10.4
nest_asyncio 1.6.0
networkx 3.3
nltk 3.9.1
numpy 1.26.4
opencv-python-headless 4.10.0.84
openpyxl 3.1.5
opentelemetry-api 1.16.0
opentelemetry-sdk 1.16.0
opentelemetry-semantic-conventions 0.37b0
orjson 3.10.12
outcome 1.3.0.post0
OWSLib 0.28.1
packaging 24.1
pandas 2.2.3
paramiko 3.5.0
parso 0.8.4
partd 1.4.2
pathspec 0.12.1
patsy 0.5.6
Pebble 5.1.0
pexpect 4.9.0
pickleshare 0.7.5
pillow 10.4.0
pip 24.2
platformdirs 4.3.6
plotly 5.24.1
plotnine 0.13.6
pluggy 1.5.0
polars 1.8.2
preshed 3.0.9
prometheus_client 0.21.0
prometheus_flask_exporter 0.23.1
prompt_toolkit 3.0.48
protobuf 4.25.3
psutil 6.0.0
ptyprocess 0.7.0
pure_eval 0.2.3
py7zr 0.20.8
pyarrow 17.0.0
pyarrow-hotfix 0.6
pyasn1 0.6.1
pyasn1_modules 0.4.1
pybcj 1.0.2
pycosat 0.6.6
pycparser 2.22
pycryptodomex 3.21.0
pydantic 2.10.3
pydantic_core 2.27.1
pydantic-settings 2.6.1
Pygments 2.18.0
PyNaCl 1.5.0
pynsee 0.1.8
pyogrio 0.10.0
pyOpenSSL 24.2.1
pyparsing 3.1.4
pyppmd 1.1.0
pyproj 3.7.0
pyshp 2.3.1
PySocks 1.7.1
python-dateutil 2.9.0
python-dotenv 1.0.1
python-magic 0.4.27
pytz 2024.1
pyu2f 0.1.5
pywaffle 1.1.1
PyYAML 6.0.2
pyzmq 26.2.0
pyzstd 0.16.2
querystring_parser 1.2.4
rasterio 1.4.3
referencing 0.35.1
regex 2024.9.11
requests 2.32.3
requests-cache 1.2.1
requests-toolbelt 1.0.0
retrying 1.3.4
rich 13.9.4
rpds-py 0.22.3
rsa 4.9
Rtree 1.3.0
ruamel.yaml 0.18.6
ruamel.yaml.clib 0.2.8
s3fs 2023.12.2
s3transfer 0.10.2
scikit-image 0.24.0
scikit-learn 1.5.2
scipy 1.13.0
seaborn 0.13.2
selenium 4.27.1
setuptools 74.1.2
shapely 2.0.6
shellingham 1.5.4
six 1.16.0
smart-open 7.0.5
smmap 5.0.0
sniffio 1.3.1
sortedcontainers 2.4.0
soupsieve 2.5
spacy 3.7.5
spacy-legacy 3.0.12
spacy-loggers 1.0.5
SQLAlchemy 2.0.35
sqlparse 0.5.1
srsly 2.4.8
stack-data 0.6.2
statsmodels 0.14.4
tabulate 0.9.0
tblib 3.0.0
tenacity 9.0.0
texttable 1.7.0
thinc 8.2.5
threadpoolctl 3.5.0
tifffile 2024.9.20
toolz 1.0.0
topojson 1.9
tornado 6.4.1
tqdm 4.66.5
traitlets 5.14.3
trio 0.27.0
trio-websocket 0.11.1
truststore 0.9.2
typer 0.15.1
typing_extensions 4.12.2
typing-inspect 0.9.0
tzdata 2024.2
Unidecode 1.3.8
url-normalize 1.4.3
urllib3 1.26.20
wasabi 1.1.3
wcwidth 0.2.13
weasel 0.4.1
webdriver-manager 4.0.2
websocket-client 1.8.0
Werkzeug 3.0.4
wheel 0.44.0
wordcloud 1.9.3
wrapt 1.16.0
wsproto 1.2.0
xgboost 2.1.1
xlrd 2.0.1
xyzservices 2024.9.0
yarl 1.13.1
yellowbrick 1.5
zict 3.0.0
zipp 3.20.2
zstandard 0.23.0

View file history

SHA Date Author Description
c1853b9 2024-11-20 15:09:19 Lino Galiana Reprise eval + reprise S3 (#576)
005d89b 2023-12-20 17:23:04 Lino Galiana Finalise l’affichage des statistiques Git (#478)
3fba612 2023-12-17 18:16:42 Lino Galiana Remove some badges from python (#476)
4cd44f3 2023-12-11 17:37:50 Antoine Palazzolo Relecture NLP (#474)
1f23de2 2023-12-01 17:25:36 Lino Galiana Stockage des images sur S3 (#466)
a06a268 2023-11-23 18:23:28 Antoine Palazzolo 2ème relectures chapitres ML (#457)
09654c7 2023-11-14 15:16:44 Antoine Palazzolo Suggestions Git & Visualisation (#449)
889a71b 2023-11-10 11:40:51 Antoine Palazzolo Modification TP 3 (#443)
a771183 2023-10-09 11:27:45 Antoine Palazzolo Relecture TD2 par Antoine (#418)
8556b79 2023-09-27 17:29:23 Julien PRAMIL Typo chapitre S3 (#415)
154f09e 2023-09-26 14:59:11 Antoine Palazzolo Des typos corrigées par Antoine (#411)
9977c5d 2023-08-28 10:43:36 Lino Galiana Fix bug path pandas (#397)
3bdf3b0 2023-08-25 11:23:02 Lino Galiana Simplification de la structure 🤓 (#393)
5d4874a 2023-08-11 15:09:33 Lino Galiana Pimp les introductions des trois premières parties (#387)
3f60d55 2023-02-02 18:59:16 Lino Galiana WIP: Change path S3 (#349)
9000723 2023-01-22 12:01:25 Lino Galiana Corrige code s3fs upload (#345)
1fe65ac 2022-12-25 21:39:51 Lino Galiana Dark boxes (#336)
2227f8e 2022-12-25 18:56:57 Lino Galiana Fix problem s3 chapter (#335)
8e5edba 2022-09-02 11:59:57 Lino Galiana Ajoute un chapitre dask (#264)
688cc15 2022-09-01 18:38:59 Lino Galiana Update index.qmd
2117d2c 2022-09-01 08:54:38 Lino Galiana Ajoute des éléments sur arrow (#262)
f10815b 2022-08-25 16:00:03 Lino Galiana Notebooks should now look more beautiful (#260)
d201e3c 2022-08-03 15:50:34 Lino Galiana Pimp la homepage ✨ (#249)
bacb5a0 2022-07-04 19:05:20 Lino Galiana Enrichir la partie elastic (#241)
12965ba 2022-05-25 15:53:27 Lino Galiana :launch: Bascule vers quarto (#226)
aa945cb 2022-03-31 09:28:14 Lino Galiana corrige une ou deux typos (#225)
34b08ec 2022-03-24 16:37:37 Lino Galiana Ajoute code sur write_parquet dans S3
c51a87b 2022-03-24 16:29:39 Lino Galiana retire typo nom bucket (#223)
3b1d9ff 2022-03-09 10:38:15 Lino Galiana Ajoute détails arrow dans la partie S3 (#220)
9c71d6e 2022-03-08 10:34:26 Lino Galiana Plus d’éléments sur S3 (#218)
2a8809f 2021-10-27 12:05:34 Lino Galiana Simplification des hooks pour gagner en flexibilité et clarté (#166)
2e4d586 2021-09-02 12:03:39 Lino Galiana Simplify badges generation (#130)
80877d2 2021-06-28 11:34:24 Lino Galiana Ajout d’un exercice de NLP à partir openfood database (#98)
6729a72 2021-06-22 18:07:05 Lino Galiana Mise à jour badge onyxia (#115)
4cdb759 2021-05-12 10:37:23 Lino Galiana :sparkles: :star2: Nouveau thème hugo :snake: :fire: (#105)
7f9f97b 2021-04-30 21:44:04 Lino Galiana 🐳 + 🐍 New workflow (docker 🐳) and new dataset for modelization (2020 🇺🇸 elections) (#99)
0a0d034 2021-03-26 20:16:22 Lino Galiana Ajout d’une section sur S3 (#97)
Retour au sommet

Notes de bas de page

  1. Elle permet aussi la lecture et l’écriture de .csv.↩︎

  2. D’ailleurs, les générations n’ayant connu nativement que ce type de stockage ne sont pas familiarisées au concept de file system et préfèrent payer le temps de recherche. Voir cet article sur le sujet.↩︎

  3. D’ailleurs, les générations n’ayant connu nativement que ce type de stockage ne sont pas familiarisées au concept de file system et préfèrent payer le temps de recherche. Voir cet article sur le sujet.↩︎

  4. D’ailleurs, les générations n’ayant connu nativement que ce type de stockage ne sont pas familiarisées au concept de file system et préfèrent payer le temps de recherche. Voir cet article sur le sujet.↩︎

Citation

BibTeX
@book{galiana2023,
  author = {Galiana, Lino},
  title = {Python pour la data science},
  date = {2023},
  url = {https://pythonds.linogaliana.fr/},
  doi = {10.5281/zenodo.8229676},
  langid = {fr},
  abstract = {Dans les entreprises et administrations, un nombre
    croissant d’infrastructure se basent sur des \_clouds\_, qui sont
    des sessions non persistentes où les données ne sont pas stockées
    dans les mêmes serveurs que les machines qui exécutent du code.
    L’une des technologies dominantes dans le domaine est un système de
    stockage nommé `S3`, développé par
    {[}Amazon{]}(https://docs.aws.amazon.com/fr\_fr/AmazonS3/latest/userguide/Welcome.html).
    `Python`, à travers plusieurs \_packages\_ (notamment `boto3`,
    `s3fs` ou `pyarrow`), permet d’utiliser ce système de stockage
    distant comme si on accédait à des fichiers depuis son poste
    personnel. Cette révolution est étroitement associée à l’émergence
    du format de données {[}`Apache
    Parquet`{]}(https://parquet.apache.org/), format utilisable en
    `Python` par le biais du package
    {[}`pyarrow`{]}(https://arrow.apache.org/docs/python/index.html) ou
    avec {[}`Spark`{]}(https://spark.apache.org/) et présentant de
    nombreux avantages pour l’analyse de données (vitesse d’import,
    possibilité de traiter des données plus volumineuses que la RAM...)}
}
Veuillez citer ce travail comme suit :
Galiana, Lino. 2023. Python pour la data science. https://doi.org/10.5281/zenodo.8229676.