La siguiente práctica muestra el estudio previo a una operación de machine learning de los datos relacionados. Para ello se aplican distíntas técnicas que intentan simplificar la tarea de aprendizaje, bien sea simplificando los datos o preprocesandolos.
Para ello, se ha desarrollado en Jupyter Notebook, una herramienta que permite la generación de resultados a través del lenguaje de programación python en un formato documento, que es el que se presenta.
Esta práctica emula la memoria de una auditoría cuyo cliente final es una empresa que nos ha encargado la realización de dicho estudio de datos.
El objetivo de este documento es el estudio, simplificación y preprocesamiento de un conjunto de datos de cara al uso de un algoritmo de machine learning.
En este apartado se describe el procesamiento realizado a los datos así como una breve explicación de los métodos aplicados.
In [2]:
#Libraries
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import matplotlib.pyplot as plt# Read Dataset
%matplotlib inline
Tenemos una base de datos médica con los resultados de análisis sanguineos de varios pacientes y una clasificación de su estado de salud, 1 para los enfermos y 2 para los sanos. En primer lugar tenemos los datos del paciente, su edad y su género. A continuación se nos dan los resultados del total de bilirrubina y bilirrubina directa, la bilirrubina es un pigmento amarillo que se encuentra en la bilis (liquido generado por el hígado), la bilirrubina total es la suma de la bilirrubina directa o conjugada y la bilirrubina indirecta.
Despues se proporciona los valores de la fosfatasa alcalina, que es una enzima que se desplaza a través de el sistema sanguíneo, pero que se acumula más que nada en el hígado, la bilis, riñones y el sistema intestinal, es útil para detectar enfermedades óseas o hepáticas.
Después están las transaminasas, la alaninoamino transferasa (ALT o GPT) y la aspartato aminotransferasa (AST o GOT), que son enzimas que se encuentran en el interior de las células de órganos como el hígado, el corazón, los riñones o los músculos, y desempeñan una importante función en el metabolismo. Cuando un análisis de sangre detecta niveles elevados de estas moléculas puede indicar que existe una lesión de las células hepáticas.
Las últimas tres variables son las proteinas totales, la albumina y el ratio de albumina y globulina. El examen de proteína total mide la cantidad total de dos clases de proteínas encontradas en la porción líquida de la sangre: albúmina y globulina.
La albúmina es una proteína plasmática cuya función más importante es el mantenimiento de la presión oncótica, es decir ayuda a impedir que se escape líquido fuera de los vasos sanguíneos, y la capacidad de transporte de hormonas, medicamentos,etc.
Las globulinas son útiles en la lucha contra las infecciones y mejorar el proceso de coagulación de la sangre. También sirven como portadora de la hormona y el transporte de las hormonas a diferentes partes del cuerpo.
Por los componentes analizados podemos concluir que es un perfil hepático para detectar problemas en el hígado.
In [3]:
#Path para linux
path = '../Recursos/indian_liver_patient.csv'
#Path para Windows
#path = '..\Recursos\indian_liver_patient.csv'
dataset = pd.read_csv('../Recursos/indian_liver_patient.csv',delimiter=',',header=0)
dataset
Out[3]:
In [4]:
# Header
header = []
for row in dataset:
header.append(row)
header
Out[4]:
In [5]:
dataset.replace("Male",0, True)
dataset.replace("Female",1,True)
dataset
Out[5]:
En este apartado vamos a desarrollar un breve estudio estadístico para los datos proporcionados. Para ello utilizaremos el método describe del objeto DataSet de la librería panda. Este método genéra un cálculo estadístico básico en el que nos describe por cada variable valores tales como la media, la desviación estandar y los percentiles 25%, 50% y 75% respecto al valor máximo.
In [8]:
print(dataset.describe()) #Descripción de los datos
#### Obtenemos contéo de sexo
print('\nPacientes masculinos (0) y femeninos (1):')
print(pd.value_counts(dataset['Gender'].values))
#### Obtenemos contéo de la clase
print('\nPacientes enfermos (1) y sanos (2):')
print(pd.value_counts(dataset['Dataset'].values))
Observando estos datos podemos sacar conclusiones tales como:
In [6]:
#### Histogram Matrix Plot
plt.figure()
dataset.hist(xlabelsize=0.5, ylabelsize=0.2,figsize=(10,10))
plt.xlabel("Data")
plt.show()
La variables que más o menos siguen está distribución (Albumin,Total_Protiens) las vamos a normalizar con la normalizacion Z-Score: $$x'=\frac{x-\mu}{\sigma}$$ Aunque la variable Age también parece seguir una distribución normal, por simplicidad y semántica de la variable, no se va a normalizar. Pasa lo mismo con las variables Genre y Dataset.
Para el resto de variables vamos a usar la normalización min-max en un intervalo [0-1]:$$x'=\frac{x-min}{max-min}$$
In [7]:
#Definición de funciones auxiliares
def zScore(var):
return (var-var.mean())/var.std()
def minMax(var):
return (var-var.min())/(var.max()-var.min())
dataNorm=dataset.copy()
dataNorm['Albumin']=zScore(dataNorm['Albumin'])
dataNorm['Total_Protiens']=zScore(dataNorm['Total_Protiens'])
dataNorm['Total_Bilirubin']=minMax(dataNorm['Total_Bilirubin'])
dataNorm['Direct_Bilirubin']=minMax(dataNorm['Direct_Bilirubin'])
dataNorm['Alkaline_Phosphotase']=minMax(dataNorm['Alkaline_Phosphotase'])
dataNorm['Alamine_Aminotransferase']=minMax(dataNorm['Alamine_Aminotransferase'])
dataNorm['Aspartate_Aminotransferase']=minMax(dataNorm['Aspartate_Aminotransferase'])
dataNorm['Albumin_and_Globulin_Ratio']=minMax(dataNorm['Albumin_and_Globulin_Ratio'])
dataNorm
Out[7]:
A partir de este punto ya tenemos un conjunto de datos normalizado con el que operar.
A la hora de tratar cualquier conjunto de datos es común observar valores que parecen que rompen la norma y que se salen de la distribución. En los algoritmos de machine learninng, este tipo de valores pueden desfavorecedores pues a tenerlos en cuenta pueden generar problemas y llegar a estados espúreos.
Por ello, debemos intentar localizarlos y tratarlos. No debemos olvidar nunca la semántica de los datos que tratamos. Para poder identificar outliers, una buena herramienta son las gráficas de caja:
In [8]:
dataset.boxplot(column='Age')
Out[8]:
In [9]:
dataset.boxplot(column='Total_Bilirubin')
Out[9]:
In [10]:
dataset.boxplot(column='Direct_Bilirubin')
Out[10]:
In [11]:
dataset.boxplot(column='Alkaline_Phosphotase')
Out[11]:
In [12]:
dataset.boxplot(column='Alamine_Aminotransferase')
Out[12]:
In [13]:
dataset.boxplot(column='Aspartate_Aminotransferase')
Out[13]:
In [14]:
dataset.boxplot(column='Total_Protiens')
Out[14]:
In [15]:
dataset.boxplot(column='Albumin')
Out[15]:
In [16]:
dataset.boxplot(column='Albumin_and_Globulin_Ratio')
Out[16]:
Como vemos en los boxplot hay varios outliers en las variables, en concreto algunos muy marcados en la total bilirubin, direct bilirubin, alkaline phosphotase, alamine aminotransferase, aspartate aminotransferase y albumin and globulin ratio. Pero al tratarse de datos médicos para detectar si alguien está enfermo o no, esos outliers pueden ser útiles para el estudio, por lo que hemos decidido conservarlos.
Cuando trabajamos con un conjunto amplio de datos, es común que no dispongamos de todos las variables para cada entrada. Debemos localizar estos puntos donde no hay valores (missing values) y tratarlos. En ocasiones, el tratar estos datos implica inicializarlos a un valor que no afecte o inclusive eliminar las entradas conflictivas, dependiendo de los datos.
In [17]:
dataset.notnull().all()
Out[17]:
In [18]:
dataset.loc[dataset['Albumin_and_Globulin_Ratio'].isnull()]
Out[18]:
Solo hay una variable con missing values: Albumin and globulin ratio, y solo en cuatro filas. Al tener una base de datos grande y pocos missing values hemos decido eliminarlos.
In [19]:
dataset.dropna()
Out[19]:
A partir de este punto tenemos los datos listos para empezar hacer un estudio semántico de los mismos sin que las anomalías de los mismos nos puedan interferir.
El primer estudio que vamos a hacer con los datos va a ser evaluar como de relacionados están unos con otros. Esto nos permitirá evaluar cuales de los atributos pueden ser determinantes para el problema que plantea estos datos.
Para ello, generaremos la matriz de correlación lineal gracias a la función corr() del objeto dataset de panda:
In [20]:
correlation=dataset.corr() #Correlation Matrix
correlation
Out[20]:
Si representamos esta matriz como un grafico donde podamos verlo más visualmente:
In [21]:
# Display the correlation matrix with a specified figure number and a bluescale
# colormap
plt.figure()
plt.matshow(correlation, fignum=1, cmap=plt.cm.Blues)
plt.ylabel("Attribute Index")
plt.show()
In [22]:
#### Scatter Matrix Plot
plt.figure()
from pandas.tools.plotting import scatter_matrix
scatter_matrix(dataset, alpha=0.3, figsize=(20, 20), diagonal='kde')
plt.show()
Como conclusión a los datos obtenidos previamnte, podemos sacar las siguientes conclusiones:
Hay alta correlación entre la bilirrubina total y la bilirrubina directa, cosa que esperamos porque bilirrubina total=bilirrubina directa + bilirrubina indirecta, tal y como se explicó previamente.
Hay alta correlación entre albumina y el total proteinas ya que el total proteinas es la suma de la albumina y la gobulina.
Hay alta correlación entre la albumina y el ratio de albumina y gobulina
Hay alta correlación entre las dos transaminasas, la alaninoamino transferasa y la aspartato aminotransferasa
Aun a pesar de los datos previos, no hay una gran correlación entre las variables y la clase. La correlación más alta con la clase es la que hay con la bilirrubina directa. Con lo cual tampoco pareciera que tuviéramos falsos predictores.
Dado el conjunto de datos y que parece que están detectando un fallo hepático, sería planteable pensar en que las diferencias entre hombres y mujeres pudieran ser determinantes y por tanto pudiera estar justificado la separacion de los datos por sexo, dejando los datos en dos subconjuntos preparados para el siguiente paso que sería el algoritmo de aprendizaje.
In [23]:
for df_gender in dataset.groupby('Gender'):
print(df_gender)
Por último, sería conveniente estudiar la necesidad de crear variables sintéticas en función de las variables actuales. El uso de estas variables permite que los algorítmos de machine learning funcionen mejor al tratar sobre una variable que está estudiada y sabemos que proporciona información determinante.
Evaluando los datos, podría ser interesante una variable para las proteinas totales y la bilirrubina total pero ya que dichas variables ya están en el conjunto de datos no hay necesidad de generar variables sintéticas.
Los algoritmos de machine learning son unas herramientass increíbles que nos permiten resolver problemas que de otra manera sería imposible de resolver. Como todo, tienen un inconveniente: los datos a utilizar y su complejidad. Por eso, un paso previo e importantísimo es el tratamiento de dichos datos.
En esta práctica hemos emulado una auditoría que nos ha permitido profundizar en el estudio y manejo de un conjunto de datos en el que en principio no teníamos información relevante sobre los mismos. Junto con los conocimientos impartidos en las clases, hemos sido capaces de interpretar y analizar un conjunto de datos relacionados con una enfermedad hepática lo que nos ha permitido profundizar y afianzar dichos conocimientos.
Hemos visto que la mayoría de los atributos se correlaciónan por igual. Esto indica que la información que porporcionan por separado relevante para la clasificación. También hemos observado que la edad y el sexo son parámetros poco determinantes dado el caso que tratamos aunque pudiera ser interesante la división por dichos datos. Esta división está mas orientada a un estudio estadístico que a una aplicación directa.
Como curiosidad comentar que hemos comentado este conjunto de datos y nuestras conclusiones con un equipo médico (amigos de uno de los integrantes) que nos ha confirmado que los datos relevantes son todos menos la edad y el sexo pues no suelen ser determinantes para las enfermedades hepáticas comunes.