lunes, 30 de noviembre de 2020

Limpieza y Preprocesamiento de Datos para aplicaciones en Ciencia de Datos

heart_disease_data_preprocessing

Análisis de Enfermedades Cardiovasculares

El preprocesamiento de datos es uno de los pasos mas importantes a la hora de trabajar de manera eficiente. Es por eso que este post lo dedico a el paso a paso desde la extraccion y limpieza del dataset, pasando por la discretizacion de variables (OneHot Encoding), hasta finalmente el código para guardar el dataset limpio y procesado.

Se realiza un Análisis de Datos Exploratorio (EDA) no visual sobre el dataset de enfermedades cardiacas extraido de la página oficial de repositorios de Machine Learning de la Universidad de California Irvine UCI.

Descripción del conjunto de datos.

Descripción de los datos Hay 14 características:

1 'Edad' : Edad en años

continuo: unidades edad = años

2 'Sexo' |

categórico | 1 = hombre 0 = mujer

3 'Tipo de dolor en el pecho' |

categórico | 1 = angina típica 2 = angina atípica 3 = dolor no anginoso 4 = asintomático

4 'Presión arterial en reposo' (al ingreso en el hospital en mmHg) |

continuo |

5 'Colesterol sérico' (en mg / dl) |

continuo |

'Azúcar en sangre en ayunas' (> 120 mg / dl) |

categórico | 1 = verdadero 0 = falso

'Electrocardiográfico en reposo' |

categórico | 0 = normal 1 = onda ST-T anormal 2 = hipertrofia ventricular izquierda Estes)

'Frecuencia cardíaca máxima alcanzada durante el ejercicio' (en bpm) |

continuo |

'Angina inducida por ejercicio' |

categórico 1 = si 0 = no

'Depresión del ST inducida por el ejercicio en relación con el reposo' |

continuo |

'Pendiente del segmento ST de ejercicio pico' |

categórico 1 = pendiente ascendente 2 = plano 3 = descendente

Inclinación del segmento ST del EKG durante el ejercicio.

'# Embarcaciones principales coloreadas por flouroscopia' |

categórico | 0 = color de fluoroscopia 0 1 = color de fluoroscopia 1 2 = color de fluoroscopia 2 3 = color de fluoroscopia 3

'Talasemia' |

categórico | 3 = normal 6 = defecto fijo 7 = defecto reversible

'diagnóstico' |

categórico | 0 = Sin enfermedad x> 0 = Enfermedad

Importar librerias

Lo primero que se realiza es importar las librerias necesarias para realizar el análisis.

In [1]:
import pandas as pd
import numpy as np 
import matplotlib.pyplot as plt
import seaborn as sns

Definicion del archivo y nombres de columnas

In [2]:
#url="http://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/processed.cleveland.data"
file='processed.cleveland.data'
nombresCol=['Edad',
          'Sexo',
          'Tipo dolor de pecho',
           'Presión arterial en reposo',
           'Colesterol',
           'Glucemia',
           'Electrocardiograma',
           'Frecuencia cardíaca máxima',
           'Angina inducida',
           'Depresión del ST',
           'Pendiente máxima del segmento ST',
           '# Vasos principales coloreados por flouroscopia',
           'Talasemia',
           'Diagnóstico']

Carga de Datos

Se cargan los datos y se guardan en data:

In [3]:
data=pd.read_csv(file, names=nombresCol)

Analisis Exploratorio de Datos

Se observan las primeras 10 muestras del dataset:

In [4]:
data.head(10)
Out[4]:
Edad Sexo Tipo dolor de pecho Presión arterial en reposo Colesterol Glucemia Electrocardiograma Frecuencia cardíaca máxima Angina inducida Depresión del ST Pendiente máxima del segmento ST # Vasos principales coloreados por flouroscopia Talasemia Diagnóstico
0 63.0 1.0 1.0 145.0 233.0 1.0 2.0 150.0 0.0 2.3 3.0 0.0 6.0 0
1 67.0 1.0 4.0 160.0 286.0 0.0 2.0 108.0 1.0 1.5 2.0 3.0 3.0 2
2 67.0 1.0 4.0 120.0 229.0 0.0 2.0 129.0 1.0 2.6 2.0 2.0 7.0 1
3 37.0 1.0 3.0 130.0 250.0 0.0 0.0 187.0 0.0 3.5 3.0 0.0 3.0 0
4 41.0 0.0 2.0 130.0 204.0 0.0 2.0 172.0 0.0 1.4 1.0 0.0 3.0 0
5 56.0 1.0 2.0 120.0 236.0 0.0 0.0 178.0 0.0 0.8 1.0 0.0 3.0 0
6 62.0 0.0 4.0 140.0 268.0 0.0 2.0 160.0 0.0 3.6 3.0 2.0 3.0 3
7 57.0 0.0 4.0 120.0 354.0 0.0 0.0 163.0 1.0 0.6 1.0 0.0 3.0 0
8 63.0 1.0 4.0 130.0 254.0 0.0 2.0 147.0 0.0 1.4 2.0 1.0 7.0 2
9 53.0 1.0 4.0 140.0 203.0 1.0 2.0 155.0 1.0 3.1 3.0 0.0 7.0 1

Tranformacion de la variable predictiva 'Diagnóstico' en binaria

El interes de este trabajo es identificar si predice o no una enfermedad cardiaca independientemente de cual sea el tipo de esta. Para ello, se transforma la columna 'Diagnóstico' para que presente solamente valores de 0 y 1.

In [5]:
data["Diagnóstico"]=np.where((data["Diagnóstico"]>0),1,0)

Inspeccion del tipo de datos

In [6]:
data.dtypes
Out[6]:
Edad                                               float64
Sexo                                               float64
Tipo dolor de pecho                                float64
Presión arterial en reposo                         float64
Colesterol                                         float64
Glucemia                                           float64
Electrocardiograma                                 float64
Frecuencia cardíaca máxima                         float64
Angina inducida                                    float64
Depresión del ST                                   float64
Pendiente máxima del segmento ST                   float64
# Vasos principales coloreados por flouroscopia     object
Talasemia                                           object
Diagnóstico                                          int32
dtype: object

Aparentemente las columnas 11 y 12 son numéricas pero data.dtypes muestra que son de tipo "object" lo que significa que dentro de estas columnas se encuentran valores de tipo 'cadena de texto' o Strings. Inpeccionando los valores únicos dentro de estas dos columnas se encuentra:

df['# Vasos principales coloreados por flouroscopia'].unique() df['Talasemia (normal, Corregir. Defecto, invertir. Defecto)'].unique()

In [7]:
for columna in data.iloc[:,11:13]:
    print(data[columna].value_counts())
0.0    176
1.0     65
2.0     38
3.0     20
?        4
Name: # Vasos principales coloreados por flouroscopia, dtype: int64
3.0    166
7.0    117
6.0     18
?        2
Name: Talasemia, dtype: int64

Así se encuentra que:

la columna 11'# Vasos principales coloreados por flouroscopia' y la columna 12 'Talasemia (normal, Corregir. Defecto, invertir. Defecto)' tienen 2 y 4 valores respectivamente escritos como "?"

Dado que son solo 6 valores en total y no es un numero significante de valores nulos se procede a eliminarlos.

Se revisa todas las caracteristicas para ver si se encuentran otro tipo de datos extraños.

for i in df.columns: print(df[i].value_counts())

todo parece encontrarse bien asi que se continua. El siguiente paso es tomar estas dos columnas y transformarlas a numericas. Para esto, primero se eliminan los 6 registros con valores nulos.

Se remplazan los valores '?' en las columnas por el valor nan

In [8]:
data=data.replace('?',np.nan)

Se procede a eliminar las muestras que presentan estos valores inválidos

In [9]:
data=data.dropna(axis=0)
In [10]:
data.isnull().sum()
Out[10]:
Edad                                               0
Sexo                                               0
Tipo dolor de pecho                                0
Presión arterial en reposo                         0
Colesterol                                         0
Glucemia                                           0
Electrocardiograma                                 0
Frecuencia cardíaca máxima                         0
Angina inducida                                    0
Depresión del ST                                   0
Pendiente máxima del segmento ST                   0
# Vasos principales coloreados por flouroscopia    0
Talasemia                                          0
Diagnóstico                                        0
dtype: int64
In [11]:
data['# Vasos principales coloreados por flouroscopia']=data['# Vasos principales coloreados por flouroscopia'].astype(str).astype(float).astype(int)
data['Talasemia']=data['Talasemia'].astype(str).astype(float).astype(int)
In [13]:
data.dtypes
Out[13]:
Edad                                               float64
Sexo                                               float64
Tipo dolor de pecho                                float64
Presión arterial en reposo                         float64
Colesterol                                         float64
Glucemia                                           float64
Electrocardiograma                                 float64
Frecuencia cardíaca máxima                         float64
Angina inducida                                    float64
Depresión del ST                                   float64
Pendiente máxima del segmento ST                   float64
# Vasos principales coloreados por flouroscopia      int32
Talasemia                                            int32
Diagnóstico                                          int32
dtype: object

Ingenieria de atributos

In [14]:
data.nunique()
Out[14]:
Edad                                                41
Sexo                                                 2
Tipo dolor de pecho                                  4
Presión arterial en reposo                          50
Colesterol                                         152
Glucemia                                             2
Electrocardiograma                                   3
Frecuencia cardíaca máxima                          91
Angina inducida                                      2
Depresión del ST                                    40
Pendiente máxima del segmento ST                     3
# Vasos principales coloreados por flouroscopia      4
Talasemia                                            3
Diagnóstico                                          2
dtype: int64
In [16]:
dolor_de_pecho_dummy=pd.get_dummies(data['Tipo dolor de pecho'],prefix='Tipo dolor de pecho')
electro_dummy=pd.get_dummies(data['Electrocardiograma'],prefix='Electrocardiograma')
pendiente_dummy=pd.get_dummies(data['Pendiente máxima del segmento ST'],prefix='Pendiente máxima del segmento ST')
fluo_dummy=pd.get_dummies(data['# Vasos principales coloreados por flouroscopia'], prefix='# Vasos principales coloreados por flouroscopia')
talasemia_dummy=pd.get_dummies(data['Talasemia'],prefix='Talasemia')
In [17]:
nueva_data=pd.concat([data,dolor_de_pecho_dummy,electro_dummy,pendiente_dummy,fluo_dummy,talasemia_dummy],axis=1)
In [19]:
# se eliminan las antiguas columnas
nueva_data=nueva_data.drop(['Tipo dolor de pecho','Electrocardiograma','Pendiente máxima del segmento ST','# Vasos principales coloreados por flouroscopia','Talasemia'], axis=1)
In [20]:
nueva_data.head()
Out[20]:
Edad Sexo Presión arterial en reposo Colesterol Glucemia Frecuencia cardíaca máxima Angina inducida Depresión del ST Diagnóstico Tipo dolor de pecho_1.0 ... Pendiente máxima del segmento ST_1.0 Pendiente máxima del segmento ST_2.0 Pendiente máxima del segmento ST_3.0 # Vasos principales coloreados por flouroscopia_0 # Vasos principales coloreados por flouroscopia_1 # Vasos principales coloreados por flouroscopia_2 # Vasos principales coloreados por flouroscopia_3 Talasemia_3 Talasemia_6 Talasemia_7
0 63.0 1.0 145.0 233.0 1.0 150.0 0.0 2.3 0 1 ... 0 0 1 1 0 0 0 0 1 0
1 67.0 1.0 160.0 286.0 0.0 108.0 1.0 1.5 1 0 ... 0 1 0 0 0 0 1 1 0 0
2 67.0 1.0 120.0 229.0 0.0 129.0 1.0 2.6 1 0 ... 0 1 0 0 0 1 0 0 0 1
3 37.0 1.0 130.0 250.0 0.0 187.0 0.0 3.5 0 0 ... 0 0 1 1 0 0 0 1 0 0
4 41.0 0.0 130.0 204.0 0.0 172.0 0.0 1.4 0 0 ... 1 0 0 1 0 0 0 1 0 0

5 rows × 26 columns

In [22]:
for i in nueva_data.columns:
    print(i)
Edad
Sexo
Presión arterial en reposo
Colesterol
Glucemia
Frecuencia cardíaca máxima
Angina inducida
Depresión del ST
Diagnóstico
Tipo dolor de pecho_1.0
Tipo dolor de pecho_2.0
Tipo dolor de pecho_3.0
Tipo dolor de pecho_4.0
Electrocardiograma_0.0
Electrocardiograma_1.0
Electrocardiograma_2.0
Pendiente máxima del segmento ST_1.0
Pendiente máxima del segmento ST_2.0
Pendiente máxima del segmento ST_3.0
# Vasos principales coloreados por flouroscopia_0
# Vasos principales coloreados por flouroscopia_1
# Vasos principales coloreados por flouroscopia_2
# Vasos principales coloreados por flouroscopia_3
Talasemia_3
Talasemia_6
Talasemia_7
In [23]:
nueva_data.to_csv('preprocessed_heart_data.csv', index=False)
In [ ]:
 
In [ ]: