Tutoriel Python - Usage de la Base sur la Qualité et la Sécurité des Soins (BQSS)#

Ce notebook vise à montrer un usage type dont peut être l’objet BQSS, de façon programmatique grâce au langage Python.

Import des librairies et des données#

Les URLS des données sont récupérable sur la page de la Base sur la Qualité et la Sécurité des Soins sur la plateforme Data Gouv accessible via ce lien. La section Fichiers contient les données disponibles, et pour chaque fichier, une description est présente incluant notamment un URL stable qu’il est recommandé d’utiliser.

La base BQSS adoptant un format clé-valeurs, il est nécessaire de récupérer la table des valeurs d’intérêts (concernant la qualité et la sécurité des soins) ainsi que les métadonnées associées spécifiant ces valeurs et les données caractérisant les établissements de santé via les numéro FINESS.

import numpy as np
import pandas as pd  # version >= 1.4.2

# chargement du fichier valeurs.csv dans un dataframe pandas
valeurs_df = pd.read_csv(
    "https://www.data.gouv.fr/fr/datasets/r/da295ccc-2011-4995-b810-ad1f1a82a83f",
    low_memory=False,
)

# chargement du fichier metadata.csv dans un dataframe pandas
metadata_df = pd.read_csv(
    "https://www.data.gouv.fr/fr/datasets/r/e56fb5a5-5a74-4507-ba77-e0411d4aa234",
    low_memory=False,
)

# chargement du fichier finess.csv dans un dataframe pandas
finess_df = pd.read_csv(
    "https://www.data.gouv.fr/fr/datasets/r/48dadbce-56a8-4dc4-ac51-befcdfd82521",
    low_memory=False,
)

Observation des données#

Il est désormais possible de consulter les informations caractérisant ces données et en observer un échantillon.

Caractérisation des données valeurs_df#

valeurs_df.info()
valeurs_df.sample(n=5, random_state=187)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3091251 entries, 0 to 3091250
Data columns (total 10 columns):
 #   Column         Dtype  
---  ------         -----  
 0   annee          int64  
 1   finess         object 
 2   finess_type    object 
 3   key            object 
 4   value_boolean  object 
 5   value_string   object 
 6   value_integer  float64
 7   value_float    float64
 8   missing_value  object 
 9   value_date     object 
dtypes: float64(2), int64(1), object(7)
memory usage: 235.8+ MB
annee finess finess_type key value_boolean value_string value_integer value_float missing_value value_date
2268814 2021 170780068 geo psy_smhosp_qls_2021-qls_classe NaN NaN NaN NaN Non concerné NaN
1598279 2021 2B0000020 jur mco_dpa_pcd_2018-pcd_evolution NaN NaN NaN NaN Non concerné NaN
2703023 2017 620000257 geo classement_48h NaN NaN NaN NaN Données insuffisantes NaN
1558517 2022 410000293 geo mco_ca_reh303c27_2022-reh3_03c27_ratio NaN NaN NaN NaN Non concerné NaN
1569137 2022 130784481 geo mco_ca_reh307c14_2022-reh3_07c14_ratio NaN NaN NaN NaN Non concerné NaN

Un grand nombre de variables de nature différente sont regroupées dans cette table (d’où la table metadata venant spécifier la nature de chaque variables). Le nom des variables est stocké dans la colonne key.

Afin de pouvoir distinguer les différents types de valeurs (booléen, entier, décimal, chaîne de caractères, valeurs manquantes, dates), des colonnes dédiées leur sont attachées.

Ainsi, pour une variable donnée, un seul type est possible… :

  • sur l’ensemble de la classe (hormis la colonne missing_value)

  • pour chaque valeur (justifiant la nature lacunaire de ces colonnes, toutes les colonnes autre que celle prenant la valeur étant à NaN)

Caractérisation des données metadata_df#

metadata_df.info()
metadata_df.sample(n=5, random_state=123)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 907 entries, 0 to 906
Data columns (total 16 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   name               907 non-null    object 
 1   title              907 non-null    object 
 2   description        289 non-null    object 
 3   type               907 non-null    object 
 4   source             907 non-null    object 
 5   indicateur         722 non-null    object 
 6   secteur            722 non-null    object 
 7   theme              722 non-null    object 
 8   acronyme           655 non-null    object 
 9   version            663 non-null    float64
 10  variable           663 non-null    object 
 11  annee              663 non-null    float64
 12  ancienne_variable  663 non-null    object 
 13  url_rapport        582 non-null    object 
 14  type_metier        841 non-null    object 
 15  finess_type        907 non-null    object 
dtypes: float64(2), object(14)
memory usage: 113.5+ KB
name title description type source indicateur secteur theme acronyme version variable annee ancienne_variable url_rapport type_metier finess_type
646 ssr_dpa_dtn1_2015-resultat_dtn1 Résultat du contrôle dépistage de troubles nut... NaN classe IQSS dossier patient SSR_DPA_DTN1_2015 SSR DPA DTN1 2015.0 resultat_dtn1 2015.0 resultat_dtn1 https://www.has-sante.fr/upload/docs/applicati... controle mixte
384 mco_dpa_qls_2021-qls_ichaut Borne haute de l'IC associé au résultat de l'i... NaN float-string IQSS dossier patient MCO_DPA_QLS_2021 MCO DPA QLS 2021.0 qls_ichaut 2021.0 ic_haut_dpa_qls_v3_mco https://www.has-sante.fr/upload/docs/applicati... intervalle_haut mixte
553 psy_ias_icsha_2018-icsha_classe Classe (Indicateur de consommation de solution... NaN classe IQSS questionnaire établissement PSY_IAS_ICSHA_2018 PSY IAS ICSHA 2018.0 icsha_classe 2018.0 classe_ias_icsha_v3_psy https://pprod-web.has-sante.fr/upload/docs/app... classe mixte
643 ssr_dpa_dtn1_2015-dtn1_c1_etbt Valeur dépistage de troubles nutrionnels (2 ni... NaN float IQSS dossier patient SSR_DPA_DTN1_2015 SSR DPA DTN1 2015.0 dtn1_c1_etbt 2015.0 dtn1_c1_etbt https://www.has-sante.fr/upload/docs/applicati... resultat_min mixte
498 mco_pcc_pcc_2021-pcc_evolution Evolution de l'indicateur par rapport au recue... NaN int-string IQSS dossier patient MCO_PCC_PCC_2021 MCO PCC PCC 2021.0 pcc_evolution 2021.0 evol_pcc_pcc_mco https://www.has-sante.fr/upload/docs/applicati... evolution mixte

La colonne name de cette table permet de faire la jointure avec la table valeurs_df via la colonne key associée.

Les métadonnées permettent de compléter les informations associées aux variables, en particulier leur type (encodé implicitement dans valeur.csv, voir ci-dessus), leur source (provenant d’origines variées), leur secteur, leur theme et leur acronyme.

Pour une recherche rapide, vous pouvez aussi utiliser directement le fichier metadata.xlsx qui permet de faire des filtres rapides sur les différentes colonnes.

Caractérisation des données finess_df#

finess_df.info()
finess_df.sample(n=5, random_state=123)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 183719 entries, 0 to 183718
Data columns (total 56 columns):
 #   Column                        Non-Null Count   Dtype  
---  ------                        --------------   -----  
 0   date_export                   183719 non-null  object 
 1   num_finess_et                 183719 non-null  object 
 2   num_finess_ej                 183719 non-null  object 
 3   raison_sociale_et             183719 non-null  object 
 4   raison_sociale_longue_et      131719 non-null  object 
 5   complement_raison_sociale     23120 non-null   object 
 6   complement_distribution       28442 non-null   object 
 7   num_voie                      139085 non-null  object 
 8   type_voie                     171873 non-null  object 
 9   libelle_voie                  176732 non-null  object 
 10  complement_voie               5612 non-null    object 
 11  lieu_dit_bp                   32176 non-null   object 
 12  commune                       9001 non-null    float64
 13  departement                   183719 non-null  object 
 14  libelle_departement           183719 non-null  object 
 15  ligne_acheminement            183719 non-null  object 
 16  telephone                     162205 non-null  object 
 17  telecopie                     104176 non-null  object 
 18  categorie_et                  183719 non-null  int64  
 19  libelle_categorie_et          183719 non-null  object 
 20  categorie_agregat_et          183719 non-null  int64  
 21  libelle_categorie_agregat_et  183719 non-null  object 
 22  siret                         153064 non-null  float64
 23  code_ape                      85791 non-null   object 
 24  code_mft                      183719 non-null  int64  
 25  libelle_mft                   183719 non-null  object 
 26  code_sph                      182641 non-null  float64
 27  libelle_sph                   182641 non-null  object 
 28  date_ouverture                183719 non-null  object 
 29  date_autorisation             183719 non-null  object 
 30  date_maj                      183719 non-null  object 
 31  num_uai                       2570 non-null    object 
 32  coord_x_et                    116403 non-null  float64
 33  coord_y_et                    116401 non-null  float64
 34  source_coord_et               116089 non-null  object 
 35  date_geocodage                118851 non-null  object 
 36  region                        174718 non-null  float64
 37  libelle_region                174718 non-null  object 
 38  code_officiel_geo             174718 non-null  object 
 39  code_postal                   183719 non-null  int64  
 40  libelle_routage               174718 non-null  object 
 41  libelle_code_ape              79852 non-null   object 
 42  ferme_cette_annee             183719 non-null  bool   
 43  latitude                      116074 non-null  float64
 44  longitude                     116074 non-null  float64
 45  libelle_commune               183719 non-null  object 
 46  adresse_postale_ligne_1       137414 non-null  object 
 47  adresse_postale_ligne_2       183719 non-null  object 
 48  raison_sociale_ej             183719 non-null  object 
 49  raison_sociale_longue_ej      147125 non-null  object 
 50  statut_juridique_ej           183719 non-null  int64  
 51  libelle_statut_juridique_ej   183719 non-null  object 
 52  statut_juridique              183719 non-null  object 
 53  type_etablissement            183719 non-null  object 
 54  actif_qualiscope              183719 non-null  bool   
 55  dernier_enregistrement        183719 non-null  bool   
dtypes: bool(3), float64(8), int64(5), object(40)
memory usage: 74.8+ MB
date_export num_finess_et num_finess_ej raison_sociale_et raison_sociale_longue_et complement_raison_sociale complement_distribution num_voie type_voie libelle_voie ... adresse_postale_ligne_1 adresse_postale_ligne_2 raison_sociale_ej raison_sociale_longue_ej statut_juridique_ej libelle_statut_juridique_ej statut_juridique type_etablissement actif_qualiscope dernier_enregistrement
165553 2019-12-31 590805925 590781605 CMP ENFANTS CAUDRY CH CAMBRAI CENTRE MEDICO-PSYCHOLOGIQUE PSY. GENERALE & IN... NaN 59G37-59I11 25 Rue GAMBETTA ... 25 Rue Gambetta 59540 CAUDRY CH CAMBRAI ETABLISSEMENT PUBLIC DE SANTE DE CAMBRAI 13 Etb.Pub.Commun.Hosp. Public Public False False
177996 2022-12-31 400013348 400011177 HOPITAL DE JOUR ADULTES - PARENTIS HOPITAL DE JOUR POUR ADULTES CH MT MARSAN - PA... CH DE MONT DE MARSAN NaN NaN Rue DALIS ... NaN 40160 PARENTIS EN BORN CHI MONT DE MARSAN ET PAYS DES SOURCES CHI DE MONT DE MARSAN ET DU PAYS DES SOURCES 13 Etb.Pub.Commun.Hosp. Public CH False False
105354 2023-12-31 920808920 780140059 HDJ LE PARC NANTERRE HOPITAL DE JOUR LE PARC NANTERRE NaN NaN 39 Rue VOLANT ... 39 Rue Volant 92000 NANTERRE CH THEOPHILE ROUSSEL CENTRE HOSPITALIER THEOPHILE ROUSSEL 12 Etb.Pub.Intdép.Hosp. Public Public False False
81451 2007-12-31 310796776 310000617 ANTENNE D'AUTODIALYSE REVEL ANTENNE D'AUTODIALYSE REVEL NaN "LE JARDIN DE BEAUSEJOUR" NaN NaN NaN ... NaN 31250 REVEL SAS CLINIQUE NÉPHRO SAINT EXUPERY SOCIÉTÉ ACTIONS SIMPLIFIÉES CLINIQUE NÉPHROLOG... 75 Autre Société Privé Privé False False
59867 2013-12-31 220000236 220017974 CENTRE HOSPITALIER DE PLOUGUERNEVEL CENTRE HOSPITALIER DE PLOUGUERNEVEL NaN NaN 2 Route DE ROSTRENEN ... 2 Route De Rostrenen 22110 PLOUGUERNEVEL ASSOCIATION HOSPITALIERE DE BRETAGNE ASSOCIATION HOSPITALIERE DE BRETAGNE 60 Ass.L.1901 non R.U.P Privé à but non lucratif Privé à but non lucratif False False

5 rows × 56 columns

Cette table contient l’ensemble des données Finess historisées. Il y a une ligne par export et par Finess présent à la date de l’export. Pour un même Finess il ya donc très souvent plusieurs lignes.

Les colonnes qui s’avèrent souvent utiles sont:

  • num_finess_et: numéro finess géographique

  • num_finess_ej: numéro finess juridique

  • latitude et longitude: les coordonnées géographiques de l’établissement dans le référentiel WGS84

  • raison_sociale_et: le nom de l’établissement

  • dernier_enregistrement: indique si cette ligne est la dernière connue pour le finess en question

Préparation des données#

Récupération des clés associées à des catégories de valeurs#

Les cas d’usages pour lesquels la base est utilisé ne concernent souvant qu’un sous-ensemble des domaines présents dans la base. On peut par exemple vouloir récupérer l’historique complet des IQSS issus du questionnaire patient (ESATIS).

La mécanique pour sélectionner le sous ensemble de données d’interêt est la suivante:

  • Dans le fichier metadata.csv utiliser la colonne source pour identifier le domaine d’intérêt

  • Dans le fichier metadata.csv recupérer le nom des variables de la source donnée qui sont stockées dans la colonne name

  • Parmis ces variables, utiliser les colonne type_metier (pour les IQSS) et/ou description pour sélectionner les variables d’intérêt

  • Dans le fichier valeurs.csv sélection toutes les lignes dont la colonne key contient une variable du domaine d’intérêt

Ci-dessous un exemple de cette mécanique:

# Affichage des sources de données existantes
metadata_df["source"].value_counts()
source
IQSS dossier patient                600
SAE                                 119
IQSS questionnaire patient           59
IQSS PMSI                            42
Certification v2014                  33
Certification v2021                  22
IQSS questionnaire établissement     21
Activités ET                         11
Name: count, dtype: int64
# Récupération des noms des variables de certification v2014
certif_14_keys = metadata_df[metadata_df["source"] == "Certification v2014"][
    "name"
]

# Récupération des nom des variables esatis
esatis_keys = metadata_df[
    metadata_df["source"] == "IQSS questionnaire patient"
]["name"]
# Affichage des types métiers certification
# Les variables de certification n'ont pas de type métier
metadata_df.loc[
    metadata_df["name"].isin(certif_14_keys),
    ["name", "description", "type_metier"],
].head(5)
name description type_metier
0 certif_V2014_id_niveau Code du niveau de certification validé par le ... NaN
1 certif_date Date de délibération du collège de la HAS qui ... NaN
2 certif_V2014_id_demarche Code interne à la HAS. Il s'agit d'un code per... NaN
3 certif_V2014_decision_thematique_1 Décision finale pour la thématique : Managemen... NaN
4 certif_V2014_decision_thematique_2 Décision finale pour la thématique : Qualité d... NaN
# Affichage des types métiers esatis
metadata_df.loc[
    metadata_df["name"].isin(esatis_keys),
    ["name", "description", "type_metier"],
].head(5)
name description type_metier
718 nb_rep_score_all_rea_ajust Nombre de réponses concernant la satisfaction ... effectif_observe
719 score_all_rea_ajust Note ajustée de satisfaction globale classemen... score
720 score_all_rea_ajust_dp Note ajustée de satisfaction globale classemen... score_arrondi
721 score_ajust_esatis_region_48h Note ajustée de satisfaction globale des patie... moyenne_regionale
722 score_ajust_esatis_type_48h Note ajustée de satisfaction globale des patie... moyenne_nationale
# Affichage des variables de type score pour esatis
metadata_df.loc[
    metadata_df["name"].isin(esatis_keys)
    & (metadata_df["type_metier"] == "score"),
    ["name", "description", "type_metier"],
]
name description type_metier
719 score_all_rea_ajust Note ajustée de satisfaction globale classemen... score
726 score_accueil_rea_ajust Note ajustée concernant la satisfaction de l'a... score
728 score_pecinf_rea_ajust Note ajustée de satisfaction de la prise en ch... score
730 score_pecmed_rea_ajust Note ajustée de satisfaction de la prise en ch... score
732 score_chambre_rea_ajust Note ajustée de satisfaction de la chambre score
734 score_repas_rea_ajust Note ajustée de satisfaction des repas score
736 score_sortie_rea_ajust Note ajustée de satisfaction de la sortie score
737 taux_reco_brut_48h Pourcentage de patients recommandant certainem... score
740 score_all_ajust_ca Score de satisfaction globale ajusté score
746 score_avh_ajust Note ajustée de la satisfaction avant l'hospit... score
748 score_acc_ajust Note ajustée concernant la satisfaction accuei... score
750 score_pec_ajust Note ajustée de la satisfaction de la prise en... score
752 score_cer_ajust Note ajustée de la satisfaction chambre et rep... score
754 score_ovs_ajust Note ajustée de la satisfaction sortie et reto... score
756 taux_reco_brut_ca Pourcentage de patients recommandant certainem... score
759 score_all_ssr_ajust Score de satisfaction globale ajusté score
765 score_accueil_ssr_ajust Note ajustée concernant la satisfaction au niv... score
767 score_pec_ssr_ajust Note ajustée concernant la satisfaction au niv... score
769 score_lieu_ssr_ajust Note ajustée concernant la satisfaction au niv... score
771 score_repas_ssr_ajust Note ajustée concernant la satisfaction au niv... score
773 score_sortie_ssr_ajust Note ajustée concernant la satisfaction au niv... score
775 taux_reco_brut_ssr Pourcentage de patients recommandant certainem... score

Isolation des domaines de données#

Afin de pouvoir préparer et analyser un domaine de données, il est nécessaire de l’isoler en vue de traitements décrits plus bas. Pour des raisons de volumétrie, le présent tutoriel se restreint aux données esatis. Mais il est naturellement possible d’effectuer ces analyses sur un ensemble de domaines de données, voire sur tous les domaines (les mêmes opérations décrites ici peuvent leur être appliquées).

# Isolation du domaine de données esatis
esatis_df = valeurs_df[valeurs_df["key"].isin(esatis_keys)]

# Isolation du domaine de données certification v2014, pour illustration
certif_df = valeurs_df[valeurs_df["key"].isin(certif_14_keys)]

Pivot de la table esatis#

Actuellement, le format clé-valeur de la base induit que chaque valeur de chaque variable pour chacun des établissements est associée à une ligne. Nous souhaitons pourvoir avoir un regroupement des valeurs pour chaque établissement.

Pour cela, nous devons effectuer les opérations suivantes (cf. cellule de code ci-dessous) :

  • Effectuer un pivot à la table pour avoir une colonne par variable (par type), chaque ligne représentant un établissement pour une année donnée (puisque certaines valeurs sont collectées annuellement)

  • Chaque classe de valeur n’ayant qu’un type de valeur acceptable (hormis le type missing_value), toutes les colonnes des autres types seront remplis de NaN => il s’agit donc également de supprimer ces colonnes inutiles

  • Les colonnes des valeurs manquantes pour une clé sont fusionnées avec la colonne de valeurs de la clé correspondante pour ne pas perdre l’information correspondante

# Les colonnes de valeurs sont pivotées selon le colonne key, pour chaque triplet (annee, finess, finess_type)
esatis_df = esatis_df.pivot(
    index=["annee", "finess", "finess_type"], columns="key"
)

# Toutes les colonnes de valeurs liées à un type incorrect vis-à-vis du type attendu sont remplies par des NaN.
# Il s'agit ici de supprimer ces colonnes qui ne portent aucune information exploitable
esatis_df = esatis_df.dropna(axis=1, how="all")

# Les colonnes associées au type missing_value sont fusionnées avec leur colonne de valeur respective
# Cette étape est optionnelle si les valeurs manquantes sont sans intérêts pour l'étude
if "missing_value" in esatis_df:
    esatis_df = esatis_df.combine_first(esatis_df["missing_value"]).drop(
        columns="missing_value"
    )

# Les colonnes qui résultent de ces opérations ont plusieurs niveaux qui sont ici applatis pour n'en avoir plus qu'un
esatis_df = esatis_df.droplevel(0, axis=1)
# Les données d'années, de finess et de finess_type utilisées jusqu'ici comme index sont basculées en colonnes dans la table.
esatis_df = esatis_df.reset_index()

Un échantillon de la table résultante peut être observée ci-dessous, en notant qu’il manque des données pour de nombreux établissements sur certaines années car certains indicateurs n’étaient pas calculés à ce moment là.

esatis_df.sample(n=5, random_state=123)
key annee finess finess_type classement_48h classement_ca classement_ssr evolution_48h evolution_ca evolution_ssr nb_reco_brut_48h ... score_pec_ssr_ajust score_pecinf_rea_ajust score_pecmed_rea_ajust score_repas_rea_ajust score_repas_ssr_ajust score_sortie_rea_ajust score_sortie_ssr_ajust taux_reco_brut_48h taux_reco_brut_ca taux_reco_brut_ssr
1129 2016 970107249 geo Données insuffisantes NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2721 2018 420000192 geo Données insuffisantes NaN NaN 4-Non calculable NaN NaN NaN ... NaN Données insuffisantes Données insuffisantes Données insuffisantes NaN Données insuffisantes NaN NaN NaN NaN
4686 2020 040780470 geo A B NaN NaN NaN NaN 40.0 ... NaN 87.86 87.21 68.11 NaN 74.31 NaN 80.0 64.4 NaN
2467 2018 170780621 geo Données insuffisantes Données insuffisantes NaN 4-Non calculable NaN NaN NaN ... NaN Données insuffisantes Données insuffisantes Données insuffisantes NaN Données insuffisantes NaN NaN NaN NaN
9788 2023 450010079 geo B B NaN 0.0 0.0 NaN 1090.0 ... NaN 82.72 82.12 60.37 NaN 66.89 NaN 64.9 68.7 NaN

5 rows × 62 columns

Exploitation des données#

Dans cette section, une analyse simple sur le domaine esatis est effectuée à des fins d’illustration quant à la valorisation de ces données.

Le cas d’usage de cette analyse est l’observation de l’évolution du positionnement national d’un établissement identifié (il s’agit ici de l’établissement associé au finess n°060000478) vis-à-vis des établissements de même catégorie (il s’agit ici de la catégorie Centres Hospitaliers, associée au code 1102) sur l’indicateur score_all_ajust_ca (correspondant au “score de satisfaction globale ajusté” en chirurgie ambulatoire).

Pour ce faire, il faut :

  • associer aux données esatis les données finess nécessaires à la jointure et à la catégorisation des établissements

  • calculer le classement de l’établissement (son rang absolu et le pourcentage associé) pour chaque année

  • représenter la distribution des établissements pour chaque année en positionnant l’établissement étudié

Ces étapes sont réalisées dans les deux sections ci-dessous.

Finalisation de la préparation des données#

# On ne prend que les informations des finess les plus récentes pour éviter les doublons à la jointure
finess_df = finess_df.loc[finess_df["dernier_enregistrement"]]

# Jointure entre les données esatis et les données Finess restreintes aux agrégats de catégories d'établissement
df = finess_df[
    [
        "num_finess_et",
        "categorie_agregat_et",
        "raison_sociale_et",
        "departement",
    ]
].merge(esatis_df, left_on="num_finess_et", right_on="finess", how="inner")
df.sample(5, random_state=123)
num_finess_et categorie_agregat_et raison_sociale_et departement annee finess finess_type classement_48h classement_ca classement_ssr ... score_pec_ssr_ajust score_pecinf_rea_ajust score_pecmed_rea_ajust score_repas_rea_ajust score_repas_ssr_ajust score_sortie_rea_ajust score_sortie_ssr_ajust taux_reco_brut_48h taux_reco_brut_ca taux_reco_brut_ssr
9433 900000035 1110 HOPITAL PRIVE DE LA MIOTTE 90 2018 900000035 geo B B NaN ... NaN 82.35 83.42 64.98 NaN 70.79 NaN NaN NaN NaN
7246 710978255 1102 CH ALIGRE BOURBON LANCY 71 2018 710978255 geo Données insuffisantes NaN NaN ... NaN Données insuffisantes Données insuffisantes Données insuffisantes NaN Données insuffisantes NaN NaN NaN NaN
9289 870000411 1110 CLINIQUE EMAILLEURS-COLOMBIER LIMOGES 87 2019 870000411 geo B D NaN ... NaN 83.06 80.98 63.31 NaN 66.76 NaN 69.3 55.5 NaN
2540 310780150 1110 CL MEDIPOLE GARONNE TOULOUSE 31 2022 310780150 geo A A NaN ... NaN 85.98 86.0 66.82 NaN 69.15 NaN 78.2 81.9 NaN
3237 350000162 1102 CHIRC SITE REDON 35 2018 350000162 geo C Données insuffisantes NaN ... NaN 84.58 80.89 63.09 NaN 64.51 NaN NaN NaN NaN

5 rows × 66 columns

# Seuls les centres hospitaliers sont conservés
df = df[df["categorie_agregat_et"] == 1102]

# Pour cette étude, seules les colonnes suivantes sont nécessaires pour la suite : annee, taux_reco_brut_ca, finess
# Par ailleurs, cet indicateur n'est collecté qu'a partir de 2019.
df = df.loc[
    df["annee"] >= 2019,
    [
        "annee",
        "finess",
        "raison_sociale_et",
        "departement",
        "score_all_ajust_ca",
    ],
]

# Les établissements sans valeurs pour l'indicateur sont retirés
df = df.dropna()

Un extrait de la table correspondante peut être observée :

df.sample(5, random_state=123)
annee finess raison_sociale_et departement score_all_ajust_ca
4545 2021 510000169 CENTRE HOSPITALIER LEON BOURGEOIS 51 79.15
2711 2022 330000555 CENTRE HOSPITALIER D'ARCACHON 33 79.3
1735 2020 220000368 CENTRE HOSPITALIER LANNION 22 82.86
4137 2021 450000047 CH DEZARNAULDS - GIEN 45 Données insuffisantes
2957 2020 340000033 CH BEZIERS 34 76.21

Visualisation des résultats#

es_finess = "060000478"
axes = df.plot.hist(by="annee", bins=20, figsize=(10, 15), legend=False)
for i, annee in enumerate(np.sort(df["annee"].unique())):
    axes[i].set_xlim(0, 100)
    axes[i].set_ylim(0, 37)
    axes[i].set_ylabel("Fréquences", fontsize=12)
    df_tmp = (
        df[df["annee"] == annee]
        .sort_values("score_all_ajust_ca", ascending=False)
        .reset_index()
    )
    value = df_tmp.loc[
        df_tmp["finess"] == es_finess, "score_all_ajust_ca"
    ].values[0]
    axes[i].axvline(value, color="red")
    axes[i].text(value - 3.2, -4.5, str(value), color="red", fontsize=12)
    axes[i].text(
        value + 1, 38, "finess n°" + str(es_finess), color="red", fontsize=12
    )
    rank = df_tmp[df_tmp["finess"] == es_finess].index.item() + 1
    card_finess = df_tmp.shape[0]
    rank_ratio = 100 * rank / card_finess
    axes[i].text(
        5,
        30,
        f"classement : {rank} / {card_finess}\nposition (%) : {rank_ratio:,.1f}",
        backgroundcolor="lightgrey",
        color="red",
        fontsize=12,
        fontweight="roman",
    )
axes[2].set_xlabel(
    "score de satisfaction globale ajusté",
    fontsize=12,
    labelpad=15,
)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[16], line 2
      1 es_finess = "060000478"
----> 2 axes = df.plot.hist(by="annee", bins=20, figsize=(10, 15), legend=False)
      3 for i, annee in enumerate(np.sort(df["annee"].unique())):
      4     axes[i].set_xlim(0, 100)

File ~/.cache/pypoetry/virtualenvs/bqss-9TtSrW0h-py3.11/lib/python3.11/site-packages/pandas/plotting/_core.py:1412, in PlotAccessor.hist(self, by, bins, **kwargs)
   1350 def hist(
   1351     self, by: IndexLabel | None = None, bins: int = 10, **kwargs
   1352 ) -> PlotAccessor:
   1353     """
   1354     Draw one histogram of the DataFrame's columns.
   1355 
   (...)
   1410         >>> ax = df.plot.hist(column=["age"], by="gender", figsize=(10, 8))
   1411     """
-> 1412     return self(kind="hist", by=by, bins=bins, **kwargs)

File ~/.cache/pypoetry/virtualenvs/bqss-9TtSrW0h-py3.11/lib/python3.11/site-packages/pandas/plotting/_core.py:1031, in PlotAccessor.__call__(self, *args, **kwargs)
   1028             label_name = label_kw or data.columns
   1029             data.columns = label_name
-> 1031 return plot_backend.plot(data, kind=kind, **kwargs)

File ~/.cache/pypoetry/virtualenvs/bqss-9TtSrW0h-py3.11/lib/python3.11/site-packages/pandas/plotting/_matplotlib/__init__.py:71, in plot(data, kind, **kwargs)
     69         kwargs["ax"] = getattr(ax, "left_ax", ax)
     70 plot_obj = PLOT_CLASSES[kind](data, **kwargs)
---> 71 plot_obj.generate()
     72 plot_obj.draw()
     73 return plot_obj.result

File ~/.cache/pypoetry/virtualenvs/bqss-9TtSrW0h-py3.11/lib/python3.11/site-packages/pandas/plotting/_matplotlib/core.py:451, in MPLPlot.generate(self)
    449 def generate(self) -> None:
    450     self._args_adjust()
--> 451     self._compute_plot_data()
    452     self._setup_subplots()
    453     self._make_plot()

File ~/.cache/pypoetry/virtualenvs/bqss-9TtSrW0h-py3.11/lib/python3.11/site-packages/pandas/plotting/_matplotlib/core.py:636, in MPLPlot._compute_plot_data(self)
    634 # no non-numeric frames or series allowed
    635 if is_empty:
--> 636     raise TypeError("no numeric data to plot")
    638 self.data = numeric_data.apply(self._convert_to_ndarray)

TypeError: no numeric data to plot

⚠️ Attention : L’analyse et l’interprétation des résultats des IQSS nécessite de bien prendre connaissance de leur définition.

Nous pouvons constater que l’établissement considéré dans cette étude a:

  • Amélioré son score (de 77.73 à 80.78)

  • Amélioré sa place dans la distribution de 54.6ème percentile à 24.3ème percentile