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: 3798334 entries, 0 to 3798333
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: 289.8+ MB
annee finess finess_type key value_boolean value_string value_integer value_float missing_value value_date
297965 2020 380000067 geo filtre_chimio True NaN NaN NaN NaN NaN
1978381 2021 970404018 geo mco_dpa_pcd_2018-pcd_moyenne_regionale NaN NaN NaN NaN Non concerné NaN
3615213 2024 530005768 geo certification_ref_2021_score_objectif_3.7 NaN NaN NaN 93.1 NaN NaN
1556881 2019 950300244 geo mco_isoortho_isoortho_2019-iso_ortho_alerte_sup NaN NaN 0.0 NaN NaN NaN
3194212 2024 140026709 geo score_accueil_ssr_ajust NaN NaN NaN NaN Données insuffisantes 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: 959 entries, 0 to 958
Data columns (total 16 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   name               959 non-null    object 
 1   title              959 non-null    object 
 2   description        293 non-null    object 
 3   type               959 non-null    object 
 4   source             959 non-null    object 
 5   indicateur         770 non-null    object 
 6   secteur            770 non-null    object 
 7   theme              770 non-null    object 
 8   acronyme           703 non-null    object 
 9   version            711 non-null    float64
 10  variable           711 non-null    object 
 11  annee              711 non-null    float64
 12  ancienne_variable  711 non-null    object 
 13  url_rapport        582 non-null    object 
 14  type_metier        889 non-null    object 
 15  finess_type        959 non-null    object 
dtypes: float64(2), object(14)
memory usage: 120.0+ KB
name title description type source indicateur secteur theme acronyme version variable annee ancienne_variable url_rapport type_metier finess_type
246 mco_ca_epssv2_2023-ca_epss_v2_moy_nat Moyenne nationale de l'indicateur (Évaluation ... NaN float-string IQSS dossier patient MCO_CA_EPSSV2_2023 MCO CA CA 2023.0 moy_nat_ca_ca_epss_v2_mco 2023.0 moy_nat_ca_ca_epss_v2_mco NaN moyenne_nationale mixte
188 mco_avc_epr1_2014-epr1_c1_ichaut_etbt Intervalle de confiance haut de l'évaluation p... NaN int IQSS dossier patient MCO_AVC_EPR1_2014 MCO AVC EPR1 2014.0 epr1_c1_ichaut_etbt 2014.0 epr1_c1_ichaut_etbt https://www.has-sante.fr/upload/docs/applicati... intervalle_haut mixte
392 mco_dpa_dtn1_2015-dtn1_c1_den_etbt Dénominateur dépistage de troubles nutrionnels... NaN float IQSS dossier patient MCO_DPA_DTN1_2015 MCO DPA DTN1 2015.0 dtn1_c1_den_etbt 2015.0 dtn1_c1_den_etbt https://www.has-sante.fr/upload/docs/applicati... denominateur mixte
518 mco_isoortho_ptg_2020-iso_ptg_alerte_sup Position de l'établissement par rapport à la b... NaN int-string IQSS PMSI MCO_ISOORTHO_PTG_2020 MCO ISOORTHO PTG 2020.0 iso_ptg_alerte_sup 2020.0 iso_ptg_alerte_sup https://www.has-sante.fr/upload/docs/applicati... status mixte
662 ssr_avc_snutv2_2022-snut_v2_ic_haut Borne haute de l'IC associé au résultat de l'i... NaN float-string IQSS dossier patient SSR_AVC_SNUTV2_2022 SSR AVC SNUTV2 2022.0 ic_haut_avc_snut_v2_ssr 2022.0 ic_haut_avc_snut_v2_smr NaN intervalle_haut 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: 2158643 entries, 0 to 2158642
Data columns (total 56 columns):
 #   Column                        Dtype  
---  ------                        -----  
 0   date_export                   object 
 1   num_finess_et                 object 
 2   num_finess_ej                 object 
 3   raison_sociale_et             object 
 4   raison_sociale_longue_et      object 
 5   complement_raison_sociale     object 
 6   complement_distribution       object 
 7   num_voie                      object 
 8   type_voie                     object 
 9   libelle_voie                  object 
 10  complement_voie               object 
 11  lieu_dit_bp                   object 
 12  commune                       float64
 13  departement                   object 
 14  libelle_departement           object 
 15  ligne_acheminement            object 
 16  telephone                     object 
 17  telecopie                     object 
 18  categorie_et                  int64  
 19  libelle_categorie_et          object 
 20  categorie_agregat_et          int64  
 21  libelle_categorie_agregat_et  object 
 22  siret                         float64
 23  code_ape                      object 
 24  code_mft                      float64
 25  libelle_mft                   object 
 26  code_sph                      float64
 27  libelle_sph                   object 
 28  date_ouverture                object 
 29  date_autorisation             object 
 30  date_maj                      object 
 31  num_uai                       object 
 32  coord_x_et                    float64
 33  coord_y_et                    float64
 34  source_coord_et               object 
 35  date_geocodage                object 
 36  region                        float64
 37  libelle_region                object 
 38  code_officiel_geo             object 
 39  code_postal                   int64  
 40  libelle_routage               object 
 41  libelle_code_ape              object 
 42  ferme_cette_annee             bool   
 43  latitude                      float64
 44  longitude                     float64
 45  libelle_commune               object 
 46  adresse_postale_ligne_1       object 
 47  adresse_postale_ligne_2       object 
 48  raison_sociale_ej             object 
 49  raison_sociale_longue_ej      object 
 50  statut_juridique_ej           float64
 51  libelle_statut_juridique_ej   object 
 52  statut_juridique              object 
 53  type_etablissement            object 
 54  actif_qualiscope              bool   
 55  dernier_enregistrement        bool   
dtypes: bool(3), float64(10), int64(3), object(40)
memory usage: 879.0+ 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
350826 2017-12-31 440002988 440001782 EHPAD SAINT-PIERRE EHPAD MAISON DE RETRAITE SAINT PIERRE NaN NaN 98 Rue DES MARRONNIERS ... 98 Rue Des Marronniers 44850 LIGNE ASSOCIATION ST PIERRE ASSOCIATION DE LA MAISON DE RETRAITE SAINT PIERRE 60.0 Ass.L.1901 non R.U.P Privé à but non lucratif Privé à but non lucratif False False
2109134 2024-12-31 590049623 590807517 SAVS LA CHRYSALIDE LA CHRYSALIDE SERVICE D'ACCOMPAGNEMENT À LA V... NaN NaN 40 Rue VERLYCK ... 40 Rue Verlyck 59190 HAZEBROUCK ASSO LES PAPILLONS BLANCS D'HAZEBROUCK ASSOCIATION DES PAPILLONS BLANCS D'HAZEBROUCK 61.0 Ass.L.1901 R.U.P. Privé à but non lucratif Privé à but non lucratif False False
291976 2009-12-31 610004459 610790701 CCAA (ANTENNE) - MORTAGNE AU PERCHE CENTRE DE CURE AMBULATOIRE EN ALCOOLOGIE CENTRE INTERCOM. ACTION SOCIALE NaN 4 Rue DE LA COMÉDIE ... 4 Rue De La Comédie 61400 MORTAGNE AU PERCHE ANPAA 61 ASSOC NATIONALE DE PREVENTION EN ALCOOLOGIE ET... 60.0 Ass.L.1901 non R.U.P Privé à but non lucratif Privé à but non lucratif False False
983070 2012-12-31 310006465 310006457 CRECHE LA BALEINE BLEUE NaN NaN NaN 17 Rue DE L'ASPIN ... 17 Rue De L'Aspin 31100 TOULOUSE ASSOCIATION LA BALEINE BLEUE NaN 60.0 Ass.L.1901 non R.U.P Privé à but non lucratif Privé à but non lucratif False False
480216 2004-12-31 970200085 970202206 CENTRE HOSPITALIER DU CARBET NaN NaN QUARTIER LAJUS NaN NaN NaN ... NaN 97221 LE CARBET CENTRE HOSPITALIER DU CARBET NaN 11.0 Etb.Pub.Départ.Hosp. Public CH 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                648
SAE                                 119
IQSS questionnaire patient           59
IQSS PMSI                            42
Certification v2014                  37
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
770 nb_rep_score_all_rea_ajust Nombre de réponses concernant la satisfaction ... effectif_observe
771 score_all_rea_ajust Note ajustée de satisfaction globale classemen... score
772 score_all_rea_ajust_dp Note ajustée de satisfaction globale classemen... score_arrondi
773 score_ajust_esatis_region_48h Note ajustée de satisfaction globale des patie... moyenne_regionale
774 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
771 score_all_rea_ajust Note ajustée de satisfaction globale classemen... score
778 score_accueil_rea_ajust Note ajustée concernant la satisfaction de l'a... score
780 score_pecinf_rea_ajust Note ajustée de satisfaction de la prise en ch... score
782 score_pecmed_rea_ajust Note ajustée de satisfaction de la prise en ch... score
784 score_chambre_rea_ajust Note ajustée de satisfaction de la chambre score
786 score_repas_rea_ajust Note ajustée de satisfaction des repas score
788 score_sortie_rea_ajust Note ajustée de satisfaction de la sortie score
789 taux_reco_brut_48h Pourcentage de patients recommandant certainem... score
792 score_all_ajust_ca Score de satisfaction globale ajusté score
798 score_avh_ajust Note ajustée de la satisfaction avant l'hospit... score
800 score_acc_ajust Note ajustée concernant la satisfaction accuei... score
802 score_pec_ajust Note ajustée de la satisfaction de la prise en... score
804 score_cer_ajust Note ajustée de la satisfaction chambre et rep... score
806 score_ovs_ajust Note ajustée de la satisfaction sortie et reto... score
808 taux_reco_brut_ca Pourcentage de patients recommandant certainem... score
811 score_all_ssr_ajust Score de satisfaction globale ajusté score
817 score_accueil_ssr_ajust Note ajustée concernant la satisfaction au niv... score
819 score_pec_ssr_ajust Note ajustée concernant la satisfaction au niv... score
821 score_lieu_ssr_ajust Note ajustée concernant la satisfaction au niv... score
823 score_repas_ssr_ajust Note ajustée concernant la satisfaction au niv... score
825 score_sortie_ssr_ajust Note ajustée concernant la satisfaction au niv... score
827 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
9880 2023 520000043 geo D NaN Données insuffisantes Non calculable NaN NaN 38.0 ... Données insuffisantes 77.72 75.89 64.49 Données insuffisantes 56.18 Données insuffisantes 50.0 NaN Données insuffisantes
3859 2019 370007569 geo B B NaN 0.0 0.0 NaN 910.0 ... NaN 82.33 82.79 61.73 NaN 67.55 NaN 66.3 66.5 NaN
5701 2020 910018423 geo Données insuffisantes NaN NaN NaN NaN NaN Données insuffisantes ... NaN Données insuffisantes Données insuffisantes Données insuffisantes NaN Données insuffisantes NaN Données insuffisantes NaN NaN
4453 2019 830000287 geo Non validé Non validé NaN NaN NaN NaN Non validé ... NaN Non validé Non validé Non validé NaN Non validé NaN Non validé Non validé NaN
10199 2023 650000417 geo C A NaN 0.0 0.0 NaN 363.0 ... NaN 83.03 82.3 55.32 NaN 65.11 NaN 44.6 75.6 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
11800 930300595 1110 HOPITAL PRIVE DU VERT GALANT 93 2022 930300595 geo D C NaN ... NaN 76.54 78.79 43.71 NaN 61.24 NaN 34.3 58.7 NaN
7011 620000240 1102 CH HENIN BEAUMONT 62 2018 620000240 geo Données insuffisantes NaN NaN ... NaN Données insuffisantes Données insuffisantes Données insuffisantes NaN Données insuffisantes NaN NaN NaN NaN
4785 440001113 1104 ICO - SITE GAUDUCHEAU 44 2024 440001113 geo A A NaN ... NaN 88.63 87.7 63.44 NaN 72.98 NaN 77.5 88.0 NaN
11207 910150085 1110 GH LES CHEMINOTS SOINS DE SUITE 91 2023 910150085 geo NaN NaN Données insuffisantes ... Données insuffisantes NaN NaN NaN Données insuffisantes NaN Données insuffisantes NaN NaN Données insuffisantes
1976 210987657 1102 HOSPICES CIVILS DE BEAUNE 21 2018 210987657 geo Données insuffisantes B NaN ... NaN Données insuffisantes Données insuffisantes Données insuffisantes NaN Données insuffisantes 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
10355 2021 830000345 CHITS CH SAINTE MUSSE 83 79.29
9600 2019 770000131 CH DE COULOMMIERS 77 Non validé
6278 2019 590000469 CH FOURMIES 59 Données insuffisantes
127 2023 020000394 CH LAON 02 78.65
5961 2023 570000059 CH MARIE-MADELEINE DE FORBACH 57 82.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