{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![Urgences - Image CC0 - pexels.com](img/pexels-pixabay-263402.jpg \"Urgences\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Biology Order Prescription\n",
    "*Levi-Dan Azoulay*  \n",
    "*Shana Zirah*  \n",
    "*Nathane Berrebi*  \n",
    "*Gaspard André*  \n",
    "*Jona Benhamou*    \n",
    "*Ali Bellamine*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Plan du document"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les étapes clés de ce projet sont les suivantes :\n",
    "\n",
    "- I. Introduction\n",
    "- II. Téléchargement des données et transformation\n",
    "- III. Exploration et visualisation les données\n",
    "- IV. Sélection des variables d'interêts\n",
    "- V. Définition et entrainement d'une solution d'apprentissage statistique"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# I. Introduction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " ## I.1 Contexte"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Chaque jour, environ 50 000 personnes se présentent dans un service d'accueil des urgences (SAU) en France. En moyenne, 75% des patients retournent à domicile, et 20% sont hospitalisés. La durée moyenne de présence au SAU est longue. On estime que seulement 20% attendront moins d'une heure, tandis que ~30% attendront entre 1h et 2H et ~30% attendront en 2 et 4H. Enfin, un peu plus de 10% resteront au SAU entre 4 et 6H. Dans un contexte de pénurie de soignants, le recours à la consultation au SAU est en constante augmentation depuis plusieurs années. L'optimisation du circuit des urgences est une problématique centrale. Le cout humain et financier des dysfonctionnements du circuit et de l'offre de soin est important. \n",
    "\n",
    "Le parcours classique du circuit des urgences est le suivant :  \n",
    "1. **Premier contact d'ordre administratif**\n",
    "2. **Premier contact soignant avec une infirmière d'accueil et d'orientation (IAO) (~M30) avec**  :\n",
    "    - Recueil du motif de consultation\n",
    "    - Prise des constantes\n",
    "    - Recueil de quelques antécédents et de l'ordonnance du patient \n",
    "    - Eventuellement ECG  \n",
    "\n",
    "\n",
    "Le patient est classé selon un score de gravité (bleu, vert, jaune, orange, rouge, ou 1-2-3-4-5)\n",
    "\n",
    "3. **Premier contact médical avec un médecin (~H1)** :\n",
    "    - Interrogatoire\n",
    "    - Examen clinique\n",
    "\n",
    "    \n",
    "A la suite de cette consultation, plusieurs cas de figures selon la situation.\n",
    "Le patient peut sortir avec ou sans ordonnance si le diagnostic est posé par l'examen clinique et ne nécéssite ni examen, ni hospitalisation.\n",
    "Le patient peut nécéssiter la réalisation d'examens (prise de sang, radiographie, scanner) ou motiver un avis d'un spécialiste. Auquel cas il doit attendre\n",
    "\n",
    "4. **Réalisation des examens complémentaire ou d'un avis (prescription, réalisation, récupération)**\n",
    "5. **Décision finale : Conclusion une fois les examens récupérés. (~H3)**\n",
    "\n",
    "Entre chaque étape, le patient attend pendant une durée plus ou moins longue. Le médecin lui « jongle » avec plusieurs patients à la fois à des étapes différentes. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# I.2 Objectifs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nous proposons d'aider à **raccourcir le temps entre l'arrivée du patient et sa sortie**, en ne subordonnant pas la décision de réaliser un examen biologique à l'examen clinique du médecin. Nous savons que le temps entre l'arrivée au SAU et la première visite avec le médecin est le temps le plus long et le plus mal vécu par les patients. \n",
    "\n",
    "**Nous proposons à l'aide d'un algorithme d'apprentissage statistique de prédire, dès les données fournies par l'IAO, la nécéssité de réaliser un examen de biologie médicale, afin de permettre aux IDE de prélever cet examen juste après l'IAO, de sorte que le médecin dès sa première visite peut conclure avec les résultats de la biologie, qu'il aurait sans cela, demandé et attendu de récuperer avant de conclure et de prendre en charge le patient.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table markdown=\"1\">\n",
    "     <TR>\n",
    "       <TH>Données d'entrée</TH>\n",
    "       <TH>Algorithme</TH>\n",
    "       <TH>Données de sortie</TH>\n",
    "     </TR>\n",
    "     <TR>\n",
    "          <TD></TD>\n",
    "          <TD></TD>\n",
    "          <TD>Vecteur {0,1}^d d'examens de biologie associée à sa réalisation (1) ou non (0)</TD>\n",
    "     </TR>\n",
    "     <TR>\n",
    "          <TD>Age</TD>\n",
    "          <TD ROWSPAN=\"6\">MLP<br \\>NLP (Embeddings, Word2Vec ...) <br \\>Autres</TD>\n",
    "          <TD>Ionogramme Complet - {0,1}</TD>\n",
    "     </TR>\n",
    "     <TR>\n",
    "          <TD>Sexe</TD>\n",
    "          <TD>Bilan hépato-biliaire - {0,1}</TD>\n",
    "     </TR>\n",
    "     <TR>\n",
    "          <TD>Motif de consultation</TD>\n",
    "          <TD>Numération sanguine (NFS) - {0,1}</TD>\n",
    "     </TR>\n",
    "     <TR>\n",
    "          <TD>Paramètres vitaux (FC, SpO2, PA, T°, FR, EVA)</TD>\n",
    "          <TD>Glycémie - {0,1}</TD>\n",
    "     </TR>\n",
    "     <TR>\n",
    "          <TD>Ordonnance d'entrée du patient</TD>\n",
    "          <TD>Hémostase - {0,1}</TD>\n",
    "     </TR>\n",
    "     <TR>\n",
    "          <TD></TD>\n",
    "          <TD>...</TD>\n",
    "     </TR>\n",
    "</table>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# I.3 Définition des métriques"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nous proposons d'effectuer la tache suivante : **prédire les examens biologiques qui seront réalisés lors de l'arrivé d'un patient aux urgences**  \n",
    "Les métriques d'évaluation des performances seront :\n",
    "- L'**accuracy**\n",
    "- La **precision**\n",
    "- L'**aire sous la courbe (AUC)**\n",
    "\n",
    "La métrique d'évaluation d'un algorithme de machine learning est particulièrement sensible dans le cadre d'une application médicale. \n",
    "Nous attachons une importance particulière à la **précision**. En effet, une sur-prescription d'examen biologique non indiqué (faux positif) pourrait entrainer un effet contraire à l'effet escompté, en prolongant le temps de prise en charge des personnes concernées, posant, outre un problème financier et un allongement du temps d'attente, un problème éthique."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# II.  Téléchargement des données et transformation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les données sont issus du projet MIMIC-IV.  \n",
    "Le projet MIMIC est un projet d'open-data médical initié par l'hopital _Beth Israel Deaconess_ à Boston.  \n",
    "Initialement, seul des données de réanimation été accessible.\n",
    "\n",
    "Pour sa 4ème édition, a été mis à disposition un jeu de données couvrant un spectre bien plus large :\n",
    "- Données relatives aux passages aux urgences\n",
    "- Données relatives aux hospitalisations\n",
    "- Données relatives aux séjour en réanimation\n",
    "- Données de radiographie thoracique avec compte rendu associé"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L'ensemble de ces données ont été mis à disposition dans le cadre de projets complémentaires :\n",
    "- [MIMIC-IV](https://physionet.org/content/mimiciv/0.4/) : hospitalisation et réanimation\n",
    "- [MIMIC-IV-ED](https://physionet.org/content/mimic-iv-ed/1.0/) : urgences\n",
    "- [MIMIC-IV-CXR](https://physionet.org/content/mimic-cxr/2.0.0/) : radiographie thoracique\n",
    "\n",
    "Ces bases sont complémentaires dans le sens où chaque collecte a été faite durant une période temporelle spécifique, qui se recoupe plus où moins.  \n",
    "Certains éléments nécessaires à l'exploitation de MIMIC-IV-ED sont présent dans MIMIC-IV.  \n",
    "La lecture de la documentation de MIMIC-IV et de MIMIC-IV-ED est vivement recommandé (lien ci-dessus).\n",
    "\n",
    "En complément, un certains nombre de ressources est disponible sur le site du projet [MIMIC-IV](https://mimic.mit.edu/)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## II.1 Téléchargement des données"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La base de données de biologie étant volumineuse (nous y reviendrons plus bas), un pré-traitement des données a été effectué.  \n",
    "Le pré-traitement est le suivant :\n",
    "- Intégration de l'ensemble des données utiles au sein d'une base de données SQLITE\n",
    "- Tri des lignes de biologies afin de ne conserver que celles répondant au critères suivants :\n",
    "  - Date de réalisation >= date de début du passage aux urgences\n",
    "  - Date de réalisation <= date de fin du passage aux urgences\n",
    "\n",
    "*Le script de transformation peut être consulté dans `database_constitution/database_constitution.py`*\n",
    "\n",
    "Un token de téléchargement des données vous a normallement été mis à disposition."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "    # Commande à executer dans le terminal\n",
    "    pip install -r requirements.txt\n",
    "    python download_data.py [TOKEN]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## II.2 Transformation des données au format tabulaire"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les données sont extraites depuis la base de données SQLITE de la façon suivante :\n",
    "  - Récupération et aggrégation des informations suivantes pour chaque consultation aux urgence identifié par un identifiant unique *stay_id* :\n",
    "    - Date de passage *intime*\n",
    "    - Genre *gender*\n",
    "    - Age *age*\n",
    "    - Température à l'accueil **temperature**\n",
    "    - Fréquence Cardiaque à l'accueil **heartrate**\n",
    "    - Fréquence respiratoire à l'accueil **resprate**\n",
    "    - Saturation en Oxygène à l'accueil **o2sat**\n",
    "    - Pression artérielle Systolique à l'accueil (**sbp**) et Diastolique (**dbp**)\n",
    "    - Cotation de douleur à l'accueil **pain**\n",
    "    - Motif de consultation à l'accueil **chiefcomplaint**\n",
    "    - Consultation dans les 7 derniers jours **last_7** ou 30 derniers jours **last_30**\n",
    "    - Antécédents connus au moment de la consultation selon la Classification Internationale des Maladies (CIM) : CIM-9 **icd9** ou CIM10 **icd10**\n",
    "    - Traitements habituels du patient lors de sa consultation, Generic Sequence Number (GSN) (**gsn**)\n",
    "  - Récupération des examens prescrits pour chaque consultation aux urgences :\n",
    "    - Les examens ont été regroupés par paquet correspondant aux techniques de laboratoire et aux organes / aspects fonctionnels explorés sur base de connaissance métier.\n",
    "    - Il s'agit pour chaque paquet d'examen d'une variables binaire indiquant si l'examen a été prescrit au moins une fois durant le passage aux urgences\n",
    "    - La prescription est identifié par la présence d'un résultat d'examen dans la table de résultats d'examens biologiques **labevents**\n",
    "    - Les différentes modalités d'examens sont :\n",
    "      - Cardiaque (**Cardiaque**)\n",
    "      - Coagulation (**Coagulation**)\n",
    "      - Gazométrie (**Gazometrie**)\n",
    "      - Glycemie Sanguine (**Glycemie_Sanguine**)\n",
    "      - Hépato-biliaire (**Hepato-Biliaire**)\n",
    "      - Ionogramme Complet (**IonoC**)\n",
    "      - Lipase (**Lipase**)\n",
    "      - Numération de Formule Sanguine (**NFS**)\n",
    "      - Phospho-Calcique (**Phospho-Calcique**)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bop_scripts import preprocessing\n",
    "\n",
    "lab_dictionnary = pd.read_csv(\"./config/lab_items.csv\").set_index(\"item_id\")[\"3\"].to_dict()\n",
    "get_drugs, get_diseases = True, True\n",
    "\n",
    "X = preprocessing.generate_features_dataset(\n",
    "    database=\"./data/mimic-iv.sqlite\",\n",
    "    get_drugs=get_drugs,\n",
    "    get_diseases=get_diseases\n",
    ")\n",
    "\n",
    "y = preprocessing.generate_labels_dataset(\n",
    "    database=\"./data/mimic-iv.sqlite\",\n",
    "    lab_dictionnary=lab_dictionnary,\n",
    ")\n",
    "\n",
    "# Par conception, last_7 et last_30 doivent valoir 0 lorsque manquant\n",
    "X[\"last_7\"] = X[\"last_7\"].fillna(0)\n",
    "X[\"last_30\"] = X[\"last_30\"].fillna(0)\n",
    "\n",
    "assert((X[\"stay_id\"] != y[\"stay_id\"]).sum() == 0) # Sanity check"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Train - test split\n",
    "# Nous gardons 10 000 lignes pour l'évaluation\n",
    "\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "X_train, X_test, y_train, y_test = train_test_split(\n",
    "    X, y, test_size=10000, random_state=42\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# III. Exploration et visualisation les données"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import seaborn as sns\n",
    "from matplotlib import pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Avant d'explorer en détail les données, nous procédons à une identification et un nettoyage des données abérrantes.\n",
    "Ce nettoyage est effectué à partir de la documentation de MIMIC, de la visualisation des données et de connaissances métier."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## III.1. Identification et suppression des outliers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "import importlib\n",
    "from bop_scripts import visualisation\n",
    "importlib.reload(visualisation)\n",
    "\n",
    "from bop_scripts.visualisation import plot_all_scatter, plot_missing_outcome, plot_missing_bar, plot_correlation, plot_labels_frequencies_and_correlation, plot_box_variable_label_distribution, plot_odd_word_wc\n",
    "from bop_scripts.preprocessing import remove_outliers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 76824 characters]",
      "text/plain": [
       "<Figure size 1440x720 with 8 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "variables = [\"temperature\", \"heartrate\", \"resprate\", \"o2sat\", \"sbp\", \"dbp\", \"pain\"]\n",
    "plot_all_scatter(X_train, variables, ncols=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "De facon évidente, des **outliers** sont présents dans le dataset. \n",
    "\n",
    "Une temperature au dela de 150°F, un rythme cardiaque au dela de 400 bpm, une saturation en oxygène au dela de 100%, ou une douleur coté au-dessus de 10 sur une echelle de 0 à 10, correspondent à des données abberantes. \n",
    "\n",
    "A l'aide de la visualisation des données, de la documentation de MIMIC, et de connaissances métier, nous definissons des intervalles pour ces variables et supprimons les outliers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>n</th>\n",
       "      <th>total</th>\n",
       "      <th>pourcentage</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>temperature</th>\n",
       "      <td>515</td>\n",
       "      <td>414671</td>\n",
       "      <td>0.12</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>heartrate</th>\n",
       "      <td>30</td>\n",
       "      <td>421179</td>\n",
       "      <td>0.01</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>resprate</th>\n",
       "      <td>78</td>\n",
       "      <td>417837</td>\n",
       "      <td>0.02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>o2sat</th>\n",
       "      <td>137</td>\n",
       "      <td>417552</td>\n",
       "      <td>0.03</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>sbp</th>\n",
       "      <td>269</td>\n",
       "      <td>419888</td>\n",
       "      <td>0.06</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>dbp</th>\n",
       "      <td>626</td>\n",
       "      <td>419060</td>\n",
       "      <td>0.15</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>pain</th>\n",
       "      <td>11614</td>\n",
       "      <td>409525</td>\n",
       "      <td>2.84</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                 n   total  pourcentage\n",
       "temperature    515  414671         0.12\n",
       "heartrate       30  421179         0.01\n",
       "resprate        78  417837         0.02\n",
       "o2sat          137  417552         0.03\n",
       "sbp            269  419888         0.06\n",
       "dbp            626  419060         0.15\n",
       "pain         11614  409525         2.84"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "variables_ranges = {\n",
    "    \"temperature\":[60,130],\n",
    "    \"heartrate\":[20, 300],\n",
    "    \"resprate\":[5, 50],\n",
    "    \"o2sat\":[20, 100],\n",
    "    \"sbp\":[40, 250],\n",
    "    \"dbp\":[20, 200],\n",
    "    \"pain\":[0,10]\n",
    "}\n",
    "\n",
    "X_train_clean, outliers = remove_outliers(X_train, variables_ranges)\n",
    "outliers.round(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 85300 characters]",
      "text/plain": [
       "<Figure size 1440x720 with 8 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_all_scatter(X_train_clean, variables, ncols=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La **distribution des variables après suppression des outliers.**\n",
    "\n",
    "On observe une **distribution normale** des variables comme la fréquence cardiaque, la pression arterielle ou la température"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## III.2. Analyse des valeurs manquantes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 121556 characters]",
      "text/plain": [
       "<Figure size 1440x720 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "categorical_features = ['gender', \"last_7\", \"last_30\"]\n",
    "continuous_features = ['age', 'temperature', 'heartrate', 'resprate', 'o2sat', 'sbp', 'dbp', 'pain']\n",
    "features = categorical_features+continuous_features\n",
    "labels = y_train.columns.values[1:].tolist()\n",
    "\n",
    "plot_missing_outcome(X_train_clean, y_train, features, labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nous observons que le taux de prises de sang prescrites augmente avec le nombre de valeurs manquantes.\n",
    "En effet, plus le nombre de features est manquant, plus les patients ont eu une prise de sang.\n",
    "La variable *donnée manquante* n'est donc pas indépendante de la target variable à prédire. \n",
    "\n",
    "Nous avons observé, en regardant de plus près les motifs de consultation des patients avec beaucoup de données manquantes, qu'il s'agissait en réalité, des **patients les plus graves**.   \n",
    "Il s'agissait typiquement de traumatismes sévères, d'urgences neuro-chirurgicales, d'arrêts cardiaques, ou d'états de choc, ayant conduits les patients **directement en service d'accueil des urgences vitales (le déchocage), sans passer par le triage et l'infirmier d'accueil.**. Cela peut se faire quand le camion du SAMU transfert directement ces patients en salle de déchocage. La prise en charge (et donc la mesure de leurs constantes vitales) a donc été réalisé directement en shuntant le parcours classique, compte tenu de la gravité de leur état médical.  \n",
    "Il pouvait aussi s'agir de patients moins graves mais particulièrement **agités**, ou dont l'état rend la prise en charge à l'IAO complexe et nécéssite donc directement l'examen par un médecin, ou l'administration d'un traitement initial (sédation par exemple).\n",
    "\n",
    "**Au total,**\n",
    "\n",
    "- **le nombre de features est directement associé à la variable d'intêret (target)**\n",
    "\n",
    "- **le nombre de features manquants est un surrogate de l'état et de la gravité des patients.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 20424 characters]",
      "text/plain": [
       "<Figure size 1080x720 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_missing_bar(X, features)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Le pourcentage de données manquantes concernant les features ne dépasse pas 5% pour chaque variable** (à l'exception de la variable \"pain\" qui atteint 7 %). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# III.3. Visualisation des corrélations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 15172 characters]",
      "text/plain": [
       "<Figure size 720x432 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "features_for_corr = ['temperature', 'heartrate','resprate','o2sat', 'sbp','dbp']\n",
    "plot_correlation(X_train_clean, features_for_corr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On observe **quelques corrélations caractéristiques** et attendues entre certaines variables:\n",
    "- Entre la pression arterielle systolique et diastolique\n",
    "- Entre la température et le rythme cardiaque (une fièvre engendre physiologiquement une tachycardie)\n",
    "- Entre le rythme cardiaque et le rythme respiratoire (un état de choc, un exercice physique ou une attaque de panique peuvent accélerer le rythme cardiaque et le rythme respiratoire de facon conjointe)\n",
    "\n",
    "Cependant, on note **de manière générale une corrélation plutôt faible dans la globalité des variables prédictives entre elles.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# III. 4. Analyse du texte"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\4078182\\Miniconda3\\lib\\site-packages\\sklearn\\utils\\deprecation.py:87: FutureWarning: Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0 and will be removed in 1.2. Please use get_feature_names_out instead.\n",
      "  warnings.warn(msg, category=FutureWarning)\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 690072 characters]",
      "text/plain": [
       "<Figure size 1440x720 with 20 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_odd_word_wc(X, y, \"chiefcomplaint\", labels, min_occurrence=3, ncols=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# III. 5. Exploration des labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 56876 characters]",
      "text/plain": [
       "<Figure size 2160x720 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "labels = y_train.columns.values[1:].tolist()\n",
    "plot_labels_frequencies_and_correlation(y, labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Concernant la **fréquence de réalisation de chaque examen biologique.**, environ 50% patients ont bénéficié d'au moins un des examens parmi NFS, ionogramme sanguin, gaz du sang tandis que les autres examens biologiques étaient réalisés chez à peu près 20%.\n",
    "\n",
    "On observe une **corrélation importante entre les 3 variables (labels)** les plus fréquentes, traduisant un co-occurence de leur réalisation aux urgences, à savoir NFS, ionogramme sanguin et gaz du sang. Il existe également une **corrélation importante entre ces 3 variables et le reste des examens biologiques.**  \n",
    "\n",
    "Autrement dit, quand une prise de sang est réalisée, dans la grande majorité des cas, celle-ci comprend en général **au moins une NFS, un ionogramme et un gaz du sang**, **plus ou moins** d'autres modalités d'examens biologiques."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# III. 6. Association entre les features et labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 156616 characters]",
      "text/plain": [
       "<Figure size 1440x1152 with 32 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "features = [\"age\", \"temperature\", \"sbp\", \"dbp\", \"heartrate\", \"resprate\", \"o2sat\", \"pain\"]\n",
    "labels = [\"NFS\", \"IonoC\", \"Cardiaque\", \"Hepato-Biliaire\"]\n",
    "plot_box_variable_label_distribution(X_train_clean, y_train, features, labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Analyser des ATCD\n",
    "# Analyse des traitements"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# IV. Sélection des variables d'interêts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 605,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bop_scripts.models import generate_model, get_features_selection\n",
    "from bop_scripts.visualisation import vizualize_features_selection\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "\n",
    "_, X_train_clean_subset, _, y_train_subset = train_test_split(\n",
    "    X_train_clean, y_train, test_size=500, random_state=42"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 606,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 80392 characters]",
      "text/plain": [
       "<Figure size 1080x864 with 18 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "qualitatives_variables = [\"gender\", \"last_7\", \"last_30\"]\n",
    "quantitatives_variables = ['age', 'temperature', 'heartrate', 'resprate', 'o2sat', 'sbp', 'dbp', 'pain']\n",
    "text_variables = [\"chiefcomplaint\"]\n",
    "\n",
    "scores = get_features_selection(X_train_clean_subset, y_train_subset.iloc[:,1:], \n",
    "                                LogisticRegression(class_weight=\"balanced\", C=1, solver=\"liblinear\", max_iter=50),\n",
    "                                qualitatives_variables, quantitatives_variables, text_variables[0], min_features=8)\n",
    "vizualize_features_selection(scores, \"roc_auc\", n_score_max=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Afin d'évaluer les combinaisons idéales de variables, nous avons effectué entrainé et mesuré les performances de l'ensemble des modèles contenant entre 7 et 11 variables pour l'ensemble des labels.\n",
    "Les performances ont été arrondis à la deuxième décimale.  \n",
    "Lorsque deux modèles présentaient les mêmes performances, le modèle contenant le moins de variables est retenu.\n",
    "\n",
    "L'enjeux est d'évaluer :\n",
    "- Si des modèles plus petits performent aussi bien que de grand modèles\n",
    "- Si certaines variables n'apportaient aucun gain de performances de manières systématique\n",
    "\n",
    "Le texte a été traité à l'aide d'un **CountVectorizer** et l'ensemble des modèles ont été entrainés à l'aide d'une régression logistique. L'algorithme d'optimisation utilisé a été **liblinear** qui s'avère converger plus rapidement dans le cadre de notre problème où un conditionnement par normalisation des variables a été effectué."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "On observe une certaine hétégénéité des variables conduisant à l'obtention d'un meilleur modèle selon le label à prédire.  \n",
    "Aucune variable ne s'avère systématiquement ne pas être inclus dans les modèles les plus performant. A l'opposé, on identifie une forte présence de l'age, du fait d'avoir consulté dans les 7 ou 30 derniers jours (**last_7** ou **last_30**) et du motif de consultation (chiefcomplaint) parmi les meilleurs modèles.  \n",
    "Chose notable, certains modèles semblent bénéficier de l'association de **last_7** et de **last_30**.  \n",
    "\n",
    "Il faut toutefois noter que les modèles proposés présentent de faibles performances dans leurs variations, ces dernières pouvant être lié à des fluctuactions d'échantillonage lors de la cross-validation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# V. Définition et entrainement d'une solution d'apprentissage statistique"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A partir des explorations précédents, nous proposons d'inclure l'ensemble des variables dans le modèle final.  \n",
    "Nous entrainerons un modèle par label à prédire plutôt qu'un modèle multi-label.\n",
    "\n",
    "Nous inclurons l'ensemble des variables explorés, c'est à dire :\n",
    "- genre\n",
    "- last_7\n",
    "- last_30\n",
    "- age\n",
    "- temperature\n",
    "- heartrate\n",
    "- resprate\n",
    "- o2sat\n",
    "- sbp\n",
    "- dbp\n",
    "- pain\n",
    "- chiefcomplaint\n",
    "\n",
    "Ce choix est motivé de la façon suivante :\n",
    "- Notre exploration des variables d'intérêt ne rend pas compte de potentielles relation complexes entre les features\n",
    "- Aucunes des variables analysés n'ont montré l'absence total d'intérêt au sein des modèles\n",
    "- Il existe un rationel métier / physiologique en faveur de l'intérêt de ces variables"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nous nous proposons d'explorer deux modèles :\n",
    "- Une classification à l'aide d'une régression logistique, entrainé à l'aide d'un algorithme stochastique (SAG, SAGA) afin d'éviter d'effectuer des calculs sur l'ensemble du jeu de données\n",
    "- Une classification à l'aide d'un réseau de neurones : Multi Layer Percreptron (MLP)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bop_scripts.models import generate_model, fit_all_classifiers\n",
    "from bop_scripts.visualisation import display_model_performances\n",
    "\n",
    "qualitatives_variables = [\"gender\", \"last_7\", \"last_30\"]\n",
    "quantitatives_variables = ['age', 'temperature', 'heartrate', 'resprate', 'o2sat', 'sbp', 'dbp', 'pain']\n",
    "text_variables = [\"chiefcomplaint\"]\n",
    "labels = y_train.columns[1:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## V. 1. Régression logistique"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1088,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.linear_model import LogisticRegression\n",
    "\n",
    "lr_classifier = generate_model(\n",
    "        LogisticRegression(class_weight=\"balanced\", solver=\"saga\"),\n",
    "        qualitatives_variables,\n",
    "        quantitatives_variables,\n",
    "        text_variables[0],\n",
    "        remove_outliers=True,\n",
    "        outliers_variables_ranges=variables_ranges,\n",
    "        CountVectorizer_kwargs={\"ngram_range\":(1,2), \"max_features\":200}\n",
    ")\n",
    "\n",
    "lr_classifiers = fit_all_classifiers(\n",
    "    lr_classifier,\n",
    "    X_train,\n",
    "    y_train.iloc[:,1:],\n",
    "    hide_warnings=True\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1097,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 346504 characters]",
      "text/plain": [
       "<Figure size 2160x2520 with 29 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "display_model_performances(lr_classifiers, X_test, y_test[labels], threshold=0.5, algorithm_name=\"régression logistique\", ncols=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## V. 2. Neural networks model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bop_scripts.nn_models import torchMLPClassifier_sklearn, torchMLP\n",
    "import torch\n",
    "\n",
    "device = \"cuda:0\" if torch.cuda.is_available() else \"cpu\"\n",
    "torch_classifier = torchMLPClassifier_sklearn(\n",
    "    torchMLP,\n",
    "    early_stop_validations_size=1000,\n",
    "    early_stop=True,\n",
    "    early_stop_metric=\"auc_score\",\n",
    "    device_train= device,\n",
    "    device_predict=\"cpu\",\n",
    "    class_weight=\"balanced\",\n",
    "    learning_rate=1e-3\n",
    ")\n",
    "\n",
    "torch_sklearn_classifier = generate_model(\n",
    "        torch_classifier,\n",
    "        qualitatives_variables,\n",
    "        quantitatives_variables,\n",
    "        text_variables[0],\n",
    "        remove_outliers=True,\n",
    "        outliers_variables_ranges=variables_ranges,\n",
    "        CountVectorizer_kwargs={\"ngram_range\":(1,2), \"max_features\":200}\n",
    ")\n",
    "\n",
    "torch_sklearn_classifiers = fit_all_classifiers(\n",
    "    torch_sklearn_classifier,\n",
    "    X_train,\n",
    "    y_train.iloc[:,1:]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bop_scripts import visualisation\n",
    "import importlib\n",
    "importlib.reload(visualisation)\n",
    "importlib.reload(nn_models)\n",
    "\n",
    "from bop_scripts.nn_models import torchMLPClassifier_sklearn, torchMLP\n",
    "from bop_scripts.visualisation import display_model_performances"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [
    {
     "ename": "IndexError",
     "evalue": "index 1 is out of bounds for axis 1 with size 1",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mIndexError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_17848/169817130.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mdisplay_model_performances\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtorch_sklearn_classifiers\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mX_test\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my_test\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mthreshold\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m0.5\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0malgorithm_name\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m\"MLP\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mncols\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32mc:\\Users\\4078182\\Personal\\git\\biologyOrderPredictor\\bop_scripts\\visualisation.py\u001b[0m in \u001b[0;36mdisplay_model_performances\u001b[1;34m(classifier, X_test, y_test, algorithm_name, threshold, ncols)\u001b[0m\n\u001b[0;32m    357\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    358\u001b[0m         \u001b[0my_test_true\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0my_test\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mlabel\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mvalues\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 359\u001b[1;33m         \u001b[0my_test_hat_proba\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mlabel_classifier\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpredict_proba\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mX_test\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    360\u001b[0m         \u001b[0my_test_hat\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0my_test_hat_proba\u001b[0m \u001b[1;33m>=\u001b[0m \u001b[0mthreshold\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m*\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    361\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mIndexError\u001b[0m: index 1 is out of bounds for axis 1 with size 1"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 16264 characters]",
      "text/plain": [
       "<Figure size 2160x504 with 4 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "display_model_performances(torch_sklearn_classifiers, X_test, y_test.iloc[:,-1], threshold=0.5, algorithm_name=\"MLP\", ncols=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "28b293e0c0671e44c7281dde6399c7c7419d3faca031d22494da8635907ada72"
  },
  "kernelspec": {
   "display_name": "Python 3.9.7 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
