Manipulación, Limpieza y Exploración de Datos#
Este cuaderno tiene como objetivo revisar diversas técnicas de manipulación y limpieza de datos. Posterior al procesamiento de datos, realizaremos un análisis exploratorio de los datos para obtener insights valiosos. Es importante tener en cuenta que usaremos el dataset bank_marketing
del repositorio de Machine Learning de UCI. Este conjunto de datos nos proporcionará una gran oportunidad para aplicar y practicar las técnicas que aprenderemos.
Contenidos#
A lo largo de este cuaderno, abordaremos los siguientes temas:
Selección y filtrado de datos: Aprenderemos cómo seleccionar y filtrar datos de manera eficiente en Python.
Manejo de valores nulos: Trataremos con valores nulos y aprenderemos técnicas para manejarlos.
Transformación de columnas: Veremos cómo transformar y manipular columnas en un DataFrame.
Estadísticas descriptivas: Obtendremos estadísticas descriptivas de nuestros datos para entender mejor su distribución y tendencias.
Correlaciones: Exploraremos las correlaciones entre diferentes variables en nuestros datos.
Ejemplos prácticos#
Aplicaremos estas técnicas en ejemplos prácticos para mejorar la calidad de los datos y para identificar tendencias y patrones en datos de ventas y marketing.
Dataset#
Para este cuaderno, utilizaremos el dataset bank_marketing
del repositorio de Machine Learning de UCI. Este conjunto de datos nos proporcionará una gran oportunidad para aplicar y practicar las técnicas que aprenderemos.
## Carga de datos
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
data=pd.read_csv('bank_marketing.csv')
# Mostrar las primeras filas
display(data.head())
# Información general del DataFrame
display(data.info())
age | job | marital | education | default | balance | housing | loan | contact | day_of_week | month | duration | campaign | pdays | previous | poutcome | HasTermDeposit | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 58 | management | married | tertiary | no | 2143 | yes | no | NaN | 5 | may | 261 | 1 | -1 | 0 | NaN | no |
1 | 44 | technician | single | secondary | no | 29 | yes | no | NaN | 5 | may | 151 | 1 | -1 | 0 | NaN | no |
2 | 33 | entrepreneur | married | secondary | no | 2 | yes | yes | NaN | 5 | may | 76 | 1 | -1 | 0 | NaN | no |
3 | 47 | blue-collar | married | NaN | no | 1506 | yes | no | NaN | 5 | may | 92 | 1 | -1 | 0 | NaN | no |
4 | 33 | NaN | single | NaN | no | 1 | no | no | NaN | 5 | may | 198 | 1 | -1 | 0 | NaN | no |
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 45211 entries, 0 to 45210
Data columns (total 17 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 age 45211 non-null int64
1 job 44923 non-null object
2 marital 45211 non-null object
3 education 43354 non-null object
4 default 45211 non-null object
5 balance 45211 non-null int64
6 housing 45211 non-null object
7 loan 45211 non-null object
8 contact 32191 non-null object
9 day_of_week 45211 non-null int64
10 month 45211 non-null object
11 duration 45211 non-null int64
12 campaign 45211 non-null int64
13 pdays 45211 non-null int64
14 previous 45211 non-null int64
15 poutcome 8252 non-null object
16 HasTermDeposit 45211 non-null object
dtypes: int64(7), object(10)
memory usage: 5.9+ MB
None
Limpieza de datos#
La limpieza de datos es un paso crucial en el proceso de análisis de datos. Los datos limpios son esenciales para obtener resultados precisos y confiables. En esta sección, aprenderemos a limpiar y manipular datos utilizando Python y la biblioteca Pandas.
Note
Pandas es una biblioteca de Python que proporciona estructuras de datos y herramientas de análisis de datos. Es ampliamente utilizada en la comunidad de ciencia de datos y es una de las bibliotecas más populares para el análisis de datos en Python.
Para el proceso de limpieza y manipulación de datos, utilizaremos las siguientes funciones y métodos de Pandas:
isnull()
: Comprueba si hay valores nulos en un DataFrame.dropna()
: Elimina filas o columnas con valores nulos de un DataFrame.fillna()
: Rellena los valores nulos con un valor específico.astype()
: Convierte el tipo de datos de una columna a un tipo de datos específico.describe()
: Muestra estadísticas descriptivas de un DataFrame.corr()
: Calcula la correlación entre columnas de un DataFrame.plot()
: Crea gráficos a partir de los datos de un DataFrame.value_counts()
: Cuenta los valores únicos en una columna de un DataFrame.
# Identificar valores faltantes
display(data.isnull().sum())
# Si hay valores faltantes, aplicar estrategias de imputación o eliminación
age 0
job 288
marital 0
education 1857
default 0
balance 0
housing 0
loan 0
contact 13020
day_of_week 0
month 0
duration 0
campaign 0
pdays 0
previous 0
poutcome 36959
HasTermDeposit 0
dtype: int64
## Variable job
data['job'].value_counts(dropna=False)
job
blue-collar 9732
management 9458
technician 7597
admin. 5171
services 4154
retired 2264
self-employed 1579
entrepreneur 1487
unemployed 1303
housemaid 1240
student 938
NaN 288
Name: count, dtype: int64
## Estrategias de imputación
# Imputación con la moda
data_1=data.copy()
data_1['job'].fillna(data_1['job'].mode()[0], inplace=True)
# Imputación con la categoría 'Desconocido'
data_2=data.copy()
data_2['job'].fillna('Desconocido', inplace=True)
# Eliminación de las filas con valores faltantes
data_3=data.copy()
data_3.dropna(subset=['job'], inplace=True)
# Comparación de las estrategias de imputación
print(data['job'].value_counts(dropna=False))
print(data_1['job'].value_counts(dropna=False))
print(data_2['job'].value_counts(dropna=False))
print(data_3['job'].value_counts(dropna=False))
job
blue-collar 9732
management 9458
technician 7597
admin. 5171
services 4154
retired 2264
self-employed 1579
entrepreneur 1487
unemployed 1303
housemaid 1240
student 938
NaN 288
Name: count, dtype: int64
job
blue-collar 10020
management 9458
technician 7597
admin. 5171
services 4154
retired 2264
self-employed 1579
entrepreneur 1487
unemployed 1303
housemaid 1240
student 938
Name: count, dtype: int64
job
blue-collar 9732
management 9458
technician 7597
admin. 5171
services 4154
retired 2264
self-employed 1579
entrepreneur 1487
unemployed 1303
housemaid 1240
student 938
Desconocido 288
Name: count, dtype: int64
job
blue-collar 9732
management 9458
technician 7597
admin. 5171
services 4154
retired 2264
self-employed 1579
entrepreneur 1487
unemployed 1303
housemaid 1240
student 938
Name: count, dtype: int64
## Tratamiento de variables nulas numéricas
Ejemplo=pd.DataFrame({'A':[1,2,None,None,3,4,None,5,6,None,7,8,None,9,10,None],})
Ejemplo
A | |
---|---|
0 | 1.0 |
1 | 2.0 |
2 | NaN |
3 | NaN |
4 | 3.0 |
5 | 4.0 |
6 | NaN |
7 | 5.0 |
8 | 6.0 |
9 | NaN |
10 | 7.0 |
11 | 8.0 |
12 | NaN |
13 | 9.0 |
14 | 10.0 |
15 | NaN |
## Imputación con la media
Ejemplo_1=Ejemplo.copy()
Ejemplo_1['A'].fillna(Ejemplo_1['A'].mean(), inplace=True)
Ejemplo_1
A | |
---|---|
0 | 1.0 |
1 | 2.0 |
2 | 5.5 |
3 | 5.5 |
4 | 3.0 |
5 | 4.0 |
6 | 5.5 |
7 | 5.0 |
8 | 6.0 |
9 | 5.5 |
10 | 7.0 |
11 | 8.0 |
12 | 5.5 |
13 | 9.0 |
14 | 10.0 |
15 | 5.5 |
## Imputación con la mediana
Ejemplo_2=Ejemplo.copy()
Ejemplo_2['A'].fillna(Ejemplo_2['A'].median(), inplace=True)
Ejemplo_2
A | |
---|---|
0 | 1.0 |
1 | 2.0 |
2 | 5.5 |
3 | 5.5 |
4 | 3.0 |
5 | 4.0 |
6 | 5.5 |
7 | 5.0 |
8 | 6.0 |
9 | 5.5 |
10 | 7.0 |
11 | 8.0 |
12 | 5.5 |
13 | 9.0 |
14 | 10.0 |
15 | 5.5 |
## Imputación con la moda
Ejemplo_3=Ejemplo.copy()
Ejemplo_3['A'].fillna(Ejemplo_3['A'].mode()[0], inplace=True)
Ejemplo_3
A | |
---|---|
0 | 1.0 |
1 | 2.0 |
2 | 1.0 |
3 | 1.0 |
4 | 3.0 |
5 | 4.0 |
6 | 1.0 |
7 | 5.0 |
8 | 6.0 |
9 | 1.0 |
10 | 7.0 |
11 | 8.0 |
12 | 1.0 |
13 | 9.0 |
14 | 10.0 |
15 | 1.0 |
## Imputación con un valor constante
Ejemplo_4=Ejemplo.copy()
Ejemplo_4['A'].fillna(-999, inplace=True)
Ejemplo_4
A | |
---|---|
0 | 1.0 |
1 | 2.0 |
2 | -999.0 |
3 | -999.0 |
4 | 3.0 |
5 | 4.0 |
6 | -999.0 |
7 | 5.0 |
8 | 6.0 |
9 | -999.0 |
10 | 7.0 |
11 | 8.0 |
12 | -999.0 |
13 | 9.0 |
14 | 10.0 |
15 | -999.0 |
## Eliminación de las filas con valores faltantes
Ejemplo_5=Ejemplo.copy()
Ejemplo_5.dropna(subset=['A'], inplace=True)
Ejemplo_5
A | |
---|---|
0 | 1.0 |
1 | 2.0 |
4 | 3.0 |
5 | 4.0 |
7 | 5.0 |
8 | 6.0 |
10 | 7.0 |
11 | 8.0 |
13 | 9.0 |
14 | 10.0 |
Corrección de tipos de datos#
El primer paso en el proceso de limpieza de datos es corregir los tipos de datos. A menudo, los datos se almacenan en formatos que no son adecuados para el análisis. Por ejemplo, una columna que debería ser numérica se almacena como una cadena. En esta sección, aprenderemos a corregir los tipos de datos de un DataFrame.
Note
Para corregir los tipos de datos de un DataFrame, utilizaremos el método astype()
de Pandas. Este método nos permite convertir el tipo de datos de una columna a un tipo de datos específico. Por ejemplo, si queremos convertir una columna a un tipo de datos numérico, podemos usar el siguiente código:
df['column_name'] = df['column_name'].astype('float')
## Variable day_of_month
data['day_of_month'].value_counts(dropna=False)
data['month'].value_counts(dropna=False)
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File ~\miniconda3\Lib\site-packages\pandas\core\indexes\base.py:3790, in Index.get_loc(self, key)
3789 try:
-> 3790 return self._engine.get_loc(casted_key)
3791 except KeyError as err:
File index.pyx:152, in pandas._libs.index.IndexEngine.get_loc()
File index.pyx:181, in pandas._libs.index.IndexEngine.get_loc()
File pandas\_libs\hashtable_class_helper.pxi:7080, in pandas._libs.hashtable.PyObjectHashTable.get_item()
File pandas\_libs\hashtable_class_helper.pxi:7088, in pandas._libs.hashtable.PyObjectHashTable.get_item()
KeyError: 'day_of_month'
The above exception was the direct cause of the following exception:
KeyError Traceback (most recent call last)
Cell In[12], line 3
1 ## Variable day_of_month
----> 3 data['day_of_month'].value_counts(dropna=False)
4 data['month'].value_counts(dropna=False)
File ~\miniconda3\Lib\site-packages\pandas\core\frame.py:3893, in DataFrame.__getitem__(self, key)
3891 if self.columns.nlevels > 1:
3892 return self._getitem_multilevel(key)
-> 3893 indexer = self.columns.get_loc(key)
3894 if is_integer(indexer):
3895 indexer = [indexer]
File ~\miniconda3\Lib\site-packages\pandas\core\indexes\base.py:3797, in Index.get_loc(self, key)
3792 if isinstance(casted_key, slice) or (
3793 isinstance(casted_key, abc.Iterable)
3794 and any(isinstance(x, slice) for x in casted_key)
3795 ):
3796 raise InvalidIndexError(key)
-> 3797 raise KeyError(key) from err
3798 except TypeError:
3799 # If we have a listlike key, _check_indexing_error will raise
3800 # InvalidIndexError. Otherwise we fall through and re-raise
3801 # the TypeError.
3802 self._check_indexing_error(key)
KeyError: 'day_of_month'
### Hagamos una cadena de texto de la forma dd-mmm
## debemos cambiar el tipo de dato de day_of_month como una cadena de texto (string)
data['day_of_month']=data['day_of_month'].astype(str)
data['day_of_month']
0 5
1 5
2 5
3 5
4 5
..
45206 17
45207 17
45208 17
45209 17
45210 17
Name: day_of_month, Length: 45211, dtype: object
## Creamos la variable day_month
data['day_month']=data['day_of_month']+'-'+data['month']+'-2023'
data['day_month']
0 5-may-2023
1 5-may-2023
2 5-may-2023
3 5-may-2023
4 5-may-2023
...
45206 17-nov-2023
45207 17-nov-2023
45208 17-nov-2023
45209 17-nov-2023
45210 17-nov-2023
Name: day_month, Length: 45211, dtype: object
## La convertimos a tipo de dato fecha
data['day_month']=pd.to_datetime(data['day_month'], format='%d-%b-%Y')
data['day_month']
0 2023-05-05
1 2023-05-05
2 2023-05-05
3 2023-05-05
4 2023-05-05
...
45206 2023-11-17
45207 2023-11-17
45208 2023-11-17
45209 2023-11-17
45210 2023-11-17
Name: day_month, Length: 45211, dtype: datetime64[ns]
### Cambiar el tipo de dato trae sus ventajas
from datetime import datetime
today=datetime.now()
data['days_to_today']=(today-data['day_month']).dt.days
data['days_to_today']
0 298
1 298
2 298
3 298
4 298
...
45206 102
45207 102
45208 102
45209 102
45210 102
Name: days_to_today, Length: 45211, dtype: int64