Cas d'usage

Tableau de bord de réanimation

Vue d’ensemble

Dashboard

Ce tableau de bord permet d’obtenir une visualistaion des indicateurs de qualité des services de réanimation.

Il a l’avantage de pouvoir être configué directement par les cliniciens, sans nécessité de connaissances préalables en programmation.

Il est organisé en plusieurs onglets :

  • Démographie : Cette section montre le nombre de patients admis sur la période choisie, ainsi que leurs informations démographiques (âge, sexe), le taux de mortalité, le nombre d’admissions, le taux de réadmission et les principaux diagnostics ICD-10 des séjours.

  • Ventilation : Cette section fournit des données sur la ventilation mécanique, y compris le nombre de patients sous ventilateurs, la durée de la ventilation, le taux d’échec d’extubation, le taux d’auto-extubation et les paramètres ventilatoires sélectionnés.

  • Sédation : Nous y retrouvons les médicaments utilisés pour la sédation, la durée totale de sédation, la consommation de neuroleptiques etc.

  • Dialyse : Cette section fournit des informations sur le nombre de patients dialysés, avec quel type de dialyse.


Installation

Pour installer ce projet, vous pouvez suivre le tutoriel “Mise en place” de la documentation.

Vous pouvez également suivre ces tutoriels, si vous souhaitez importer vos propres données :

Contributions

LinkR est un projet en cours de développement.

En cas de problème pour l’installation ou pour l’utilisation, ou si vous avez des suggestions pour améliorer l’application, merci de nous contacter à linkr-app@pm.me.

Prochaines étapes

Pour le moment, seule la partie “Démographie” est disponible, les autres sections en cours de développement.

Ce tableau de bord est configuré pour visualiser les données de réanimation. Une prochaine étape sera d’appliquer ce modèle de tableau de bords aux autres services hospitaliers et à la médecine libérale.

OMOP

Requêtes usuelles v5.4

Introduction

Nous présentons ici quelques requêtes que l’on utilise fréquemment pour requêter les tables de bases de données au format OMOP.

Ces requêtes se basent sur la version 5.4 du schéma OMOP.

The OMOP CDM v5.4 entity relationship diagram from Martijn Schuemie and Renske Los

Pour chaque tables, les requêtes sont disponibles :

  • en utilisant la librairie dplyr de R (directement depuis LinkR)
  • en utilisant du SQL (PostgreSQL)
  • en utilisant la librairie pandas de Python

Pour le code en dplyr, nous utiliserons les fonctions join_concept et count_concept_rows, qui sont disponibles dans LinkR (pas besoin de les déclarer de nouveau) :

# Permet de faire la jointure des ID et des noms de concepts

join_concept <- function(df, concept_df, key, name, copy = TRUE) {

    # Dans LinkR, les variables de données d$... sont de type lazy, tandis que d$concept ne l'est pas.
    # Nous utilisons l'option copy = TRUE pour copier temporairement la variable d$... en mémoire locale,
    # parce qu'une table lazy ne peut pas être fusionnée directement avec une table locale.

    df %>%
        dplyr::left_join(
            concept_df %>%
            dplyr::select(!!key := concept_id, !!name := concept_name),
        by = key,
        copy = copy
    )
}

# Permet de faire le décompte de chaque concept

count_concept_rows <- function(df, group_col, name_col) {
    df %>%
        dplyr::group_by(dplyr::across(dplyr::all_of(c(group_col, name_col)))) %>%
        dplyr::summarize(n = n(), .groups = 'drop') %>%
        dplyr::arrange(desc(n))
}

PERSON

OMOP CDM v5.4 Person Table
  1. Calcul de l’âge

L’âge est calculé depuis les variables d$visit_occurrence (hospitalisations) ou d$visit_detail (séjours dans les unités au cours d’une hospitalisation).

Il s’agit de l’âge du patient à l’admission pour chaque hospitalisation ou admission dans une unité.

# Le code de calcul des dates étant mal converti en SQL, nous collectons les données avec dplyr::collect(),
# ce qui signifie que les données sont copiées dans un dataframe localement.
# Le code dplyr n'est ainsi pas converti en SQL.

d$visit_occurrence %>%
    dplyr::left_join(
        d$person %>% dplyr::select(person_id, birth_datetime),
        by = "person_id"
    ) %>%
    dplyr::collect() %>%
    dplyr::mutate(
        age = round(as.numeric(difftime(visit_start_datetime, birth_datetime, units = "days")) / 365.25, 1)
    )
-- DuckDB / PostgreSQL

SELECT 
    v.*, 
    ROUND(
        EXTRACT(EPOCH FROM (v.visit_start_datetime - p.birth_datetime)) / (365.25 * 86400), 
        1
    ) AS age
FROM 
    visit_occurrence v
LEFT JOIN 
    (SELECT person_id, birth_datetime FROM person) p
ON 
    v.person_id = p.person_id;
import pandas as pd

merged_df = pd.merge(
    visit_occurrence,
    person[['person_id', 'birth_datetime']],
    on='person_id',
    how='left'
)

merged_df['age'] = round(
    (merged_df['visit_start_datetime'] - merged_df['birth_datetime']).dt.total_seconds() / (365.25 * 86400), 
    1
)
  1. Jointure des noms de concepts
d$person %>%
    join_concept(d$concept, "gender_concept_id", "gender_concept_name") %>%
    join_concept(d$concept, "race_concept_id", "race_concept_name") %>%
    join_concept(d$concept, "ethnicity_concept_id", "ethnicity_concept_name")
-- DuckDB / PostgreSQL

SELECT 
    p.*, 
    c.concept_name AS gender_concept_name
FROM 
    person p
LEFT JOIN 
    (SELECT concept_id AS gender_concept_id, concept_name FROM concept) c
ON 
    p.gender_concept_id = c.gender_concept_id;
import pandas as pd

concept_df = concept[['concept_id', 'concept_name']].rename(
    columns={'concept_id': 'gender_concept_id', 'concept_name': 'gender_concept_name'}
)

merged_df = pd.merge(
    person, 
    concept_df, 
    how='left', 
    left_on='gender_concept_id', 
    right_on='gender_concept_id'
)

VISIT_OCCURRENCE

OMOP CDM v5.4 Person Table
  1. Jointure des noms de concepts
d$visit_occurrence %>%
    join_concept(d$concept, "visit_concept_id", "visit_concept_name") %>%
    join_concept(d$concept, "visit_type_concept_id", "visit_type_concept_name") %>%
    join_concept(d$concept, "admitted_from_concept_id", "admitted_from_concept_name") %>%
    join_concept(d$concept, "discharge_to_concept_id", "discharge_to_concept_name")
  1. Calcul du nombre d’occurrences par visit_concept_name
d$visit_occurrence %>%
    join_concept(d$concept, "visit_concept_id", "visit_concept_name") %>%
    count_concept_rows("visit_concept_id", "visit_concept_name") %>%
    dplyr::collect() %>%
    print(n = 100)

VISIT_DETAIL

OMOP CDM v5.4 Person Table
  1. Jointure des noms de concepts
d$visit_detail %>%
    join_concept(d$concept, "visit_detail_concept_id", "visit_detail_concept_name") %>%
    join_concept(d$concept, "visit_detail_type_concept_id", "visit_detail_type_concept_name") %>%
    join_concept(d$concept, "admitted_from_concept_id", "admitted_from_concept_name") %>%
    join_concept(d$concept, "discharge_to_concept_id", "discharge_to_concept_name")
  1. Calcul du nombre d’occurrences par visit_detail_concept_name
d$visit_detail %>%
    join_concept(d$concept, "visit_detail_concept_id", "visit_detail_concept_name") %>%
    count_concept_rows("visit_detail_concept_id", "visit_detail_concept_name") %>%
    dplyr::collect() %>%
    print(n = 100)

CONDITION_OCCURRENCE

OMOP CDM v5.4 Person Table
  1. Jointure des noms de concepts
d$condition_occurrence %>%
    join_concept(d$concept, "condition_concept_id", "condition_concept_name") %>%
    join_concept(d$concept, "condition_type_concept_id", "condition_type_concept_name")
  1. Calcul du nombre d’occurrences par condition_concept_name
d$condition_occurrence %>%
    join_concept(d$concept, "condition_concept_id", "condition_concept_name") %>%
    count_concept_rows("condition_concept_id", "condition_concept_name") %>%
    dplyr::collect() %>%
    print(n = 100)

MEASUREMENT

OMOP CDM v5.4 Person Table
  1. Jointure des noms de concepts
d$measurement %>%
    join_concept(d$concept, "measurement_concept_id", "measurement_concept_name") %>%
    join_concept(d$concept, "measurement_type_concept_id", "measurement_type_concept_name") %>%
    join_concept(d$concept, "operator_concept_id", "operator_concept_name") %>%
    join_concept(d$concept, "unit_concept_id", "unit_concept_name")
  1. Calcul du nombre d’occurrences par measurement_concept_name
d$measurement %>%
    join_concept(d$concept, "measurement_concept_id", "measurement_concept_name") %>%
    count_concept_rows("measurement_concept_id", "measurement_concept_name") %>%
    dplyr::collect() %>%
    print(n = 100)

OBSERVATION

OMOP CDM v5.4 Person Table
  1. Jointure des noms de concepts
d$observation %>%
    join_concept(d$concept, "observation_concept_id", "observation_concept_name") %>%
    join_concept(d$concept, "observation_type_concept_id", "observation_type_concept_name") %>%
    join_concept(d$concept, "value_as_concept_id", "value_as_concept_name") %>%
    join_concept(d$concept, "qualifier_concept_id", "qualifier_concept_name") %>%
    join_concept(d$concept, "unit_concept_id", "unit_concept_name")
  1. Calcul du nombre d’occurrences par observation_concept_name
d$observation %>%
    join_concept(d$concept, "observation_concept_id", "observation_concept_name") %>%
    count_concept_rows("observation_concept_id", "observation_concept_name") %>%
    dplyr::collect() %>%
    print(n = 100)

Bases de données

MIMIC

La base de données MIMIC, pour Medical Information Mart for Intensive Care, est une base de données nord-américaine contenant des données de plus de 50 000 patients admis en réanimation. Il s’agit de l’une des bases de données de réanimation les plus utilisées, du fait de son accès gratuit.

Malgré des données d’une qualité imparfaite, elle constitue un bon socle pour apprendre à manipuler les données issues d’entrepôts de données de santé (EDS).

Elle existe en plusieurs versions, dont la plus récente est la MIMIC-IV.


Faut-il avoir des connaissances en programmation ?


Non, il n’est pas forcément nécessaire d’avoir des connaissances en programmation pour manipuler les données de cette base de données.

La manipulation de la base de données en elle-même nécessitera des connaissances en programmation, notamment en SQL. Il s’agit d’une base de données avec un schéma particulier, qui est difficile à prendre en main.

Cependant, il est possible de manipuler ces données entièrement avec une interface graphique : c’est une des raisons pour lesquelles LinkR a été créé.


Comment accéder aux données ?


La base de donneés MIMIC comporte pour les versions III et IV des bases tests, qui contiennent les données anonymisées de 100 patients et qui sont accessibles publiquement.

Vous pouvez télécharger les données ici :

Pour accéder aux bases de données complètes, il est nécessaire de valider quelques étapes.

Rendez-vous sur la page de la base MIMIC-III.

Vous verrez cet encadré tout en bas de la page :

Vous devez donc commencer par vous inscrire sur le site physionet.org.

Vous devrez faire une demande d’accès à Physionet, en renseignant quelques informations et en donnant les coordonnées d’un superviseur ou d’un collègue, à qui un mail sera envoyé.

Vous devrez ensuite compléter le CITI Course, il s’agit d’une formation nécessaire afin d’accéder aux données hébergées sur le site Physionet. Les différentes étapes sont détaillées ici.

Vous pourrez ensuite télécharger le certificat une fois le CITI Course terminé, vous pourrez le le déposer ici pour validation par l’équipe de Physionet.

Il ne vous restera plus qu’à signer le data use agreement.