nom | profession | |
---|---|---|
0 | Astérix | |
1 | Obélix | Tailleur de menhir |
2 | Assurancetourix | Barde |
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
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"
}
]
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.
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 valeurs6 ; 5 ; 0
, il va privilégier l’utilisation du typeint
au typedouble
qui sera lui même préféré au typeobject
(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, leCSV
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 leCSV
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
"""
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ées000012
ne seront pas considérées identiques à un code-barre12
. - 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 documentationSpark
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 Python
1. 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
"""
= StringIO(s)
source
= pd.read_csv(source, sep=";", index_col=False)
df "taille"] = [155, 190, 175]
df[= pa.Table.from_pandas(df)
table
table
"example.parquet") pq.write_table(table,
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
= pq.read_table("example.parquet", columns=["taille", "cheveux"])
table
"cheveux").aggregate([("taille", "mean")])) (table.group_by(
La manière équivalente de procéder en passant
par l’intermédiaire de pandas
est
= pq.read_table("example.parquet", columns=["taille", "cheveux"])
table
"cheveux")["taille"].mean()) (table.to_pandas().groupby(
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.
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.
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.
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
= s3fs.S3FileSystem(client_kwargs={"endpoint_url": "https://minio.lab.sspcloud.fr"})
fs
= "mon_nom_utilisateur_sspcloud"
MY_BUCKET 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
.
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
< -"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"
api_query = requests.get(url_api).json()
response_json = pd.json_normalize(response_json["results"])
df_dpe
2) df_dpe.head(
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.
= "mon_nom_utilisateur_sspcloud"
MY_BUCKET = f"{MY_BUCKET}/diffusion/df_dpe.csv"
FILE_PATH_OUT_S3
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 :
f"{MY_BUCKET}/diffusion") fs.ls(
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
).
= f"{MY_BUCKET}/diffusion/df_dpe.parquet"
FILE_PATH_OUT_S3
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"
)= requests.get(contours_url, stream=True)
response = zipfile.ZipFile(io.BytesIO(response.content))
zipfile "departements_fr")
zipfile.extractall(
# Vérification du dossier (local, pas sur S3)
"departements_fr") os.listdir(
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.
"departements_fr/", f"{MY_BUCKET}/diffusion/departements_fr/", recursive=True) fs.put(
Vérifions que le dossier a bien été copié :
f"{MY_BUCKET}/diffusion/departements_fr") fs.ls(
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 !
= "mon_nom_utilisateur_sspcloud"
MY_BUCKET = f"{MY_BUCKET}/diffusion/df_dpe.csv"
FILE_PATH_S3
# Import
with fs.open(FILE_PATH_S3, "r") as file_in:
= pd.read_csv(file_in)
df_dpe
# Vérification
2) df_dpe.head(
De même, si le fichier est en Parquet
(en n’oubliant pas de passer de r
à rb
pour tenir compte de la compression) :
= "mon_nom_utilisateur_sspcloud"
MY_BUCKET = f"{MY_BUCKET}/diffusion/df_dpe.parquet"
FILE_PATH_S3
# Import
with fs.open(FILE_PATH_S3, "rb") as file_in:
= pd.read_parquet(file_in)
df_dpe
# Vérification
2) df_dpe.head(
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
f"{MY_BUCKET}/diffusion/departements_fr/", "departements_fr/", recursive=True) fs.get(
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
= gpd.read_file("departements_fr")
df_dep 2) df_dep.head(
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) |
Notes de bas de page
Elle permet aussi la lecture et l’écriture de
.csv
.↩︎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.↩︎
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.↩︎
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
@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...)}
}
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 :
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 officielleIl 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 utiliserboto3
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.
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:S3FS
👇La logique est identique avec
s3fs
.Si on a des jetons d’accès à jour et dans les variables d’environnement adéquates:
Arrow
👇La logique d’
Arrow
est proche de celle des3fs
. Seuls les noms d’arguments changentSi on a des jetons d’accès à jour et dans les variables d’environnement adéquates:
Snakemake
👇La logique de
Snakemake
est, quant à elle, plus proche de celle deboto3
. Seuls les noms d’arguments changentSi on a des jetons d’accès à jour et dans les variables d’environnement adéquates:
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:
S3FS
👇La logique est la même, seuls les noms d’arguments diffèrent
Arrow
👇Tout est en argument cette fois:
Snakemake
👇La logique est la même, seuls les noms d’arguments diffèrent
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 bucketdonnees-insee
.boto3
👇Pour cela, la méthode
list_objects
offre toutes les options nécessaires: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 commeboto3
):Arrow
👇mc
👇3.2.4 Télécharger un fichier depuis
S3
pour l’enregistrer en localCette 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
S3FS
👇Snakemake
👇mc
👇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
👇S3FS
👇Le code suivant devrait permettre d’effectuer la même opération avec
s3fs
Snakemake
👇Arrow
👇Arrow
est une librairie qui permet de lire desCSV
. Il est néanmoins beaucoup plus pratique d’utiliser le formatparquet
avecarrow
. Dans un premier temps, on configure le filesystem avec les fonctionalités d’Arrow
(cf. précédemment).Pour lire un csv, on fera:
Pour un fichier au format parquet, la démarche est plus simple grâce à l’argument
filesystem
danspyarrow.parquet.ParquetDataset
:3.2.6 Uploader un fichier
boto3
👇S3FS
👇Arrow
👇Supposons que
df
soit unpd.DataFrame
Dans un système local, on convertirait en tableArrow
puis on écrirait enparquet
(voir la documentation officielle). Quand on est sur un systèmeS3
, il s’agit seulement d’ajouter notre connexion àS3
dans l’argumentfilesystem
(voir la page sur ce sujet dans la documentation Arrow)Snakemake
👇mc
👇