### EMOIS 2026 - atelier PMSI avec R, Python, SQL, perspectives sur les fichiers parquet *sur LibreDataHub* <br> <hr> ###### Guillaume Pressiat & Lucas Bourneuf - CHU de Brest ###### Adrien Parrot & Antoine Lamer - InterHop.org ###### Raphaël Simon & Vincent Biot - ATIH --- ## InterHop.org - Pour des communs numériques en santé - Transparence et partage libre / décentralisé des savoirs en informatique médicale - Protection de la vie privée et préservation du secret médical (Serment d'Hippocrate) ---- ## InterHop.org/nous Professionnel·les de santé, ingénieur·es, datascientists, juriste, DPO ---- ## Datathon L'association organise des hackathons sur données interhop.org/projets/datathon <hr> #### Objectifs - Aide aux développements d'algorithmes - Réutilisation locale dans les centres de soins - Se rencontrer et travailler ensemble :-) ---- ## Outils proposés par InterHop [Bitwarden](https://interhop.org/projets/bitwarden) [Element](https://interhop.org/projets/element) [Goupile](https://interhop.org/projets/goupile) [Rekkord](https://rekkord.org) [LinkR](https://interhop.org/projets/linkr) [LibreDataHub](https://home.libredatahub.org) ---- ### [Bitwarden](https://password.interhop.org/) ![](https://pad.interhop.org/uploads/8e9720b4-5490-442b-979d-c197f1ec3023.png =300x) ---- ### [Goupile](https://goupile.hds.interhop.org/) ![](https://pad.interhop.org/uploads/89f0f5c2-9680-4e11-8fcf-16b1a32d2bd3.png =700x) https://pubmed.ncbi.nlm.nih.gov/35612138/ ---- ### [Cryptpad](https://cryptpad.hds.interhop.org/) ![](https://pad.interhop.org/uploads/7ba240c0-547c-4115-aeda-411423614f8d.png) ---- ### [LibreDataHub](https://libredatahub.org) ![](https://pad.interhop.org/uploads/27989271-3470-46b1-9cea-efc1bdee55ba.png =800x) ---- ### [LinkR](https://linkr.interhop.org) ![](https://pad.interhop.org/uploads/313463a3-146b-4423-8b5b-aa7a6e49d661.png =800x) https://authors.elsevier.com/a/1kovW4xGJ-GTlY --- ### Inscription sur SSO InterHop Via le SSO Keycloak keycloak.interhop.org/realms/Interhop/account ![](https://pad.interhop.org/uploads/89719b05-0f5e-44ea-aaa6-85190d856345.png =300x) ---- ### Connexion à LibreDataHub home.libredatahub.org Dans le cadre de cet atelier, on utilisera des comptes génériques de formation que nous vous avons communiqué en tout début d'atelier ---- ## Programme de l'atelier - Lire les données PMSI au format in/out avec R et Python - réaliser des traitements sur ces données - les réexporter au format parquet dans l'objectif d'alimenter un lac de données - thématique métier : séjours pour pose de prothèse de hanche ---- ## Les outils de l'atelier - R : les packages [pmeasyr](https://github.com/GuillaumePressiat/pmeasyr), [nomensland](https://github.com/GuillaumePressiat/nomensland), dplyr et arrow - Python : les packages [pypmsi](https://github.com/GuillaumePressiat/pypmsi) et [refpymsi](https://github.com/GuillaumePressiat/refpymsi), polars - fichiers parquet : écriture avec le package arrow - lac de données : requêtage avec duckdb - requêtage sur le stockage s3 InterHop ---- ## Les données de l'atelier ### Base école ATIH - Des fichiers .rsa ont été générés, avec des données issues de - La base école ATIH 2022 et 2023 - Merci à Raphaël Simon, Vincent Biot et Matthieu Mercier (ATIH) ! --- ## parquet ? à lire : https://dridk.me/parquet-files.html ![](https://dridk.me/images/parquet/row_vs_col.png) ---- ## parquet : en quelques mots - Un format libre et open source, indépendant du langage - Plus de problèmes de typage (date, char, int, etc.) à la lecture (csv, txt, ...) - Format en colonnes : plutôt que par ligne, économie de stockage, accélère l'exécution des requêtes (OLAP) - Compression et décompression des données efficaces [source](https://www.databricks.com/fr/blog/what-is-parquet) ---- ## arrow ? ![](https://dridk.me/images/parquet/arrow.png) ---- ## parquet + arrow = le top --- ## Maintenant, codons ensemble ! ---- ## PMSI et parquet avec R On travaillera dans RStudio@libredatahub Pour copier / coller du code, c'est [ici](https://github.com/GuillaumePressiat/emois-2026/blob/main/prepare-atelier/lake_with_duckdb.R) ---- ### R / Lire les données <style> .slides pre { text-align: left; display: flex; justify-content: center; } </style> ```r library(pmeasyr) library(dplyr, warn.conflicts = FALSE) library(arrow) rsa <- irsa(finess = 'EMOIS2026', annee = 2023, mois = 12, path = 'public/base_ecole_atih', typi = 6) %>% tdiag() ``` ---- ### R / écrire les fichiers parquet exemple 1 : un seul fichier parquet ```r rsa$rsa %>% arrow::write_dataset( 'r_lake/mco_rsa.parquet' ) ``` ---- ### R / écrire les fichiers parquet exemple 2 : partition de fichiers parquet ```r rsa$rsa %>% arrow::write_dataset( 'r_lake/mco_rsa', partitioning = c('ANSOR', 'MOISSOR') ) ``` ---- ### R / requêter les données avec duckdb ```r library(duckdb) drv <- duckdb() con <- dbConnect(drv) dbExecute(con, " --INSTALL httpfs; LOAD httpfs") dbGetQuery(con, " select count(*) from read_parquet('r_lake/mco_rsa/*/*/*.parquet', hive_partitioning = true) where RSATYPE = 'C' ") ``` ---- ### R / créer une vue dans duckdb ```r dbExecute(con, " create or replace view mco_rsa as select * from read_parquet('r_lake/mco_rsa/*/*/*.parquet', hive_partitioning = true)") ``` --- ## PMSI et parquet avec Python On travaillera dans JupyterLab@libredatahub Pour copier / coller du code, c'est [ici](https://github.com/GuillaumePressiat/emois-2026/blob/main/prepare-atelier/lake_with_duckdb_polars.ipynb) ```python import polars as pl import pypmsi as pm import duckdb p = pm.noyau_pmsi(finess = 'EMOIS2026', annee = 2023, mois = 12, path = 'public/base_ecole_atih') rsa = p.irsa() ``` ---- ### Python / écrire les fichiers parquet exemple 1 : un seul fichier parquet ```python ( rsa['rsa'] .write_parquet('python_lake/mco_rsa.parquet') ) ``` ---- ### Python / écrire les fichiers parquet exemple 2 : partition de fichiers parquet ```r ( rsa['rsa'] .write_parquet('python_lake/mco_rsa', partition_by=['ansor', 'moissor']) ) ``` ---- ### Python / requêter les données avec duckdb ```python duckdb.sql(""" select count(*) from read_parquet( 'python_lake/mco_rsa/*/*/*.parquet', hive_partitioning = true) where RSATYPE = 'C' """) ``` ---- ### Python / créer une vue dans duckdb ```python duckdb.sql( """ create or replace view mco_rsa as select * from read_parquet('python_lake/mco_rsa/*/*/*.parquet', hive_partitioning = true) """) ``` --- ## PMSI et parquet avec SQL Ouvrir cbeaver pour écrire du SQL ---- ## Requêtes des parquets précédents avec duckdb - ouvrir une nouvelle connexion duckdb - installer l'extension httfs, la charger ```sql SELECT ansor, moissor, count(*) FROM read_parquet('**/pub_lake/mco_actes/*/*/*.parquet', hive_partitioning = true) group by ansor, moissor; SELECT ansor, moissor, count(*) FROM read_parquet('**/pub_lake/mco_actes/*/*/*.parquet', hive_partitioning = true) were regexp_matches(acte, 'N.KA') group by ansor, moissor; ``` ---- ## Requêtage s3 interhop ---- ## Définitions - S3 : **s**imple **s**torage **s**ervice - bucket : grand répertoire contenant des fichiers, conteneur principal - path : chemin vers des fichiers / objets - partition : ensemble de fichiers homogènes (même tables explosées en plusieurs fichiers) **exemples :** ``` s3://bucket/path/fichier s3://bucket/path/partition/fichier ``` ---- ## `glob` : lister les objets S3 ```sql -- listing de tous les objets sur le s3 SELECT * FROM glob('s3://interhop-pmsi-tuto/**'); -- listing de tous les objets sur le s3 affichés sans l'extension parquet SELECT distinct regexp_replace(file, '/data_[0-9].parquet', '') FROM glob('s3://interhop-pmsi-tuto/**'); ``` On distingue différents types de stockage : - sans partitionnement - partitionnement hive : *`annee=2026/mois=03`* ---- ## Requêtes des objets S3 Ici, on requête la partie `fixe` des RSA base école ATIH. ```sql -- pas de partition, deux années fixe séparées select count(*), annee from read_parquet('s3://interhop-pmsi-tuto/*/fixe.parquet') group by annee; -- partition hive, deux années fixe rangées dans annee=202x select annee, count(*) from read_parquet('s3://interhop-pmsi-tuto/hive_style/fixe/*/*.parquet') group by annee; ``` ---- ## Requêtes des objets S3 InterHop Ici, on fait une grosse jointure entre `fixe` et `acte` ```sql select f.annee, a.acte, count(*) as nb from read_parquet('s3://interhop-pmsi-tuto/hive_style/fixe/*/*.parquet') f inner join read_parquet('s3://interhop-pmsi-tuto/hive_style/acte/*/*.parquet') a on f.ident = a.ident and f.annee = a.annee where regexp_matches(a.acte, 'N.KA') group by f.annee, a.acte order by nb desc ``` ---- ## Bonnes pratiques - organiser, ranger les fichiers en pensant aux pouvoirs de `glob` - garder une logique stable au fil du temps - penser que l'on peut réorganiser, mais que cela a un coût (temps, risque d'erreurs) --- ## [pmsi-io](https://github.com/GuillaumePressiat/pmsi-io/) <img src="https://guillaumepressiat.github.io/images/pmsi-io-2.png" alt="pmsi-io" width="700"/> --- ## Ouverture : parquet et ATIH Et si un jour nous avions des fichiers parquet en sortie des logiciels de l'ATIH (Druides) ?
{"type":"slide","tags":"interhop, ppt, emois, 2026","slideOptions":{"transition":"slide"}}