Workshop de Python

Cargamos datasets con información de admisiones a hospitales de enfermos de diabetes. El objetivo es, una vez limpiado el dataset, estudiarlo para extraer el máximo número de insights de los datos.

Información sobre los datos

  • Encounter ID
    • ID único de la admisión de un paciente
  • Patient number
    • ID único de paciente
  • Race
    • Raza
  • Gender
    • Sexo
  • Age

    • Edad (en intervalos de 10 años)
  • Weight

    • Peso en libras
  • Admission type
    • Identificador del tipo de admisión
  • Discharge disposition
    • Identificador del tipo de alta
  • Admission source
    • Identificador de la fuente de admisión
  • Time in hospital
    • Número de días que han pasado entre admission y discharge (admisión y alta)
  • Payer code
    • Identificador del código de pago
  • Medical specialty
    • Identificador de la especialidad médica donde es ingresado el paciente
  • Number of lab procedures
    • Número de análisis en laboratorio que se han llevado a cabo durante el ingreso del paciente
  • Number of procedures
    • Número de procedimientos (diferentes a los análisis en laboratorio) que se han llevado a cabo durante el ingreso del paciente
  • Number of medications
    • Número de nombres de medicamentos distintos que se hayan administrado al paciente durante su ingreso
  • Number of outpatient visits
    • Número de visitas como paciente externo (no ingresado, por ejemplo) que ha tenido el paciente durante el año antes de esta admisión
  • Number of emergency
    • Numero de visitas de emergencia del paciente durante el año antes de esta admisión
  • Number of inpatient visits
    • Número de visitas en las que ha estado ingresado el paciente durante el año antes de esta admisión
  • Diagnosis 1
  • Diagnosis 2
  • Diagnosis 3
  • Number of diagnoses
    • Número de diagnósticos que se han introducido al sistema
  • Glucose serum test result
    • Indica el rango del resultado de la prueba de glucosa, o si no se llevó a cabo la prueba. Valores: “>200,” “>300,” “normal,” y “none” si no se realizó la prueba
  • A1c test result
    • Indica el rango del resultado de la prueba de hemoglobina A1c, o si no se llevó a cabo la prueba. Valores: “>8” si el resultado fue mayor del 8%, “>7” si el resultado fue mayor del 7% pero menor del 8%, “normal” si el resultado fue menor del 7%, y “none” si no se realizó la prueba.
  • Change of medications
    • Indica si ha habido un cambio en la medicación para la diabetes (tanto en la dosis como en el propio medicamento)
  • Diabetes medications
    • Indica si ha el paciente toma medicación para la diabetes
  • 24 features for medications
    • Para los nombres genéricos de los medicamentos metformin, repaglinide, nateglinide, chlorpropamide, glimepiride, acetohexamide, glipizide, glyburide, tolbutamide, pioglitazone, rosiglitazone, acarbose, miglitol, troglitazone, tolazamide, examide, sitagliptin, insulin, glyburide-metformin, glipizide-metformin, glimepiridepioglitazone, metformin-rosiglitazone, y metforminpioglitazone, la variable indica si se prescribió o no el medicamento durante la visita del paciente
    • Valores: “up” si se subió la dosis durante la visita, “down” si se bajó la dosis, “steady” si la dosis no cambió, y “no” si no se prescribió el medicamento
  • Readmitted
    • Indica cuántos días ha tardado el paciente en volver al hospital y ser readmitido. Valores: “<30” si el paciente fue readmitido en menos de 30 días, “>30” si el paciente fue readmitido en más de 30 días, y “No” si el paciente no ha sido readmitido

Cargar librerías

In [ ]:
%matplotlib inline

import re
import random
from collections import Counter

import pandas as pd
pd.set_option('display.max_colwidth', -1)
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import kstest
sns.set(color_codes=True)
/usr/local/lib/python3.6/site-packages/pandas/compat/__init__.py:117: UserWarning: Could not import the lzma module. Your installed Python is incomplete. Attempting to use lzma compression will result in a RuntimeError.
  warnings.warn(msg)
/usr/local/lib/python3.6/site-packages/ipykernel/__main__.py:8: FutureWarning: Passing a negative integer is deprecated in version 1.0 and will not be supported in future version. Instead, use None to not limit the column width.

Lectura de datos

In [ ]:
diabetes = spark.read.csv(DATA_PATH+'diabetic_data.csv', sep=',', header=True, inferSchema=True).toPandas()
admission_source = spark.read.csv(DATA_PATH+'admission_source_id.csv', sep=',', header=True, inferSchema=True).toPandas()
admission_type = spark.read.csv(DATA_PATH+'admission_type_id.csv', sep=',', header=True, inferSchema=True).toPandas()
discharge_disposition = spark.read.csv(DATA_PATH+'discharge_disposition_id.csv', sep=',', header=True, inferSchema=True).toPandas()
/usr/local/lib/python3.6/site-packages/pyarrow/util.py:43: FutureWarning: pyarrow.open_stream is deprecated as of 0.17.0, please use pyarrow.ipc.open_stream instead.
  warnings.warn(msg, FutureWarning)

Comprobar correcta lectura de los datos

Comprobamos correcta descarga y lectura de datos

In [ ]:
diabetes.head()
Out[ ]:
encounter_id patient_nbr race gender age weight admission_type_id discharge_disposition_id admission_source_id time_in_hospital ... citoglipton insulin glyburide-metformin glipizide-metformin glimepiride-pioglitazone metformin-rosiglitazone metformin-pioglitazone change diabetesMed readmitted
0 2278392 8222157 Caucasian Female [0-10) ? 6 25 1 1 ... No No No No No No No No No NO
1 149190 55629189 Caucasian Female [10-20) ? 1 1 7 3 ... No Up No No No No No Ch Yes >30
2 64410 86047875 AfricanAmerican Female [20-30) ? 1 1 7 2 ... No No No No No No No No Yes NO
3 500364 82442376 Caucasian Male [30-40) ? 1 1 7 2 ... No Up No No No No No Ch Yes NO
4 16680 42519267 Caucasian Male [40-50) ? 1 1 7 1 ... No Steady No No No No No Ch Yes NO

5 rows × 50 columns

In [ ]:
admission_source.head()
Out[ ]:
admission_source_id description
0 1 Physician Referral
1 2 Clinic Referral
2 3 HMO Referral
3 4 Transfer from a hospital
4 5 Transfer from a Skilled Nursing Facility (SNF)
In [ ]:
admission_type.head()
Out[ ]:
admission_type_id description
0 1 Emergency
1 2 Urgent
2 3 Elective
3 4 Newborn
4 5 Not Available
In [ ]:
discharge_disposition.head()
Out[ ]:
discharge_disposition_id description
0 1 Discharged to home
1 2 Discharged/transferred to another short term hospital
2 3 Discharged/transferred to SNF
3 4 Discharged/transferred to ICF
4 5 Discharged/transferred to another type of inpatient care institution

Juntar todos los datos en el mismo DataFrame

Left join con admission source

In [ ]:
df = pd.merge(diabetes,admission_source,how='left',on='admission_source_id')
df=df.rename(columns={'description':'admission_source'})

Left join con admission type

In [ ]:
df = pd.merge(df,admission_type,how='left',on='admission_type_id')
df=df.rename(columns={'description':'admission_type'})

Left join con discharge_disposition

In [ ]:
df = pd.merge(df,discharge_disposition,how='left',on='discharge_disposition_id')
df=df.rename(columns={'description':'discharge_disposition'})
df.head()
Out[ ]:
encounter_id patient_nbr race gender age weight admission_type_id discharge_disposition_id admission_source_id time_in_hospital ... glipizide-metformin glimepiride-pioglitazone metformin-rosiglitazone metformin-pioglitazone change diabetesMed readmitted admission_source admission_type discharge_disposition
0 2278392 8222157 Caucasian Female [0-10) ? 6 25 1 1 ... No No No No No No NO Physician Referral None Not Mapped
1 149190 55629189 Caucasian Female [10-20) ? 1 1 7 3 ... No No No No Ch Yes >30 Emergency Room Emergency Discharged to home
2 64410 86047875 AfricanAmerican Female [20-30) ? 1 1 7 2 ... No No No No No Yes NO Emergency Room Emergency Discharged to home
3 500364 82442376 Caucasian Male [30-40) ? 1 1 7 2 ... No No No No Ch Yes NO Emergency Room Emergency Discharged to home
4 16680 42519267 Caucasian Male [40-50) ? 1 1 7 1 ... No No No No Ch Yes NO Emergency Room Emergency Discharged to home

5 rows × 53 columns

Estudiar las dimensiones del dataset

El dataframe cuenta con 101,766 registros y 53 columnas

In [ ]:
df.shape
Out[ ]:
(101766, 53)

Data Wrangling

Cambiar los nombres de las columnas para que cumplan buenas prácticas

Observo nombres

In [ ]:
df.columns
Out[ ]:
Index(['encounter_id', 'patient_nbr', 'race', 'gender', 'age', 'weight',
       'admission_type_id', 'discharge_disposition_id', 'admission_source_id',
       'time_in_hospital', 'payer_code', 'medical_specialty',
       'num_lab_procedures', 'num_procedures', 'num_medications',
       'number_outpatient', 'number_emergency', 'number_inpatient', 'diag_1',
       'diag_2', 'diag_3', 'number_diagnoses', 'max_glu_serum', 'A1Cresult',
       'metformin', 'repaglinide', 'nateglinide', 'chlorpropamide',
       'glimepiride', 'acetohexamide', 'glipizide', 'glyburide', 'tolbutamide',
       'pioglitazone', 'rosiglitazone', 'acarbose', 'miglitol', 'troglitazone',
       'tolazamide', 'examide', 'citoglipton', 'insulin',
       'glyburide-metformin', 'glipizide-metformin',
       'glimepiride-pioglitazone', 'metformin-rosiglitazone',
       'metformin-pioglitazone', 'change', 'diabetesMed', 'readmitted',
       'admission_source', 'admission_type', 'discharge_disposition'],
      dtype='object')

1) Pasamos a minusculas los nombres de las columnas

2) Pasamos los guiones medios a guiones bajos

In [ ]:
nuevas_col = [col.lower().replace('-','_') for col in df.columns]
df.columns = nuevas_col
df.columns
Out[ ]:
Index(['encounter_id', 'patient_nbr', 'race', 'gender', 'age', 'weight',
       'admission_type_id', 'discharge_disposition_id', 'admission_source_id',
       'time_in_hospital', 'payer_code', 'medical_specialty',
       'num_lab_procedures', 'num_procedures', 'num_medications',
       'number_outpatient', 'number_emergency', 'number_inpatient', 'diag_1',
       'diag_2', 'diag_3', 'number_diagnoses', 'max_glu_serum', 'a1cresult',
       'metformin', 'repaglinide', 'nateglinide', 'chlorpropamide',
       'glimepiride', 'acetohexamide', 'glipizide', 'glyburide', 'tolbutamide',
       'pioglitazone', 'rosiglitazone', 'acarbose', 'miglitol', 'troglitazone',
       'tolazamide', 'examide', 'citoglipton', 'insulin',
       'glyburide_metformin', 'glipizide_metformin',
       'glimepiride_pioglitazone', 'metformin_rosiglitazone',
       'metformin_pioglitazone', 'change', 'diabetesmed', 'readmitted',
       'admission_source', 'admission_type', 'discharge_disposition'],
      dtype='object')

Estudiar el formato de las variables, ver cuáles se deberían modificar y modificarlas cuando se considere oportuno

Se observan los tipos de variables en las columnas, y se nota lo siguiente:

  • Aunque los diag_X tengan una gran mayoria de numeros, no deberian transformarsee a INT ya que hay casos suplementarios en los cuales el codigo puede arrancar con una V o una E.

  • Aunque AGE y WEIGHT en la vida real son numeros INT, aqui se los trata como rangos, por lo que esta bien que sean Object

  • Tambien se nota la presencia de valores None o "?", pero seran atendidos mas adelante

Se han revisado los tipos de datos de cada columna, chequeandolos contra las descripciones entregadas en la base de datos. Como conclusión, los valores tomados son correctos, aunque hay que estudiar la presencia de Nulos y valores atipicos

In [ ]:
df.dtypes
Out[ ]:
encounter_id                int32 
patient_nbr                 int32 
race                        object
gender                      object
age                         object
weight                      object
admission_type_id           int32 
discharge_disposition_id    int32 
admission_source_id         int32 
time_in_hospital            int32 
payer_code                  object
medical_specialty           object
num_lab_procedures          int32 
num_procedures              int32 
num_medications             int32 
number_outpatient           int32 
number_emergency            int32 
number_inpatient            int32 
diag_1                      object
diag_2                      object
diag_3                      object
number_diagnoses            int32 
max_glu_serum               object
a1cresult                   object
metformin                   object
repaglinide                 object
nateglinide                 object
chlorpropamide              object
glimepiride                 object
acetohexamide               object
glipizide                   object
glyburide                   object
tolbutamide                 object
pioglitazone                object
rosiglitazone               object
acarbose                    object
miglitol                    object
troglitazone                object
tolazamide                  object
examide                     object
citoglipton                 object
insulin                     object
glyburide_metformin         object
glipizide_metformin         object
glimepiride_pioglitazone    object
metformin_rosiglitazone     object
metformin_pioglitazone      object
change                      object
diabetesmed                 object
readmitted                  object
admission_source            object
admission_type              object
discharge_disposition       object
dtype: object

Se han revisado los valores unicos de las columnas categoricas para revisar que los tipos coincidan con las descripciones otorgadas.

In [ ]:
df_cat = df.select_dtypes(include = ['object'])

for i in df_cat.columns:
    
    print(df[i].value_counts())
Caucasian          76099
AfricanAmerican    19210
?                  2273 
Hispanic           2037 
Other              1506 
Asian              641  
Name: race, dtype: int64
Female             54708
Male               47055
Unknown/Invalid    3    
Name: gender, dtype: int64
[70-80)     26068
[60-70)     22483
[50-60)     17256
[80-90)     17197
[40-50)     9685 
[30-40)     3775 
[90-100)    2793 
[20-30)     1657 
[10-20)     691  
[0-10)      161  
Name: age, dtype: int64
?            98569
[75-100)     1336 
[50-75)      897  
[100-125)    625  
[125-150)    145  
[25-50)      97   
[0-25)       48   
[150-175)    35   
[175-200)    11   
>200         3    
Name: weight, dtype: int64
?     40256
MC    32439
HM    6274 
SP    5007 
BC    4655 
MD    3532 
CP    2533 
UN    2448 
CM    1937 
OG    1033 
PO    592  
DM    549  
CH    146  
WC    135  
OT    95   
MP    79   
SI    55   
FR    1    
Name: payer_code, dtype: int64
?                                   49949
InternalMedicine                    14635
Emergency/Trauma                    7565 
Family/GeneralPractice              7440 
Cardiology                          5352 
                                    ...  
Surgery-PlasticwithinHeadandNeck    1    
Proctology                          1    
SportsMedicine                      1    
Dermatology                         1    
Perinatology                        1    
Name: medical_specialty, Length: 73, dtype: int64
428    6862
414    6581
786    4016
410    3614
486    3508
       ... 
791    1   
957    1   
833    1   
804    1   
299    1   
Name: diag_1, Length: 717, dtype: int64
276     6752
428     6662
250     6071
427     5036
401     3736
        ... 
863     1   
E938    1   
212     1   
833     1   
E813    1   
Name: diag_2, Length: 749, dtype: int64
250     11555
401     8289 
276     5175 
428     4577 
427     3955 
        ...  
123     1    
841     1    
370     1    
E912    1    
E965    1    
Name: diag_3, Length: 790, dtype: int64
None    96420
Norm    2597 
>200    1485 
>300    1264 
Name: max_glu_serum, dtype: int64
None    84748
>8      8216 
Norm    4990 
>7      3812 
Name: a1cresult, dtype: int64
No        81778
Steady    18346
Up        1067 
Down      575  
Name: metformin, dtype: int64
No        100227
Steady    1384  
Up        110   
Down      45    
Name: repaglinide, dtype: int64
No        101063
Steady    668   
Up        24    
Down      11    
Name: nateglinide, dtype: int64
No        101680
Steady    79    
Up        6     
Down      1     
Name: chlorpropamide, dtype: int64
No        96575
Steady    4670 
Up        327  
Down      194  
Name: glimepiride, dtype: int64
No        101765
Steady    1     
Name: acetohexamide, dtype: int64
No        89080
Steady    11356
Up        770  
Down      560  
Name: glipizide, dtype: int64
No        91116
Steady    9274 
Up        812  
Down      564  
Name: glyburide, dtype: int64
No        101743
Steady    23    
Name: tolbutamide, dtype: int64
No        94438
Steady    6976 
Up        234  
Down      118  
Name: pioglitazone, dtype: int64
No        95401
Steady    6100 
Up        178  
Down      87   
Name: rosiglitazone, dtype: int64
No        101458
Steady    295   
Up        10    
Down      3     
Name: acarbose, dtype: int64
No        101728
Steady    31    
Down      5     
Up        2     
Name: miglitol, dtype: int64
No        101763
Steady    3     
Name: troglitazone, dtype: int64
No        101727
Steady    38    
Up        1     
Name: tolazamide, dtype: int64
No    101766
Name: examide, dtype: int64
No    101766
Name: citoglipton, dtype: int64
No        47383
Steady    30849
Down      12218
Up        11316
Name: insulin, dtype: int64
No        101060
Steady    692   
Up        8     
Down      6     
Name: glyburide_metformin, dtype: int64
No        101753
Steady    13    
Name: glipizide_metformin, dtype: int64
No        101765
Steady    1     
Name: glimepiride_pioglitazone, dtype: int64
No        101764
Steady    2     
Name: metformin_rosiglitazone, dtype: int64
No        101765
Steady    1     
Name: metformin_pioglitazone, dtype: int64
No    54755
Ch    47011
Name: change, dtype: int64
Yes    78363
No     23403
Name: diabetesmed, dtype: int64
NO     54864
>30    35545
<30    11357
Name: readmitted, dtype: int64
 Emergency Room                                               57494
 Physician Referral                                           29565
None                                                          6781 
Transfer from a hospital                                      3187 
 Transfer from another health care facility                   2264 
Clinic Referral                                               1104 
 Transfer from a Skilled Nursing Facility (SNF)               855  
HMO Referral                                                  187  
 Not Mapped                                                   161  
 Not Available                                                125  
 Court/Law Enforcement                                        16   
 Transfer from hospital inpt/same fac reslt in a sep claim    12   
 Transfer from critial access hospital                        8    
 Transfer from Ambulatory Surgery Center                      2    
 Extramural Birth                                             2    
Normal Delivery                                               2    
 Sick Baby                                                    1    
Name: admission_source, dtype: int64
Emergency        53990
Elective         18869
Urgent           18480
None             5291 
Not Available    4785 
Not Mapped       320  
Trauma Center    21   
Newborn          10   
Name: admission_type, dtype: int64
Discharged to home                                                                                           60234
Discharged/transferred to SNF                                                                                13954
Discharged/transferred to home with home health service                                                      12902
None                                                                                                         3691 
Discharged/transferred to another short term hospital                                                        2128 
Discharged/transferred to another rehab fac including rehab units of a hospital .                            1993 
Expired                                                                                                      1642 
Discharged/transferred to another type of inpatient care institution                                         1184 
Not Mapped                                                                                                   989  
Discharged/transferred to ICF                                                                                815  
Left AMA                                                                                                     623  
Discharged/transferred to a long term care hospital.                                                         412  
Hospice / home                                                                                               399  
Hospice / medical facility                                                                                   372  
Discharged/transferred/referred to a psychiatric hospital of psychiatric distinct part unit of a hospital    139  
Discharged/transferred to home under care of Home IV provider                                                108  
Discharged/transferred within this institution to Medicare approved swing bed                                63   
Discharged/transferred to a nursing facility certified under Medicaid but not certified under Medicare.      48   
Admitted as an inpatient to this hospital                                                                    21   
Discharged/transferred/referred to this institution for outpatient services                                  14   
Discharged/transferred/referred another institution for outpatient services                                  11   
Expired at home. Medicaid only, hospice.                                                                     8    
Neonate discharged to another hospital for neonatal aftercare                                                6    
Discharged/transferred to a federal health care facility.                                                    5    
Still patient or expected to return for outpatient services                                                  3    
Expired in a medical facility. Medicaid only, hospice.                                                       2    
Name: discharge_disposition, dtype: int64

Estudiar si hay registros repetidos

  • Agrupando por el id de cada paciente, vemos que hay 16.773 registros duplicados.
  • Agrupando por el encounter id unico de cada registro, vemos que no hay duplicados
In [ ]:
pacientes_repetidos = df.groupby('patient_nbr')['patient_nbr'].agg(['count'])

pacientes_repetidos[pacientes_repetidos['count']>1].shape
Out[ ]:
(16773, 1)
In [ ]:
admision_repetidos = df.groupby('encounter_id')['encounter_id'].agg(['count'])

admision_repetidos[admision_repetidos['count']>1].shape
Out[ ]:
(0, 1)

Estudiar si hay variables que siempre o prácticamente siempre toman el mismo valor: tomar siempre el mismo valor no aporta información al modelo

Se ha separado este analisis en 3:

1) Analizar la importancia de las columnas cuyos datos son las medicaciones. Estas son 23, y notamos que unicamente 7 de ellas no cuentan con "No" en 100000 o mas registros. Consideramos que estas 7 son las que pueden considerarse imporantes para el modelo.

In [ ]:
medicamentos = df.iloc[:,24:47] #creo data frame para columnas de medicamentos

medicamentos_conteo = pd.DataFrame().reset_index() #creo data frame vacio

#loop para corroborar los conteos de No's

for columna in medicamentos.columns:
    data = pd.DataFrame(medicamentos[columna].value_counts()).reset_index()
    medicamentos_conteo = pd.merge(data,medicamentos_conteo,how='outer',on='index')   
    

#Arreglo formato del dataframe y transpongo para visualizarlo mas facilmente

medicamentos_conteo.set_index('index',inplace=True)
medicamentos_conteo=medicamentos_conteo.transpose()
medicamentos_conteo
Out[ ]:
index No Steady Up Down
metformin_pioglitazone 101765.0 1.0 NaN NaN
metformin_rosiglitazone 101764.0 2.0 NaN NaN
glimepiride_pioglitazone 101765.0 1.0 NaN NaN
glipizide_metformin 101753.0 13.0 NaN NaN
glyburide_metformin 101060.0 692.0 8.0 6.0
insulin 47383.0 30849.0 11316.0 12218.0
citoglipton 101766.0 NaN NaN NaN
examide 101766.0 NaN NaN NaN
tolazamide 101727.0 38.0 1.0 NaN
troglitazone 101763.0 3.0 NaN NaN
miglitol 101728.0 31.0 2.0 5.0
acarbose 101458.0 295.0 10.0 3.0
rosiglitazone 95401.0 6100.0 178.0 87.0
pioglitazone 94438.0 6976.0 234.0 118.0
tolbutamide 101743.0 23.0 NaN NaN
glyburide 91116.0 9274.0 812.0 564.0
glipizide 89080.0 11356.0 770.0 560.0
acetohexamide 101765.0 1.0 NaN NaN
glimepiride 96575.0 4670.0 327.0 194.0
chlorpropamide 101680.0 79.0 6.0 1.0
nateglinide 101063.0 668.0 24.0 11.0
repaglinide 100227.0 1384.0 110.0 45.0
metformin 81778.0 18346.0 1067.0 575.0
In [ ]:
no_aporta = medicamentos_conteo[medicamentos_conteo['No']>100000]
print(no_aporta.shape)
no_aporta
(16, 4)
Out[ ]:
index No Steady Up Down
metformin_pioglitazone 101765.0 1.0 NaN NaN
metformin_rosiglitazone 101764.0 2.0 NaN NaN
glimepiride_pioglitazone 101765.0 1.0 NaN NaN
glipizide_metformin 101753.0 13.0 NaN NaN
glyburide_metformin 101060.0 692.0 8.0 6.0
citoglipton 101766.0 NaN NaN NaN
examide 101766.0 NaN NaN NaN
tolazamide 101727.0 38.0 1.0 NaN
troglitazone 101763.0 3.0 NaN NaN
miglitol 101728.0 31.0 2.0 5.0
acarbose 101458.0 295.0 10.0 3.0
tolbutamide 101743.0 23.0 NaN NaN
acetohexamide 101765.0 1.0 NaN NaN
chlorpropamide 101680.0 79.0 6.0 1.0
nateglinide 101063.0 668.0 24.0 11.0
repaglinide 100227.0 1384.0 110.0 45.0

2) Para el resto de las variables categoricas, se creo una funcion para devolver en formato DataFrame los value counts, y luego se ha generado un loop para que lo devuelva para cada variable categorica que no sean medicamentos, y se han seleccionado aquellas con valores repetidos en muchas ocasiones.

In [ ]:
def valores_unicos(columna):
    data_frame = pd.DataFrame(df[columna].value_counts())
    data_frame['share']=data_frame[columna]/sum(data_frame[columna])*100 
    return data_frame
In [ ]:
#Funcion para mostrar DataFrames lado por lado
#fuente: https://stackoverflow.com/questions/38783027/jupyter-notebook-display-two-pandas-tables-side-by-side

from IPython.display import display_html
from itertools import chain,cycle
def display_side_by_side(*args,titles=cycle([''])):
    html_str=''
    for df,title in zip(args, chain(titles,cycle(['</br>'])) ):
        html_str+='<th style="text-align:center"><td style="vertical-align:top">'
        html_str+=f'<h2>{title}</h2>'
        html_str+=df.to_html().replace('table','table style="display:inline"')
        html_str+='</td></th>'
    display_html(html_str,raw=True)
In [ ]:
no_medicamentos = df_cat.iloc[:,0:11] + df_cat.iloc[:,34:37]

for col in no_medicamentos.columns:
    display_side_by_side(valores_unicos(col).head(),titles = [col])

a1cresult

a1cresult share
None 84748 83.277322
>8 8216 8.073423
Norm 4990 4.903406
>7 3812 3.745848

age

age share
[70-80) 26068 25.615628
[60-70) 22483 22.092840
[50-60) 17256 16.956547
[80-90) 17197 16.898571
[40-50) 9685 9.516931

change

change share
No 54755 53.804807
Ch 47011 46.195193

diabetesmed

diabetesmed share
Yes 78363 77.003125
No 23403 22.996875

diag_1

diag_1 share
428 6862 6.742920
414 6581 6.466796
786 4016 3.946308
410 3614 3.551284
486 3508 3.447124

diag_2

diag_2 share
276 6752 6.634829
428 6662 6.546391
250 6071 5.965647
427 5036 4.948608
401 3736 3.671167

diag_3

diag_3 share
250 11555 11.354480
401 8289 8.145157
276 5175 5.085195
428 4577 4.497573
427 3955 3.886367

gender

gender share
Female 54708 53.758623
Male 47055 46.238429
Unknown/Invalid 3 0.002948

max_glu_serum

max_glu_serum share
None 96420 94.746772
Norm 2597 2.551933
>200 1485 1.459230
>300 1264 1.242065

medical_specialty

medical_specialty share
? 49949 49.082208
InternalMedicine 14635 14.381031
Emergency/Trauma 7565 7.433720
Family/GeneralPractice 7440 7.310890
Cardiology 5352 5.259124

payer_code

payer_code share
? 40256 39.557416
MC 32439 31.876069
HM 6274 6.165124
SP 5007 4.920111
BC 4655 4.574219

race

race share
Caucasian 76099 74.778413
AfricanAmerican 19210 18.876639
? 2273 2.233555
Hispanic 2037 2.001651
Other 1506 1.479866

readmitted

readmitted share
NO 54864 53.911916
>30 35545 34.928169
<30 11357 11.159916

weight

weight share
? 98569 96.858479
[75-100) 1336 1.312816
[50-75) 897 0.881434
[100-125) 625 0.614154
[125-150) 145 0.142484

Columna weight: notamos que alrededor de 100K de registros tienen dato faltante, por lo que no estaria aportando informacion al modelo

Columna max_glu_serum: notamos que alrededor de 100K de registros tienen dato None, por lo que no estaria aportando informacion al modelo

Columna a1cresult: notamos que alrededor de 85K de registros tienen dato faltante, no deberia descartarse de entrada, pero en la ejecucion del modelo podria probarse el score con y sin esta variable.

3) Para las columnas numericas, se decidio analizar los datos estadisticos a traves de la funcion describe

Se observa que las columnas number_outpatient, number_emergency, number_inpatient tienen una gran concentracion hasta el tercer (o segundo) cuartil de valores identicos, por lo que no aportaría tanta informacion al modelo.

In [ ]:
df.select_dtypes(include = ['int32']).iloc[:,5:].describe()
Out[ ]:
time_in_hospital num_lab_procedures num_procedures num_medications number_outpatient number_emergency number_inpatient number_diagnoses
count 101766.000000 101766.000000 101766.000000 101766.000000 101766.000000 101766.000000 101766.000000 101766.000000
mean 4.395987 43.095641 1.339730 16.021844 0.369357 0.197836 0.635566 7.422607
std 2.985108 19.674362 1.705807 8.127566 1.267265 0.930472 1.262863 1.933600
min 1.000000 1.000000 0.000000 1.000000 0.000000 0.000000 0.000000 1.000000
25% 2.000000 31.000000 0.000000 10.000000 0.000000 0.000000 0.000000 6.000000
50% 4.000000 44.000000 1.000000 15.000000 0.000000 0.000000 0.000000 8.000000
75% 6.000000 57.000000 2.000000 20.000000 0.000000 0.000000 1.000000 9.000000
max 14.000000 132.000000 6.000000 81.000000 42.000000 76.000000 21.000000 16.000000

Estudiar los nulos en el dataset

No hay NAs

In [ ]:
for col in df.columns:
    if(df[col].isna().sum() >0):
        print(col,"con",df[col].isna().sum(),"nulos.")
    else: print(col, '-> No hay valores nulos')
encounter_id -> No hay valores nulos
patient_nbr -> No hay valores nulos
race -> No hay valores nulos
gender -> No hay valores nulos
age -> No hay valores nulos
weight -> No hay valores nulos
admission_type_id -> No hay valores nulos
discharge_disposition_id -> No hay valores nulos
admission_source_id -> No hay valores nulos
time_in_hospital -> No hay valores nulos
payer_code -> No hay valores nulos
medical_specialty -> No hay valores nulos
num_lab_procedures -> No hay valores nulos
num_procedures -> No hay valores nulos
num_medications -> No hay valores nulos
number_outpatient -> No hay valores nulos
number_emergency -> No hay valores nulos
number_inpatient -> No hay valores nulos
diag_1 -> No hay valores nulos
diag_2 -> No hay valores nulos
diag_3 -> No hay valores nulos
number_diagnoses -> No hay valores nulos
max_glu_serum -> No hay valores nulos
a1cresult -> No hay valores nulos
metformin -> No hay valores nulos
repaglinide -> No hay valores nulos
nateglinide -> No hay valores nulos
chlorpropamide -> No hay valores nulos
glimepiride -> No hay valores nulos
acetohexamide -> No hay valores nulos
glipizide -> No hay valores nulos
glyburide -> No hay valores nulos
tolbutamide -> No hay valores nulos
pioglitazone -> No hay valores nulos
rosiglitazone -> No hay valores nulos
acarbose -> No hay valores nulos
miglitol -> No hay valores nulos
troglitazone -> No hay valores nulos
tolazamide -> No hay valores nulos
examide -> No hay valores nulos
citoglipton -> No hay valores nulos
insulin -> No hay valores nulos
glyburide_metformin -> No hay valores nulos
glipizide_metformin -> No hay valores nulos
glimepiride_pioglitazone -> No hay valores nulos
metformin_rosiglitazone -> No hay valores nulos
metformin_pioglitazone -> No hay valores nulos
change -> No hay valores nulos
diabetesmed -> No hay valores nulos
readmitted -> No hay valores nulos
admission_source -> No hay valores nulos
admission_type -> No hay valores nulos
discharge_disposition -> No hay valores nulos

Sin embargo, hay valores que deberian considerarse como Nulos

In [ ]:
nulos = df.replace('?',np.nan)

for col in nulos.columns:
    if(nulos[col].isna().sum() >0):
        print(col,"->",nulos[col].isna().sum(),"nulos.")
race -> 2273 nulos.
weight -> 98569 nulos.
payer_code -> 40256 nulos.
medical_specialty -> 49949 nulos.
diag_1 -> 21 nulos.
diag_2 -> 358 nulos.
diag_3 -> 1423 nulos.

Preparamos un dataframe sin Nulos para contrastar con el df original en los ultimos ejercicios

Comprobamos correcta descarga y lectura de datos

Reemplazamos, para las columnas con pocos valores nulos, con la Moda

In [ ]:
inputar = ['race','diag_1','diag_2','diag_3']
for col in inputar:
    nulos[col].fillna(nulos[col].mode()[0], inplace=True)

for col in nulos.columns:
    if(nulos[col].isna().sum() >0):
        print(col,"->",nulos[col].isna().sum(),"nulos.")
weight -> 98569 nulos.
payer_code -> 40256 nulos.
medical_specialty -> 49949 nulos.

Estudiar los outliers

Separamos las variables numericas en un data frame nuevo, sin contar los IDs

In [ ]:
df_num = df.select_dtypes(include = ['int32'])
df_num=df_num.drop(columns=['encounter_id','patient_nbr','admission_type_id','discharge_disposition_id','admission_source_id'])
df_num.head()
Out[ ]:
time_in_hospital num_lab_procedures num_procedures num_medications number_outpatient number_emergency number_inpatient number_diagnoses
0 1 41 0 1 0 0 0 1
1 3 59 0 18 0 0 0 9
2 2 11 5 13 2 0 1 6
3 2 44 1 16 0 0 0 7
4 1 51 0 8 0 0 0 5

Evaluo normalidad para saber que test de outlier aplicar

In [ ]:
def is_normal(variable, alpha=0.05):
    
    # get std, mean
    mean = variable.mean()
    std = variable.std()
    # run kstest and get pvalue
    pvalue = kstest(variable, 'norm', args=(mean, std)).pvalue
    # check if pvalue is higher than alpha
    return pvalue >= alpha

normal_cols = []
non_normal_cols = []

for col in df_num.columns:
    normal = is_normal(df[col])
    if normal:
        normal_cols.append(col)
    else:
        non_normal_cols.append(col)

Todas las variables numericas siguen una distribucion que no es normal

In [ ]:
print(non_normal_cols)
normal_cols
['time_in_hospital', 'num_lab_procedures', 'num_procedures', 'num_medications', 'number_outpatient', 'number_emergency', 'number_inpatient', 'number_diagnoses']
Out[ ]:
[]

Las variables no siguen una distribucion normal, pero si lo hubieran sido, se hubieran evaluado sus outliers de la siguiente manera:

In [ ]:
from scipy.stats import kstest
from scipy import stats

for col in df_num.columns:
    mean = df_num[col].mean()
    std = df_num[col].std()
    mean, std
    norm_data = stats.norm.rvs(loc=mean, scale=std, size=100000)
    plt.figure(figsize=(6,3))
    sns.distplot(df_num[col], kde=False, norm_hist=True)
    sns.distplot(norm_data, kde=False, norm_hist=True)
    min_thresh = mean - 3 * std
    max_thresh = mean + 3 * std
    
    plt.figure(figsize=(6, 3))

    sns.distplot(df_num[col], kde=False, hist_kws={'edgecolor': 'k'})

    plt.axvline(min_thresh, color='red', linewidth=2)
    plt.axvline(max_thresh, color='red', linewidth=2)
/var/sds/packages/lib/python3.6/site-packages/seaborn/distributions.py:2619: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms).
  warnings.warn(msg, FutureWarning)

Se genera una funcion para devolver un dataframe sin outliers

In [ ]:
def remove_normal_outliers(df, col_name):
    """
    Función para eliminar outliers de una distribución
    de datos normal
    """
    
    # get mean, std
    mean = df[col_name].mean()
    std = df[col_name].std()
    
    # get thresholds
    min_thresh = mean - 3 * std
    max_thresh = mean + 3 * std
    
    # filter dataframe
    df_no_out = df[(df[col_name] >= min_thresh) & 
                   (df[col_name] <= max_thresh)]
    
    # return filtered dataframe
    return df_no_out

Se aplica la funcion a cada columna, devolviendo el % de outliers

In [ ]:
for col in df_num:
    
    df_no_out = remove_normal_outliers(df, col)
    nrows = len(df)
    nrow_no_out = len(df_no_out)
    
    n_outliers = nrows - nrow_no_out
    perc_outliers = round(100 * n_outliers / nrows, 2)
    
    print('{0} has {1}% outliers'.format(col, perc_outliers))
time_in_hospital has 1.02% outliers
num_lab_procedures has 0.04% outliers
num_procedures has 0.0% outliers
num_medications has 1.34% outliers
number_outpatient has 1.43% outliers
number_emergency has 1.64% outliers
number_inpatient has 1.98% outliers
number_diagnoses has 0.28% outliers

Como en realidad no siguen una distribucion normal, se evalua a traves de TUKEY

Test de Tukey para valores atípicos

Cuando la distribución de datos es no normal, una forma muy sencilla de eliminar outliers de forma analítica es aplicando el test de Tukey. Éste dice que se considera valor atípico a todo aquel que esté fuera del siguiente rango:

  • Valor atípico: $ Q_1 - 1.5\cdot IQR > x > Q_3 + 1.5 \cdot IQR$
  • Valor atípico extremo: $Q_1 - 3\cdot IQR > x >Q_3 + 3\cdot IQR$
In [ ]:
def tukey_outliers(df,column,extreme=False):
    q1, q3 = np.percentile(df[column],[25,75])
    iqr = q3 - q1
    constant = 1.5 if not extreme else 3
    return df[~((df[column]>(q3+constant*iqr)) | (df[column]<(q1-constant*iqr)))] #la onda significa que no te trae eso, todo menos eso te trae.

Creamos un loop para devolver el % de outliers segun la evaluacion de cada columna

In [ ]:
for columna in df_num.columns:
    outliers = round((1 - len(tukey_outliers(df_num,columna,extreme=False))/len(df_num))*100,2)
    print('Outliers en columna',columna,':',outliers,'%')
Outliers en columna time_in_hospital : 2.21 %
Outliers en columna num_lab_procedures : 0.14 %
Outliers en columna num_procedures : 4.87 %
Outliers en columna num_medications : 2.51 %
Outliers en columna number_outpatient : 16.45 %
Outliers en columna number_emergency : 11.19 %
Outliers en columna number_inpatient : 6.93 %
Outliers en columna number_diagnoses : 0.28 %

Se crea un df "limpio" sin outliers para poder contrastar las distribuciones con box plots debajo

In [ ]:
df_limpio = df_num

for columna in df_num.columns:
    sin_outliers = tukey_outliers(df_limpio,columna,extreme=False)
    df_limpio=sin_outliers

df_limpio
Out[ ]:
time_in_hospital num_lab_procedures num_procedures num_medications number_outpatient number_emergency number_inpatient number_diagnoses
1 3 59 0 18 0 0 0 9
3 2 44 1 16 0 0 0 7
4 1 51 0 8 0 0 0 5
6 4 70 1 21 0 0 0 7
7 5 73 0 12 0 0 0 8
... ... ... ... ... ... ... ... ...
101757 5 21 1 16 0 0 1 9
101761 3 51 0 16 0 0 0 9
101762 5 33 3 18 0 0 1 9
101764 10 45 2 21 0 0 1 9
101765 6 13 3 3 0 0 0 9

66592 rows × 8 columns

Creamos una funcion para comparar los boxplots con y sin outliers

In [ ]:
def plot_outliers(column):
    plt.figure(figsize = (12,5))
    plt.subplot(1,2,1)
    
    ax = sns.boxplot(x=df_num[column]) #originales
    
    plt.title('Con outliers')

    temp = tukey_outliers(df_num,column,extreme=False) #sin outliers
    plt.subplot(1,2,2)
    ax2= sns.boxplot(x=temp[column])
    plt.title('Sin outliers')
In [ ]:
for i in df_num.columns:
    plot_outliers(i)

Estudiar que todas las variables ahora sí tengan el formato y contenido adecuado. si todavía no, arreglarlas.

Las variables tienen el formato correcto

In [ ]:
nulos.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 101766 entries, 0 to 101765
Data columns (total 53 columns):
 #   Column                    Non-Null Count   Dtype 
---  ------                    --------------   ----- 
 0   encounter_id              101766 non-null  int32 
 1   patient_nbr               101766 non-null  int32 
 2   race                      101766 non-null  object
 3   gender                    101766 non-null  object
 4   age                       101766 non-null  object
 5   weight                    3197 non-null    object
 6   admission_type_id         101766 non-null  int32 
 7   discharge_disposition_id  101766 non-null  int32 
 8   admission_source_id       101766 non-null  int32 
 9   time_in_hospital          101766 non-null  int32 
 10  payer_code                61510 non-null   object
 11  medical_specialty         51817 non-null   object
 12  num_lab_procedures        101766 non-null  int32 
 13  num_procedures            101766 non-null  int32 
 14  num_medications           101766 non-null  int32 
 15  number_outpatient         101766 non-null  int32 
 16  number_emergency          101766 non-null  int32 
 17  number_inpatient          101766 non-null  int32 
 18  diag_1                    101766 non-null  object
 19  diag_2                    101766 non-null  object
 20  diag_3                    101766 non-null  object
 21  number_diagnoses          101766 non-null  int32 
 22  max_glu_serum             101766 non-null  object
 23  a1cresult                 101766 non-null  object
 24  metformin                 101766 non-null  object
 25  repaglinide               101766 non-null  object
 26  nateglinide               101766 non-null  object
 27  chlorpropamide            101766 non-null  object
 28  glimepiride               101766 non-null  object
 29  acetohexamide             101766 non-null  object
 30  glipizide                 101766 non-null  object
 31  glyburide                 101766 non-null  object
 32  tolbutamide               101766 non-null  object
 33  pioglitazone              101766 non-null  object
 34  rosiglitazone             101766 non-null  object
 35  acarbose                  101766 non-null  object
 36  miglitol                  101766 non-null  object
 37  troglitazone              101766 non-null  object
 38  tolazamide                101766 non-null  object
 39  examide                   101766 non-null  object
 40  citoglipton               101766 non-null  object
 41  insulin                   101766 non-null  object
 42  glyburide_metformin       101766 non-null  object
 43  glipizide_metformin       101766 non-null  object
 44  glimepiride_pioglitazone  101766 non-null  object
 45  metformin_rosiglitazone   101766 non-null  object
 46  metformin_pioglitazone    101766 non-null  object
 47  change                    101766 non-null  object
 48  diabetesmed               101766 non-null  object
 49  readmitted                101766 non-null  object
 50  admission_source          101766 non-null  object
 51  admission_type            101766 non-null  object
 52  discharge_disposition     101766 non-null  object
dtypes: int32(13), object(40)
memory usage: 41.9+ MB

Antes de estudiar el comportamiento de las variables, veremos si hay alguna variable derivada a crear

Previo al analisis, unimos los dataframes previamente separados para evaluar nulos y outliers

In [ ]:
df_ids = df[['encounter_id','patient_nbr','admission_type_id','discharge_disposition_id','admission_source_id']].reset_index()
df_nulos = nulos.select_dtypes(include = ['object'])
df_nulos.reset_index(inplace=True)
df_limpio.reset_index(inplace=True)
In [ ]:
df_final = pd.merge(df_limpio,df_ids,how='left',on='index')
df_final = pd.merge(df_final,df_nulos,how='left',on='index')
df_final
Out[ ]:
index time_in_hospital num_lab_procedures num_procedures num_medications number_outpatient number_emergency number_inpatient number_diagnoses encounter_id ... glipizide_metformin glimepiride_pioglitazone metformin_rosiglitazone metformin_pioglitazone change diabetesmed readmitted admission_source admission_type discharge_disposition
0 1 3 59 0 18 0 0 0 9 149190 ... No No No No Ch Yes >30 Emergency Room Emergency Discharged to home
1 3 2 44 1 16 0 0 0 7 500364 ... No No No No Ch Yes NO Emergency Room Emergency Discharged to home
2 4 1 51 0 8 0 0 0 5 16680 ... No No No No Ch Yes NO Emergency Room Emergency Discharged to home
3 6 4 70 1 21 0 0 0 7 55842 ... No No No No Ch Yes NO Clinic Referral Elective Discharged to home
4 7 5 73 0 12 0 0 0 8 63768 ... No No No No No Yes >30 Emergency Room Emergency Discharged to home
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
66587 101757 5 21 1 16 0 0 1 9 443842136 ... No No No No No Yes NO Emergency Room Emergency Discharged to home
66588 101761 3 51 0 16 0 0 0 9 443847548 ... No No No No Ch Yes >30 Emergency Room Emergency Discharged/transferred to SNF
66589 101762 5 33 3 18 0 0 1 9 443847782 ... No No No No No Yes NO Transfer from a Skilled Nursing Facility (SNF) Emergency Discharged/transferred to ICF
66590 101764 10 45 2 21 0 0 1 9 443857166 ... No No No No Ch Yes NO Emergency Room Urgent Discharged/transferred to SNF
66591 101765 6 13 3 3 0 0 0 9 443867222 ... No No No No No No NO Emergency Room Emergency Discharged to home

66592 rows × 54 columns

In [ ]:
df_final.shape
Out[ ]:
(66592, 54)

Correctamente, solo hay valores nulos en las que decidimos dejar de esa manera

In [ ]:
df_final.isna().sum()
Out[ ]:
index                       0    
time_in_hospital            0    
num_lab_procedures          0    
num_procedures              0    
num_medications             0    
number_outpatient           0    
number_emergency            0    
number_inpatient            0    
number_diagnoses            0    
encounter_id                0    
patient_nbr                 0    
admission_type_id           0    
discharge_disposition_id    0    
admission_source_id         0    
race                        0    
gender                      0    
age                         0    
weight                      65156
payer_code                  28855
medical_specialty           31049
diag_1                      0    
diag_2                      0    
diag_3                      0    
max_glu_serum               0    
a1cresult                   0    
metformin                   0    
repaglinide                 0    
nateglinide                 0    
chlorpropamide              0    
glimepiride                 0    
acetohexamide               0    
glipizide                   0    
glyburide                   0    
tolbutamide                 0    
pioglitazone                0    
rosiglitazone               0    
acarbose                    0    
miglitol                    0    
troglitazone                0    
tolazamide                  0    
examide                     0    
citoglipton                 0    
insulin                     0    
glyburide_metformin         0    
glipizide_metformin         0    
glimepiride_pioglitazone    0    
metformin_rosiglitazone     0    
metformin_pioglitazone      0    
change                      0    
diabetesmed                 0    
readmitted                  0    
admission_source            0    
admission_type              0    
discharge_disposition       0    
dtype: int64

Creamos version 2 de Age para pasar a numerica, tomando el valor medio de los rangos.

De esta manera, se deja el df_final como aquel sin Outliers, sin Nulos en las columans indicadas, y con todas las columnas originales, sumada Age_v2

In [ ]:
df_final['age_v2'] = df_final['age'].replace({'[70-80)':75, '[60-70)':65, '[50-60)':55,'[80-90)':85, '[40-50)':45, '[30-40)':35, '[20-30)':25,'[10-20)':15, '[0-10)':5,'[90-100)':95})
In [ ]:
df_final['age_v2']
Out[ ]:
0        15
1        35
2        45
3        65
4        75
         ..
66587    75
66588    75
66589    85
66590    85
66591    75
Name: age_v2, Length: 66592, dtype: int64

Exploratory Data Analysis

Crear la matriz de correlación

Primero se ha corrido la matriz de correlacion para el data frame final, sin Outliers y sin Nulos.

Sin embargo, se nota la presencia de NAs:

Esta es causada por el valor = 0 de la varianza de las columnas number_outpatient y number_emergency. Como la gran mayoria de sus datos originales eran igual a 0, al eliminar los outliers, 0 queda como el unico valor posible, causando que la varianza/desvió sea 0 y que el denominador de la matriz corr tambien lo sea, causando un error

In [ ]:
#data frame final

df_final_num = df_final.select_dtypes(include = ['int32','int','int64']).drop(columns=['index','encounter_id','patient_nbr','admission_type_id','discharge_disposition_id','admission_source_id'])
df_final_num.corr()
Out[ ]:
time_in_hospital num_lab_procedures num_procedures num_medications number_outpatient number_emergency number_inpatient number_diagnoses age_v2
time_in_hospital 1.000000 0.286915 0.129905 0.403312 NaN NaN 0.080621 0.210216 0.123872
num_lab_procedures 0.286915 1.000000 -0.011079 0.204867 NaN NaN 0.031188 0.126082 0.018446
num_procedures 0.129905 -0.011079 1.000000 0.274022 NaN NaN -0.043059 0.040807 -0.031565
num_medications 0.403312 0.204867 0.274022 1.000000 NaN NaN 0.067013 0.273466 0.070538
number_outpatient NaN NaN NaN NaN NaN NaN NaN NaN NaN
number_emergency NaN NaN NaN NaN NaN NaN NaN NaN NaN
number_inpatient 0.080621 0.031188 -0.043059 0.067013 NaN NaN 1.000000 0.100966 0.050586
number_diagnoses 0.210216 0.126082 0.040807 0.273466 NaN NaN 0.100966 1.000000 0.253681
age_v2 0.123872 0.018446 -0.031565 0.070538 NaN NaN 0.050586 0.253681 1.000000

Por este motivo, se deja plasmada la matriz de correlacion para los datos numericos originales.

In [ ]:
#data frame original

df_num.corr()
Out[ ]:
time_in_hospital num_lab_procedures num_procedures num_medications number_outpatient number_emergency number_inpatient number_diagnoses
time_in_hospital 1.000000 0.318450 0.191472 0.466135 -0.008916 -0.009681 0.073623 0.220186
num_lab_procedures 0.318450 1.000000 0.058066 0.268161 -0.007602 -0.002279 0.039231 0.152773
num_procedures 0.191472 0.058066 1.000000 0.385767 -0.024819 -0.038179 -0.066236 0.073734
num_medications 0.466135 0.268161 0.385767 1.000000 0.045197 0.013180 0.064194 0.261526
number_outpatient -0.008916 -0.007602 -0.024819 0.045197 1.000000 0.091459 0.107338 0.094152
number_emergency -0.009681 -0.002279 -0.038179 0.013180 0.091459 1.000000 0.266559 0.055539
number_inpatient 0.073623 0.039231 -0.066236 0.064194 0.107338 0.266559 1.000000 0.104710
number_diagnoses 0.220186 0.152773 0.073734 0.261526 0.094152 0.055539 0.104710 1.000000

Pintar la matriz de correlación

1) Matrix de correlacion para df_final sin nulos ni outliers

In [ ]:
df_numerical_corr = df_final_num.corr(method='spearman')

fig, ax = plt.subplots(figsize=(15,10))
sns.heatmap(df_numerical_corr, xticklabels=list(df_numerical_corr), yticklabels=list(df_numerical_corr),
            annot=True, fmt='.1f', linewidths = 0.5, cmap="Spectral", ax=ax)
Out[ ]:
<AxesSubplot:>

2) Matrix de correlacion para df original, con nulos y outliers

In [ ]:
df_numerical_corr = df_num.corr(method='spearman')

mask = np.zeros_like(df_numerical_corr)
mask[np.triu_indices_from(mask)] = False

fig, ax = plt.subplots(figsize=(15,10))
sns.heatmap(df_numerical_corr, xticklabels=list(df_numerical_corr), yticklabels=list(df_numerical_corr),
            annot=True, fmt='.1f', linewidths = 0.5, cmap="Spectral", ax=ax,mask=mask)
Out[ ]:
<AxesSubplot:>

Como la matriz es simétrica, se puede observar solo la primera diagonal

In [ ]:
def Generate_heatmap_graph(corr, chart_title, mask_uppertri=False ):
    
    mask = np.zeros_like(corr)
    mask[np.triu_indices_from(mask)] = mask_uppertri
    fig,ax = plt.subplots(figsize=(12,12))
    ax.set_facecolor('white')
    sns.heatmap(corr
                , mask = mask
                , square = True
                , annot = True
                , annot_kws={'size': 10.5, 'weight' : 'bold'}
                , cmap=plt.get_cmap("YlOrBr")
                , linewidths=.1)
    plt.title(chart_title, fontsize=14)

    plt.show()
var_corr = round(df_num.corr(),2)
Generate_heatmap_graph(var_corr
                       ,chart_title = 'Correlation Heatmap'
                       ,mask_uppertri = True)

Determinar qué par de variables están correlacionadas

Observando la correlacion en el df orignal, vemos que tan solo 2 combinaciones de variables otorgan una correlacion mayor a 0,35, por lo que puede decirse que la correlacion es baja a lo largo de la base.

La correlacion mas alta es aquella de Numero de medicaciones con Tiempo en hospital, de 0,47.

La variable con mayor correlacion para el resto de las variables es Tiempo en hospital. Se puede notar tanto arriba como debajo que aparece en el top2 de todas las otras variables numericas del df

In [ ]:
for col in df_num.columns:
    df_numerical_corr=round(df_num.corr()[col].sort_values(ascending=False).iloc[1:3],2)
    print('***********',col, 'correlacionada con:***********\n',df_numerical_corr)
*********** time_in_hospital correlacionada con:***********
 num_medications       0.47
num_lab_procedures    0.32
Name: time_in_hospital, dtype: float64
*********** num_lab_procedures correlacionada con:***********
 time_in_hospital    0.32
num_medications     0.27
Name: num_lab_procedures, dtype: float64
*********** num_procedures correlacionada con:***********
 num_medications     0.39
time_in_hospital    0.19
Name: num_procedures, dtype: float64
*********** num_medications correlacionada con:***********
 time_in_hospital    0.47
num_procedures      0.39
Name: num_medications, dtype: float64
*********** number_outpatient correlacionada con:***********
 number_inpatient    0.11
number_diagnoses    0.09
Name: number_outpatient, dtype: float64
*********** number_emergency correlacionada con:***********
 number_inpatient     0.27
number_outpatient    0.09
Name: number_emergency, dtype: float64
*********** number_inpatient correlacionada con:***********
 number_emergency     0.27
number_outpatient    0.11
Name: number_inpatient, dtype: float64
*********** number_diagnoses correlacionada con:***********
 num_medications     0.26
time_in_hospital    0.22
Name: number_diagnoses, dtype: float64

Probamos la matriz de correlacion pasando a dummy a Race y Gender, pero los resultados son los mismos. Unicamente se observan correlacionadas a estas dos las distintas opciones de sus mismas variables.

Por otro lado, no se pasan a dummy las columnas de medicamentos y a que al ser alrededor de 100K = No, seria una constante.

In [ ]:
df_dum=pd.get_dummies(df, prefix=['A', 'D'], columns=['race', 'gender'])
In [ ]:
df_dum_num = df_dum.select_dtypes(include = ['int32','int','int64','uint8']).drop(columns=['encounter_id','patient_nbr','admission_type_id','discharge_disposition_id','admission_source_id'])
In [ ]:
def Generate_heatmap_graph(corr, chart_title, mask_uppertri=False ):
    
    mask = np.zeros_like(corr)
    mask[np.triu_indices_from(mask)] = mask_uppertri
    fig,ax = plt.subplots(figsize=(12,12))
    ax.set_facecolor('white')
    sns.heatmap(corr
                , mask = mask
                , square = True
                , annot = True
                , annot_kws={'size': 10.5, 'weight' : 'bold'}
                , cmap=plt.get_cmap("YlOrBr")
                , linewidths=.1)
    plt.title(chart_title, fontsize=14)

    plt.show()
var_corr = round(df_dum_num.corr(),2)
Generate_heatmap_graph(var_corr
                       ,chart_title = 'Correlation Heatmap'
                       ,mask_uppertri = True)