Aprende a realizar el análisis exploratorio de un conjunto de datos, determina conclusiones y las técnicas para un análisis más avanzado.
Preparación de datos: análisis exploratorio
Desarrollado por: Ing. Remigio Hurtado Ortiz, PhD. Correo de contacto: [email protected]
En esta práctica aprenderás a realizar las actividades de un análisis exploratorio cuyo objetivo es comprender y obtener información valiosa de un conjunto de datos antes de realizar análisis más avanzados o modelado estadístico.
Requisitos: es importante tener conocimientos de Python.
Esta práctica está estructurada de la siguiente manera:
Definiciones de análisis de datos
El análisis de datos es un proceso de recolección, limpieza, transformación y modelado de datos con el objetivo de descubrir información útil, informar conclusiones y respaldar la toma de decisiones.
El análisis de datos tiene múltiples facetas y enfoques, que abarcan diversas técnicas bajo una variedad de nombres como minería de datos, business intelligence, aprendizaje automático, análisis del bigdata, etc.
El análisis de datos es la piedra angular en diferentes dominios de negocios y de las ciencias.
El análisis exploratorio de datos es una etapa fundamental en el proceso de análisis de datos para comprender la esencia de los datos, identificar problemas de calidad y definir la dirección que tomará el análisis posterior.
Tipos de variables
1. Categóricas: Las variables categóricas indican el tipo o categoría de una observación. Por lo tanto, las variables categóricas son variables cualitativas y en la mayoría de conjuntos de datos están representadas por un valor no numérico.
1.1 Nominales: la categoría indica la identidad del objetivo. No hay un grado o diferencia entre las categorías, lo que se enfatiza es el nombre. Algunos ejemplos de variables categóricas nominales son: sexo, país, color de ojos, etc.
1.2 Ordinales: la categoría indica un orden. Algunos ejemplos de variables categóricas ordinales son: nivel de educación (primaria, secundaria, superior), las escalas de las encuestas de satisfacción (insatisfecho, neutro, satisfecho), calificación cualitativa (insuficiente, buena, muy buena, sobresaliente, muy sobresaliente), etc.
2. Numéricas: Las variables numéricas tienen valores que describen una cantidad medible. Las variables numéricas son variables cuantitativas.
2.1 Continuas: estas son variables que son cuantitativas y pueden medirse a lo largo de una secuencia o un rango de valores. Existen dos tipos de variables continuas: las variables de intervalo y las variables de relaciones. Las variables de intervalo pueden tener cualquier valor dentro de un rango de valores. Algunos ejemplos son temperatura o tiempo. Las variables de relaciones son las variables de intervalo especiales donde un valor de cero (0) significa que no hay ninguna variable. Entre los ejemplos se incluyen ingresos o el volumen de ventas.
2.2 Discretas: estos tipos de variables continuas son cuantitativos, pero tienen un valor específico de un conjunto de valores finito. Los ejemplos incluyen el número de sensores habilitados en una red, el número de automóviles en un estacionamiento, la cantidad de visitas a una página web, la cantidad de llamadas recibidas, etc.
Actividades del análisis exploratorio
Las actividades clave en un análisis exploratorio de datos incluyen:
Carga de Datos: Importar los datos desde diferentes fuentes, como archivos CSV, hojas de cálculo, páginas web, bases de datos, etc.
Exploración de Datos Iniciales: Comprender la estructura de los datos, tamaño, cantidad de variables y observaciones, tipos de variables y la calidad de los datos (verificar si hay valores faltantes, duplicados o atípicos).
Resumen Estadístico: Calcular estadísticas descriptivas básicas, como la media, mediana, desviación estándar, percentiles, para comprender la distribución y la variabilidad de los datos.
Visualización de Datos: Crear gráficos y visualizaciones para representar los datos de manera efectiva. Esto puede incluir histogramas, diagramas de dispersión, gráficos de barras y diagramas de caja, entre otros.
Análisis de Variables Categóricas: Si existen variables categóricas, realizar análisis de frecuencia para comprender la distribución de categorías.
Análisis de Correlación: Evaluar las relaciones entre las variables mediante el cálculo de correlaciones.
Manejo de Datos Faltantes: Decidir cómo manejar los valores faltantes, que puede incluir imputación (sustitución), eliminación o análisis de su impacto en los resultados.
Análisis de Outliers: Identificar y comprender los valores atípicos que pueden afectar el análisis y la interpretación de los resultados.
Segmentación de Datos: Si es relevante, dividir el conjunto de datos en segmentos o grupos con características similares para un análisis más detallado.
Generación de Hipótesis: Basado en la exploración de datos, formular hipótesis iniciales sobre relaciones o patrones que se puedan investigar más adelante.
Presentación de Resultados: Comunicar hallazgos iniciales a través de informes o presentaciones que resuman las observaciones y los posibles pasos siguientes en el análisis.
Ejemplo práctico de análisis exploratorio
Instalación e importación de librerías
#!pip install seaborn
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns
import copy
print("Módulos y clases importados")
Módulos y clases importados
Carga y exploración inicial
Nombre del dataset: Statlog (German Credit Data). Enlace: http://archive.ics.uci.edu/ml/datasets/statlog+(german+credit+data) [2]
Descripción general: Este conjunto de datos clasifica a las personas descritas por un conjunto de atributos como riesgos crediticios buenos o malos.
Número de Variables (o atributos): 21
Número de instancias (clientes en el banco): 1000
Salida:
TIPOCLIENTE (BUEN CLIENTE O BAJO RIESGO:1, MAL CLIENTE O ALTO RIESGO:2)
nombresVariables=['ESTADOCUENTACORRIENTE','PLAZOMESESCREDITO','HISTORIALCREDITO','PROPOSITOCREDITO','MONTOCREDITO',
'SALDOCUENTAAHORROS','TIEMPOACTUALEMPLEO','TASAPAGO','ESTADOCIVILYSEXO','GARANTE','TIEMPORESIDENCIAACTUAL',
'ACTIVOS','EDAD','OTROSPLANESPAGO','VIVIENDA','CANTIDADCREDITOSEXISTENTES','EMPLEO',
'CANTIDADPERSONASAMANTENER','TELEFONO','TRABAJADOREXTRANJERO','TIPOCLIENTE']
#Cargar un dataframe desde una URL
# URL descripción: https://archive.ics.uci.edu/dataset/144/statlog+german+credit+data
dfOriginal = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/statlog/german/german.data', sep = ' ',
names=nombresVariables)
#Cargar un dataframe de un archivo local
#dataset = "Datasets/german.data"
#dfOriginal = pd.read_csv(dataset, sep = ' ', names=nombresVariables)
dataframe=copy.deepcopy(dfOriginal)
print('cantidad de observaciones (clientes): ',dataframe.shape[0])
print('cantidad de variables: ',dataframe.shape[1])
print(dataframe.shape)
dataframe.tail()
cantidad de observaciones (clientes): 1000 cantidad de variables: 21 (1000, 21)
ESTADOCUENTACORRIENTE | PLAZOMESESCREDITO | HISTORIALCREDITO | PROPOSITOCREDITO | MONTOCREDITO | SALDOCUENTAAHORROS | TIEMPOACTUALEMPLEO | TASAPAGO | ESTADOCIVILYSEXO | GARANTE | … | ACTIVOS | EDAD | OTROSPLANESPAGO | VIVIENDA | CANTIDADCREDITOSEXISTENTES | EMPLEO | CANTIDADPERSONASAMANTENER | TELEFONO | TRABAJADOREXTRANJERO | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
995 | A14 | 12 | A32 | A42 | 1736 | A61 | A74 | 3 | A92 | A101 | … | A121 | 31 | A143 | A152 | 1 | A172 | 1 | A191 | A201 | 1 |
996 | A11 | 30 | A32 | A41 | 3857 | A61 | A73 | 4 | A91 | A101 | … | A122 | 40 | A143 | A152 | 1 | A174 | 1 | A192 | A201 | 1 |
997 | A14 | 12 | A32 | A43 | 804 | A61 | A75 | 4 | A93 | A101 | … | A123 | 38 | A143 | A152 | 1 | A173 | 1 | A191 | A201 | 1 |
998 | A11 | 45 | A32 | A43 | 1845 | A61 | A73 | 4 | A93 | A101 | … | A124 | 23 | A143 | A153 | 1 | A173 | 1 | A192 | A201 | 2 |
999 | A12 | 45 | A34 | A41 | 4576 | A62 | A71 | 3 | A93 | A101 | … | A123 | 27 | A143 | A152 | 1 | A173 | 1 | A191 | A201 | 1 |
5 rows × 21 columns
dataframe.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 1000 entries, 0 to 999 Data columns (total 21 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 ESTADOCUENTACORRIENTE 1000 non-null object 1 PLAZOMESESCREDITO 1000 non-null int64 2 HISTORIALCREDITO 1000 non-null object 3 PROPOSITOCREDITO 1000 non-null object 4 MONTOCREDITO 1000 non-null int64 5 SALDOCUENTAAHORROS 1000 non-null object 6 TIEMPOACTUALEMPLEO 1000 non-null object 7 TASAPAGO 1000 non-null int64 8 ESTADOCIVILYSEXO 1000 non-null object 9 GARANTE 1000 non-null object 10 TIEMPORESIDENCIAACTUAL 1000 non-null int64 11 ACTIVOS 1000 non-null object 12 EDAD 1000 non-null int64 13 OTROSPLANESPAGO 1000 non-null object 14 VIVIENDA 1000 non-null object 15 CANTIDADCREDITOSEXISTENTES 1000 non-null int64 16 EMPLEO 1000 non-null object 17 CANTIDADPERSONASAMANTENER 1000 non-null int64 18 TELEFONO 1000 non-null object 19 TRABAJADOREXTRANJERO 1000 non-null object 20 TIPOCLIENTE 1000 non-null int64 dtypes: int64(8), object(13) memory usage: 164.2+ KB
Resumen estadístico
<count:> esta es la cantidad de valores incluidos en las estadísticas.
<mean:> este es el promedio de los valores.
<std:> esta es la desviación estándar de la distribución.
<min:> este es el valor más bajo de la distribución.
25%: este es el valor del primer cuartil. El 25 % de los valores se encuentran en o por debajo de este valor.
50%: este es el valor para el segundo cuartil. El 50 % de los valores se encuentran en o por debajo de este valor. También es el valor de la mediana.
75%: este es el valor para el tercer cuartil. El 75 % de los valores se encuentran en o por debajo de este valor.
<max:> este es el valor más alto de la distribución.
#Calcular las medidas descriptivas de las variables que tienen formato numérico
dataframe.describe()
PLAZOMESESCREDITO | MONTOCREDITO | TASAPAGO | TIEMPORESIDENCIAACTUAL | EDAD | CANTIDADCREDITOSEXISTENTES | CANTIDADPERSONASAMANTENER | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|---|
count | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 |
mean | 20.903000 | 3271.258000 | 2.973000 | 2.845000 | 35.546000 | 1.407000 | 1.155000 | 1.300000 |
std | 12.058814 | 2822.736876 | 1.118715 | 1.103718 | 11.375469 | 0.577654 | 0.362086 | 0.458487 |
min | 4.000000 | 250.000000 | 1.000000 | 1.000000 | 19.000000 | 1.000000 | 1.000000 | 1.000000 |
25% | 12.000000 | 1365.500000 | 2.000000 | 2.000000 | 27.000000 | 1.000000 | 1.000000 | 1.000000 |
50% | 18.000000 | 2319.500000 | 3.000000 | 3.000000 | 33.000000 | 1.000000 | 1.000000 | 1.000000 |
75% | 24.000000 | 3972.250000 | 4.000000 | 4.000000 | 42.000000 | 2.000000 | 1.000000 | 2.000000 |
max | 72.000000 | 18424.000000 | 4.000000 | 4.000000 | 75.000000 | 4.000000 | 2.000000 | 2.000000 |
¿Qué conclusiones puedes indicar a partir de las estadísticas?
Por ejemplo:
Edad Media: La edad media de los clientes del banco es de 35 años. Esto significa que, en promedio, los clientes tienen alrededor de 35 años de edad.
Edad Mínima: La edad mínima registrada entre los clientes es de 19 años. Esto indica que el banco tiene clientes jóvenes, incluyendo a personas que recién comienzan su vida financiera.
Edad Máxima: La edad máxima registrada entre los clientes es de 75 años. Esto sugiere que el banco también atrae a clientes mayores que pueden estar planificando su jubilación o gestionando sus finanzas en la edad adulta.
Diversidad de Edades: La diferencia entre la edad mínima y máxima (75 – 19 = 56 años) es bastante amplia (desviación estandar es de 11.37). Esto refleja una diversidad de edades en la base de clientes del banco, lo que puede requerir una variedad de servicios y productos adaptados a diferentes grupos demográficos.
Población Equilibrada: En términos generales, la edad media de 35 años sugiere que la población de clientes del banco tiende a ser equilibrada en cuanto a edades, sin una inclinación significativa hacia un grupo etario particular.
Estas conclusiones pueden ser útiles para el banco a la hora de tomar decisiones estratégicas sobre sus servicios y productos. Por ejemplo, podrían considerar la introducción de productos financieros específicos para jóvenes o para personas mayores, o diseñar campañas de marketing dirigidas a diferentes grupos de edad. También podrían utilizar esta información para mejorar la experiencia del cliente y la personalización de servicios en función de la edad.
Visualización de datos
#Se genera una figura de frecuencia de clase, es decir, la cantidad de muestras en cada clase.
Y=dataframe['TIPOCLIENTE']
sns.countplot(x=Y)
<AxesSubplot: xlabel='TIPOCLIENTE', ylabel='count'>
#Ordenar el DataFrame por una variable
dataframeTratamiento=dataframe.sort_values(by=['EDAD'])
dataframeTratamiento.head(5)
ESTADOCUENTACORRIENTE | PLAZOMESESCREDITO | HISTORIALCREDITO | PROPOSITOCREDITO | MONTOCREDITO | SALDOCUENTAAHORROS | TIEMPOACTUALEMPLEO | TASAPAGO | ESTADOCIVILYSEXO | GARANTE | … | ACTIVOS | EDAD | OTROSPLANESPAGO | VIVIENDA | CANTIDADCREDITOSEXISTENTES | EMPLEO | CANTIDADPERSONASAMANTENER | TELEFONO | TRABAJADOREXTRANJERO | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
391 | A12 | 12 | A32 | A42 | 983 | A64 | A72 | 1 | A92 | A101 | … | A121 | 19 | A143 | A151 | 1 | A172 | 1 | A191 | A201 | 1 |
633 | A14 | 9 | A32 | A42 | 1980 | A61 | A72 | 2 | A92 | A102 | … | A123 | 19 | A143 | A151 | 2 | A173 | 1 | A191 | A201 | 2 |
513 | A12 | 12 | A33 | A43 | 585 | A61 | A73 | 4 | A94 | A102 | … | A121 | 20 | A143 | A151 | 2 | A173 | 1 | A191 | A201 | 1 |
155 | A11 | 12 | A32 | A42 | 1282 | A61 | A73 | 2 | A92 | A101 | … | A123 | 20 | A143 | A151 | 1 | A173 | 1 | A191 | A201 | 2 |
410 | A12 | 24 | A32 | A43 | 1967 | A61 | A75 | 4 | A92 | A101 | … | A123 | 20 | A143 | A152 | 1 | A173 | 1 | A192 | A201 | 1 |
5 rows × 21 columns
#Visualización 2D: varias variables para análisis
import matplotlib.pyplot as plt
#plt.style.use('classic')
fig = plt.figure(figsize = (8,8))
ax = fig.add_subplot(1,1,1)
ax.set_xlabel('EDAD', fontsize = 12)
ax.set_ylabel('PLAZOMESESCREDITO', fontsize = 12)
ax.set_title('Análisis entre dos variables', fontsize = 12)
targets = [1, 2]# 1 bajo riesgo. 2 alto riesgo
colors = ['g', 'r']
for target, color in zip(targets,colors):
indicesToKeep = dataframeTratamiento['TIPOCLIENTE'] == target
ax.scatter(dataframeTratamiento.loc[indicesToKeep, 'EDAD']
, dataframeTratamiento.loc[indicesToKeep, 'PLAZOMESESCREDITO']
, c = color
, s = 40)
ax.legend(['Bajo riesgo','Alto Riesgo'])
ax.grid()
plt.show()
#Visualización 2D: una variable para análisis
#print(plt.style.available)
#plt.style.use('dark_background')
#plt.style.use('seaborn-darkgrid')
dataframeTratamientoOrdenado=dataframeTratamiento.sort_values(by=['MONTOCREDITO'])
variableAnalisis=dataframeTratamientoOrdenado.loc[:,['MONTOCREDITO']]
cantidadMuestras=len(dataframeTratamientoOrdenado.index)
arrayCantidadMuestras= range(cantidadMuestras)
plt.plot(arrayCantidadMuestras,variableAnalisis, color='blue', linestyle='dashed', linewidth = 1,
marker='o', markerfacecolor='blue', markersize=3)
#plt.ylim((0,35))
plt.title('Análisis de una variable')
plt.xlabel('Número de cliente')
plt.ylabel('Monto de crédito solicitado')
plt.show()
Análisis de variables categóricas
def descripcionCantidadCategorias(dataframe, categorical_features):
cantidadTotalVariables = len(dataframe.columns)
cantidadVariablesCategoricas = len(categorical_features)
for variable in categorical_features:
cantidadCategorías=dataframe[variable].nunique()
print('Cantidad de categorías en la variable',variable,':',cantidadCategorías)
return cantidadVariablesCategoricas
categorical_features = ['ESTADOCUENTACORRIENTE','HISTORIALCREDITO','SALDOCUENTAAHORROS','TIEMPOACTUALEMPLEO',
'ESTADOCIVILYSEXO','ACTIVOS','VIVIENDA','EMPLEO','PROPOSITOCREDITO','GARANTE','TRABAJADOREXTRANJERO']
cantidadVariablesCategoricas=descripcionCantidadCategorias(dataframe, categorical_features)
print('cantidad de variables categóricas:',cantidadVariablesCategoricas)
Cantidad de categorías en la variable ESTADOCUENTACORRIENTE : 4 Cantidad de categorías en la variable HISTORIALCREDITO : 5 Cantidad de categorías en la variable SALDOCUENTAAHORROS : 5 Cantidad de categorías en la variable TIEMPOACTUALEMPLEO : 5 Cantidad de categorías en la variable ESTADOCIVILYSEXO : 4 Cantidad de categorías en la variable ACTIVOS : 4 Cantidad de categorías en la variable VIVIENDA : 3 Cantidad de categorías en la variable EMPLEO : 4 Cantidad de categorías en la variable PROPOSITOCREDITO : 10 Cantidad de categorías en la variable GARANTE : 3 Cantidad de categorías en la variable TRABAJADOREXTRANJERO : 2 cantidad de variables categóricas: 11
Hay 3 variables categóricas nominales: ‘PROPOSITOCREDITO’,’GARANTE’,’TRABAJADOREXTRANJERO’
PROPOSITOCREDITO: En principio son 11 categorías. Sin embargo, en el conjunto de datos, no hay clientes que pertenezcan a la categoría A47. Por lo tanto, PROPOSITOCREDITO tiene 10 categorías:
- A40 : carro (nuevo)
- A41 : car (usado)
- A42 : mueble/equipo
- A43 : radio/televisión
- A44 : electrodomésticos
- A45 : reparaciones
- A46 : educación
- A47 : (vacaciones) -> en el dataset no hay un cliente que haya pedido un crédito para vacaciones
- A48 : reciclaje
- A49 : negocios
- A410 : otros
GARANTE: (A101 : ninguno, A102 : co-aplicante, A103 : garante) = 3 categorías
TRABAJADOREXTRANJERO: (A201 : yes, A202 : no) = 2 categorías
Análisis de correlación
En esta subsección se realiza un anális de correlaciones y mapas de calor para una mejor comprensión.
#Análisis entre dos variables: x, y
x = np.array([5,5,1])
y = np.array([1,2,5])
#Covarianza
covariance = np.cov(x, y, bias=True)[0][1]
#bias: False para (N - 1) si es una muestra, True para (N) si es toda la población
print('Covarianza:',covariance)
#Diccionario
dict = {'X': x,
'Y': y}
#Creación de dataframe a partir de un diccionario
df = pd.DataFrame(dict)
#Desviación estándar de una variable
std=df['Y'].std(ddof=0)
print('Desviación estándar:',std)
#Correlaciones
df.corr()
Covarianza: -3.111111111111111 Desviación estándar: 1.699673171197595
X | Y | |
---|---|---|
X | 1.000000 | -0.970725 |
Y | -0.970725 | 1.000000 |
#Análisis entre tres variables: u1, u2, u3
u1 = np.array([5,1])
u2 = np.array([5,2])
u3 = np.array([1,5])
#Covarianza
covariance = np.cov(u2, u3, bias=True)[0][1]
#bias: False para (N - 1) si es una muestra, True para (N) si es toda la población
print('Covarianza:',covariance)
#Diccionario
dict = {'u1': u1,
'u2': u2,
'u3': u3}
#Creación de dataframe a partir de un diccionario
df = pd.DataFrame(dict)
#Desviación estándar de una variable
std=df['u3'].std(ddof=0)
print('Desviación estándar:',std)
#Correlaciones
df.corr()
Covarianza: -3.0 Desviación estándar: 2.0
u1 | u2 | u3 | |
---|---|---|---|
u1 | 1.0 | 1.0 | -1.0 |
u2 | 1.0 | 1.0 | -1.0 |
u3 | -1.0 | -1.0 | 1.0 |
#Análisis entre todas las variables
cr=dataframe.corr(numeric_only = True)
cr.head()
PLAZOMESESCREDITO | MONTOCREDITO | TASAPAGO | TIEMPORESIDENCIAACTUAL | EDAD | CANTIDADCREDITOSEXISTENTES | CANTIDADPERSONASAMANTENER | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|---|
PLAZOMESESCREDITO | 1.000000 | 0.624984 | 0.074749 | 0.034067 | -0.036136 | -0.011284 | -0.023834 | 0.214927 |
MONTOCREDITO | 0.624984 | 1.000000 | -0.271316 | 0.028926 | 0.032716 | 0.020795 | 0.017142 | 0.154739 |
TASAPAGO | 0.074749 | -0.271316 | 1.000000 | 0.049302 | 0.058266 | 0.021669 | -0.071207 | 0.072404 |
TIEMPORESIDENCIAACTUAL | 0.034067 | 0.028926 | 0.049302 | 1.000000 | 0.266419 | 0.089625 | 0.042643 | 0.002967 |
EDAD | -0.036136 | 0.032716 | 0.058266 | 0.266419 | 1.000000 | 0.149254 | 0.118201 | -0.091127 |
#Un mapa de calor simple
sns.heatmap(cr)
plt.savefig('attribute_correlations.png')
#Un mapa de calor personalizado: colores, tamaño
fig, ax = plt.subplots(figsize = (7, 5))
#sns.heatmap(cr, cmap ='viridis', linewidths = 0.30, annot = True)
sns.heatmap(cr, cmap ='GnBu', linewidths = 0.30, annot = False)
<AxesSubplot: >
#Un mapa de calor personalizado: colores, región inferior a la diagonal, tamaño
mask = np.zeros_like(cr)
mask[np.triu_indices_from(mask)] = True
with sns.axes_style("white"):
f, ax = plt.subplots(figsize=(5, 5))
ax = sns.heatmap(cr, mask=mask, square=True, linewidths=.5, cmap="YlGnBu")
plt.savefig('attribute_correlations.png')
#Análisis a partir de los valores: correlaciones positivas
dfCorrOrdenado=cr.sort_values(by=['TIPOCLIENTE'], ascending=False)
dfCorrOrdenado['TIPOCLIENTE'].head(10)
TIPOCLIENTE 1.000000 PLAZOMESESCREDITO 0.214927 MONTOCREDITO 0.154739 TASAPAGO 0.072404 TIEMPORESIDENCIAACTUAL 0.002967 CANTIDADPERSONASAMANTENER -0.003015 CANTIDADCREDITOSEXISTENTES -0.045732 EDAD -0.091127 Name: TIPOCLIENTE, dtype: float64
#Análisis a partir de los valores: correlaciones negativas
dfCorrOrdenado=cr.sort_values(by=['TIPOCLIENTE'], ascending=True)
dfCorrOrdenado['TIPOCLIENTE'].head(10)
EDAD -0.091127 CANTIDADCREDITOSEXISTENTES -0.045732 CANTIDADPERSONASAMANTENER -0.003015 TIEMPORESIDENCIAACTUAL 0.002967 TASAPAGO 0.072404 MONTOCREDITO 0.154739 PLAZOMESESCREDITO 0.214927 TIPOCLIENTE 1.000000 Name: TIPOCLIENTE, dtype: float64
#Visualización para análisis entre dos variables
variable1Analisis='MONTOCREDITO'
variable2Analisis='PLAZOMESESCREDITO'
dataframe.plot(x=variable1Analisis, y=variable2Analisis, style='o')
plt.title('Análisis de Correlación')
plt.xlabel(variable1Analisis)
plt.ylabel(variable2Analisis)
plt.show()
Manejo de datos faltantes y variables no relevantes
Se descartan algunas variables que se supone no aportan al análisis o tienen datos faltantes.
#Se eliminan algunas variables
dataframe=dataframe.drop(['OTROSPLANESPAGO'], axis=1)
dataframe=dataframe.drop(['TELEFONO'], axis=1)
print(dataframe.shape)
dataframe.head()
(1000, 19)
ESTADOCUENTACORRIENTE | PLAZOMESESCREDITO | HISTORIALCREDITO | PROPOSITOCREDITO | MONTOCREDITO | SALDOCUENTAAHORROS | TIEMPOACTUALEMPLEO | TASAPAGO | ESTADOCIVILYSEXO | GARANTE | TIEMPORESIDENCIAACTUAL | ACTIVOS | EDAD | VIVIENDA | CANTIDADCREDITOSEXISTENTES | EMPLEO | CANTIDADPERSONASAMANTENER | TRABAJADOREXTRANJERO | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | A11 | 6 | A34 | A43 | 1169 | A65 | A75 | 4 | A93 | A101 | 4 | A121 | 67 | A152 | 2 | A173 | 1 | A201 | 1 |
1 | A12 | 48 | A32 | A43 | 5951 | A61 | A73 | 2 | A92 | A101 | 2 | A121 | 22 | A152 | 1 | A173 | 1 | A201 | 2 |
2 | A14 | 12 | A34 | A46 | 2096 | A61 | A74 | 2 | A93 | A101 | 3 | A121 | 49 | A152 | 1 | A172 | 2 | A201 | 1 |
3 | A11 | 42 | A32 | A42 | 7882 | A61 | A74 | 2 | A93 | A103 | 4 | A122 | 45 | A153 | 1 | A173 | 2 | A201 | 1 |
4 | A11 | 24 | A33 | A40 | 4870 | A61 | A73 | 3 | A93 | A101 | 4 | A124 | 53 | A153 | 2 | A173 | 2 | A201 | 2 |
#Se modifica la variable de salida (de estudio): TIPOCLIENTE. Es mejor tener la salida en formato binario cuando hay dos clases
dataframe['TIPOCLIENTE'] = dataframe['TIPOCLIENTE'].replace(2, 0)
dataframe.head()
ESTADOCUENTACORRIENTE | PLAZOMESESCREDITO | HISTORIALCREDITO | PROPOSITOCREDITO | MONTOCREDITO | SALDOCUENTAAHORROS | TIEMPOACTUALEMPLEO | TASAPAGO | ESTADOCIVILYSEXO | GARANTE | TIEMPORESIDENCIAACTUAL | ACTIVOS | EDAD | VIVIENDA | CANTIDADCREDITOSEXISTENTES | EMPLEO | CANTIDADPERSONASAMANTENER | TRABAJADOREXTRANJERO | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | A11 | 6 | A34 | A43 | 1169 | A65 | A75 | 4 | A93 | A101 | 4 | A121 | 67 | A152 | 2 | A173 | 1 | A201 | 1 |
1 | A12 | 48 | A32 | A43 | 5951 | A61 | A73 | 2 | A92 | A101 | 2 | A121 | 22 | A152 | 1 | A173 | 1 | A201 | 0 |
2 | A14 | 12 | A34 | A46 | 2096 | A61 | A74 | 2 | A93 | A101 | 3 | A121 | 49 | A152 | 1 | A172 | 2 | A201 | 1 |
3 | A11 | 42 | A32 | A42 | 7882 | A61 | A74 | 2 | A93 | A103 | 4 | A122 | 45 | A153 | 1 | A173 | 2 | A201 | 1 |
4 | A11 | 24 | A33 | A40 | 4870 | A61 | A73 | 3 | A93 | A101 | 4 | A124 | 53 | A153 | 2 | A173 | 2 | A201 | 0 |
#Se genera una figura de frecuencia de clase, es decir, la cantidad de muestras en cada clase.
Y=dataframe['TIPOCLIENTE']
sns.countplot(x=Y)
<AxesSubplot: xlabel='TIPOCLIENTE', ylabel='count'>
Selección y extracción de variables
#Selección de variables según el análisis de correlación
dataframe=dataframe[['PLAZOMESESCREDITO', 'MONTOCREDITO', 'TASAPAGO', 'EDAD', 'CANTIDADCREDITOSEXISTENTES', 'CANTIDADPERSONASAMANTENER','TIPOCLIENTE']]
print(dataframe.shape)
dataframe.head()
(1000, 7)
PLAZOMESESCREDITO | MONTOCREDITO | TASAPAGO | EDAD | CANTIDADCREDITOSEXISTENTES | CANTIDADPERSONASAMANTENER | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|
0 | 6 | 1169 | 4 | 67 | 2 | 1 | 1 |
1 | 48 | 5951 | 2 | 22 | 1 | 1 | 0 |
2 | 12 | 2096 | 2 | 49 | 1 | 2 | 1 |
3 | 42 | 7882 | 2 | 45 | 1 | 2 | 1 |
4 | 24 | 4870 | 3 | 53 | 2 | 2 | 0 |
Segmentación y filtros
#Ordenar por alguna variable
#Continuar a partir del dataframe con las variables más relevantes (dataframe) o desde el dataframe sin outliers (df_filtrado)
dataframeTratamiento=dataframe.sort_values(by=['EDAD'])
print(dataframeTratamiento.shape)
dataframeTratamiento.head(5)
(1000, 7)
PLAZOMESESCREDITO | MONTOCREDITO | TASAPAGO | EDAD | CANTIDADCREDITOSEXISTENTES | CANTIDADPERSONASAMANTENER | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|
391 | 12 | 983 | 1 | 19 | 1 | 1 | 1 |
633 | 9 | 1980 | 2 | 19 | 2 | 1 | 0 |
513 | 12 | 585 | 4 | 20 | 2 | 1 | 1 |
155 | 12 | 1282 | 2 | 20 | 1 | 1 | 0 |
410 | 24 | 1967 | 4 | 20 | 1 | 1 | 1 |
#Filtrar por valores de algunas variables
#Valores para PLAZOMESESCREDITO
valoresK=[6,12,18,24,30,36,42,48,54,60,66,72]
#Valores para EDAD
Nmin=20
Nmax=65
dataFiltros=dataframeTratamiento.loc[(dataframeTratamiento['EDAD'] >= Nmin) & (dataframeTratamiento['EDAD'] <= Nmax)]#Filtrar por algunos valores de N
dataFiltros=dataFiltros.loc[(dataFiltros['PLAZOMESESCREDITO'].isin(valoresK))]#Filtrar por algunos valores en K
print(dataFiltros.shape)
dataFiltros.head(5)
(736, 7)
PLAZOMESESCREDITO | MONTOCREDITO | TASAPAGO | EDAD | CANTIDADCREDITOSEXISTENTES | CANTIDADPERSONASAMANTENER | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|
513 | 12 | 585 | 4 | 20 | 2 | 1 | 1 |
155 | 12 | 1282 | 2 | 20 | 1 | 1 | 0 |
410 | 24 | 1967 | 4 | 20 | 1 | 1 | 1 |
296 | 12 | 4675 | 1 | 20 | 1 | 1 | 1 |
905 | 12 | 1107 | 2 | 20 | 1 | 2 | 1 |
dataSegmentos=dataFiltros.groupby(['PLAZOMESESCREDITO', 'EDAD'])
dataSegmentos=dataSegmentos.mean()#calcula la media de todas la variables para los clientes con el mismo plazo y edad
dataSegmentos.head(5)
MONTOCREDITO | TASAPAGO | CANTIDADCREDITOSEXISTENTES | CANTIDADPERSONASAMANTENER | TIPOCLIENTE | ||
---|---|---|---|---|---|---|
PLAZOMESESCREDITO | EDAD | |||||
6 | 21 | 1766.000000 | 1.00 | 1.00 | 1.000000 | 1.00 |
22 | 454.000000 | 3.00 | 1.00 | 1.000000 | 1.00 | |
23 | 4253.750000 | 2.00 | 1.00 | 1.000000 | 0.50 | |
24 | 1249.750000 | 2.25 | 1.25 | 1.250000 | 0.75 | |
26 | 1630.333333 | 2.00 | 1.00 | 1.333333 | 1.00 |
dataSegmentos=dataSegmentos.groupby('PLAZOMESESCREDITO')
dataSegmentos.head(2)
MONTOCREDITO | TASAPAGO | CANTIDADCREDITOSEXISTENTES | CANTIDADPERSONASAMANTENER | TIPOCLIENTE | ||
---|---|---|---|---|---|---|
PLAZOMESESCREDITO | EDAD | |||||
6 | 21 | 1766.000000 | 1.000000 | 1.000000 | 1.0 | 1.000000 |
22 | 454.000000 | 3.000000 | 1.000000 | 1.0 | 1.000000 | |
12 | 20 | 1664.600000 | 2.600000 | 1.200000 | 1.2 | 0.600000 |
21 | 1025.666667 | 4.000000 | 1.000000 | 1.0 | 0.666667 | |
18 | 20 | 2039.000000 | 1.000000 | 1.000000 | 1.0 | 0.000000 |
21 | 1651.333333 | 3.000000 | 1.000000 | 1.0 | 1.000000 | |
24 | 20 | 2824.500000 | 3.250000 | 1.250000 | 1.0 | 0.500000 |
21 | 4277.500000 | 2.000000 | 1.000000 | 1.5 | 0.500000 | |
30 | 21 | 3441.000000 | 2.000000 | 1.000000 | 1.0 | 0.000000 |
22 | 3832.000000 | 2.000000 | 1.000000 | 1.0 | 1.000000 | |
36 | 23 | 5756.333333 | 2.666667 | 1.666667 | 1.0 | 0.333333 |
24 | 5843.200000 | 3.200000 | 1.400000 | 1.0 | 0.400000 | |
42 | 26 | 4370.000000 | 3.000000 | 2.000000 | 2.0 | 0.000000 |
29 | 7166.000000 | 2.000000 | 1.000000 | 1.0 | 1.000000 | |
48 | 22 | 5951.000000 | 2.000000 | 1.000000 | 1.0 | 0.000000 |
23 | 15672.000000 | 2.000000 | 1.000000 | 1.0 | 0.000000 | |
54 | 39 | 9436.000000 | 2.000000 | 1.000000 | 2.0 | 1.000000 |
58 | 15945.000000 | 3.000000 | 1.000000 | 1.0 | 0.000000 | |
60 | 21 | 12898.500000 | 2.000000 | 1.500000 | 1.0 | 1.000000 |
24 | 7408.000000 | 4.000000 | 1.000000 | 1.0 | 0.000000 | |
72 | 24 | 5595.000000 | 2.000000 | 1.000000 | 1.0 | 0.000000 |
Análisis y eliminación de outliers
from sklearn import preprocessing
#Se normaliza los datos, los valores entre 0 y 1
data_scaled_minmax = preprocessing.MinMaxScaler(feature_range=(0, 1))
data_scaled_minmax = data_scaled_minmax.fit_transform(dataFiltros)
dataframe_scaled_minmax = pd.DataFrame(data=data_scaled_minmax,columns=dataframe.columns)
print(dataframe_scaled_minmax.shape)
dataframe_scaled_minmax.head()
(736, 7)
PLAZOMESESCREDITO | MONTOCREDITO | TASAPAGO | EDAD | CANTIDADCREDITOSEXISTENTES | CANTIDADPERSONASAMANTENER | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|
0 | 0.090909 | 0.018433 | 1.000000 | 0.0 | 0.333333 | 0.0 | 1.0 |
1 | 0.090909 | 0.056784 | 0.333333 | 0.0 | 0.000000 | 0.0 | 0.0 |
2 | 0.272727 | 0.094476 | 1.000000 | 0.0 | 0.000000 | 0.0 | 1.0 |
3 | 0.090909 | 0.243480 | 0.000000 | 0.0 | 0.000000 | 0.0 | 1.0 |
4 | 0.090909 | 0.047155 | 0.333333 | 0.0 | 0.000000 | 1.0 | 1.0 |
import plotly.graph_objs as go
import numpy as np
import plotly.express as px
from mpl_toolkits.mplot3d import Axes3D
# Genera datos de ejemplo (reemplaza esto con tus propios datos)
#np.random.seed(0)
#data = np.random.rand(100, 3)
#np.random.rand(200)
# Selecciona las tres columnas que deseas convertir
columnas_seleccionadas = dataframe_scaled_minmax[['PLAZOMESESCREDITO', 'MONTOCREDITO', 'TASAPAGO']]
# Convierte las columnas seleccionadas en un numpy.ndarray
data = columnas_seleccionadas.to_numpy()
# Define un umbral (de distancia euclidiana) para identificar outliers (puedes ajustar esto según tus necesidades)
umbral = 1.2
# Calcula la distancia euclidiana desde el origen (centro) para cada punto de datos
distancias = np.linalg.norm(data, axis=1)
# Identifica los índices de los outliers
outliers_indices = np.where(distancias > umbral)[0]
# Separa los datos en dos grupos: puntos normales y outliers
puntos_normales = data[distancias <= umbral]
outliers = data[outliers_indices]
# Crea una figura 3D interactiva
fig = go.Figure()
# Plotea los puntos de datos normales (dentro de la esfera)
fig.add_trace(go.Scatter3d(
x=puntos_normales[:, 0],
y=puntos_normales[:, 1],
z=puntos_normales[:, 2],
mode='markers',
marker={'color':'blue', 'size':4},
name='Puntos Normales'
))
# Plotea los outliers (fuera de la esfera)
fig.add_trace(go.Scatter3d(
x=outliers[:, 0],
y=outliers[:, 1],
z=outliers[:, 2],
mode='markers',
marker={'color':'red', 'size':4},
name='Outliers'
))
# Crea una esfera que excluye los outliers
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = umbral * np.outer(np.cos(u), np.sin(v))
y = umbral * np.outer(np.sin(u), np.sin(v))
z = umbral * np.outer(np.ones_like(u), np.cos(v))
fig.add_trace(go.Surface(x=x, y=y, z=z, colorscale='greens', opacity=0.3, showscale=False, name='Esfera Umbral'))
# Configura el diseño de la figura
fig.update_layout(
scene={
'xaxis_title':'X: PLAZOMESESCREDITO', #EJE X
'yaxis_title':'Y: MONTOCREDITO', #EJE Y
'zaxis_title':'Z: TASAPAGO' #EJE Z
},
scene_camera={
'center':{'x':0, 'y':0, 'z':0},
'eye':{'x':1.25, 'y':1.25, 'z':1.25}
}
)
# Guarda la figura interactica
#fig.write_html("grafico_interactivo.html")
# Muestra la figura interactiva
fig.show()
# Calcular la distancia al origen para cada muestra
dataframe_scaled_minmax['DIST'] = np.sqrt(dataframe_scaled_minmax['PLAZOMESESCREDITO']**2 + dataframe_scaled_minmax['MONTOCREDITO']**2 + dataframe_scaled_minmax['TASAPAGO']**2)
# Filtrar el DataFrame para obtener solo las muestras dentro de la esfera
df_filtrado = dataframe_scaled_minmax[dataframe_scaled_minmax['DIST'] <= umbral]
df_filtrado = df_filtrado.drop(['DIST'], axis=1)
#Guardar el dataset
df_filtrado.to_csv("DatasetNormalizadoyFiltrado.csv", sep=";",index = False)
print(df_filtrado.shape)
df_filtrado.head(20)
(704, 7)
PLAZOMESESCREDITO | MONTOCREDITO | TASAPAGO | EDAD | CANTIDADCREDITOSEXISTENTES | CANTIDADPERSONASAMANTENER | TIPOCLIENTE | |
---|---|---|---|---|---|---|---|
0 | 0.090909 | 0.018433 | 1.000000 | 0.000000 | 0.333333 | 0.0 | 1.0 |
1 | 0.090909 | 0.056784 | 0.333333 | 0.000000 | 0.000000 | 0.0 | 0.0 |
2 | 0.272727 | 0.094476 | 1.000000 | 0.000000 | 0.000000 | 0.0 | 1.0 |
3 | 0.090909 | 0.243480 | 0.000000 | 0.000000 | 0.000000 | 0.0 | 1.0 |
4 | 0.090909 | 0.047155 | 0.333333 | 0.000000 | 0.000000 | 1.0 | 1.0 |
5 | 0.272727 | 0.135798 | 0.666667 | 0.000000 | 0.000000 | 0.0 | 0.0 |
6 | 0.181818 | 0.098437 | 0.000000 | 0.000000 | 0.000000 | 0.0 | 0.0 |
7 | 0.090909 | 0.023330 | 1.000000 | 0.000000 | 0.000000 | 0.0 | 0.0 |
8 | 0.272727 | 0.151095 | 0.333333 | 0.000000 | 0.000000 | 0.0 | 0.0 |
9 | 0.272727 | 0.185265 | 1.000000 | 0.000000 | 0.333333 | 0.0 | 1.0 |
10 | 0.181818 | 0.048201 | 1.000000 | 0.022222 | 0.000000 | 0.0 | 1.0 |
11 | 0.090909 | 0.034995 | 1.000000 | 0.022222 | 0.000000 | 0.0 | 1.0 |
12 | 0.272727 | 0.095576 | 0.333333 | 0.022222 | 0.000000 | 1.0 | 0.0 |
13 | 0.000000 | 0.083416 | 0.000000 | 0.022222 | 0.000000 | 0.0 | 1.0 |
14 | 0.363636 | 0.175580 | 0.333333 | 0.022222 | 0.000000 | 0.0 | 0.0 |
15 | 0.181818 | 0.139155 | 0.000000 | 0.022222 | 0.000000 | 0.0 | 1.0 |
16 | 0.090909 | 0.057170 | 1.000000 | 0.022222 | 0.000000 | 0.0 | 1.0 |
17 | 0.272727 | 0.347639 | 0.333333 | 0.022222 | 0.000000 | 0.0 | 1.0 |
18 | 0.818182 | 0.544404 | 0.333333 | 0.022222 | 0.000000 | 0.0 | 1.0 |
20 | 0.090909 | 0.035875 | 1.000000 | 0.022222 | 0.000000 | 0.0 | 0.0 |
Conclusiones
El análisis exploratorio de datos es fundamental para comprender la esencia de los datos, identificar problemas de calidad y conducir el análisis posterior. Ayuda a los analistas a tomar decisiones informadas sobre cómo abordar un conjunto de datos antes de aplicar técnicas más avanzadas de modelado (por ejemplo, machine learning) o inferencia estadística.
Referencias y enlaces
[1] P. Joshi. (2017). Artificial intelligence with python. Packt Publishing Ltd.
[2] Dua, D. and Graff, C. (2019). UCI Machine Learning Repository [http://archive.ics.uci.edu/ml]. Irvine, CA: University of California, School of Information and Computer Science.
Enlaces de documentación de scikit-learn:
https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OrdinalEncoder.html
https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html
https://scikit-learn.org/stable/auto_examples/compose/plot_column_transformer_mixed_types.html
https://scikit-learn.org/stable/modules/generated/sklearn.compose.ColumnTransformer.html