Guía de Estudio: Análisis Exploratorio de Datos (EDA) Básico con Python
Licencia: Este documento se rige por la Licencia Pública General de GNU (GNU
General Public License v3.0). Puedes encontrar una copia de la licencia en
https://www.gnu.org/licenses/gpl-3.0.html.
Fecha de Creación: 2025-05-08
1. Introducción al Análisis Exploratorio de Datos (EDA)
¿Qué es EDA?
El Análisis Exploratorio de Datos (EDA, por sus siglas en inglés: Exploratory Data
Analysis) es el proceso de investigar conjuntos de datos para resumir sus principales
características, a menudo con métodos visuales. Es un paso crucial en el ciclo de
vida de la ciencia de datos, ya que permite comprender mejor los datos antes de
aplicar técnicas de modelado más complejas.
¿Por qué es importante el EDA?
• Descubrir patrones: Identificar tendencias, correlaciones y anomalías.
• Detectar errores: Encontrar valores faltantes, atípicos o incorrectos.
• Formular hipótesis: Generar preguntas e ideas basadas en los datos.
• Seleccionar variables: Determinar qué variables son relevantes para un
análisis posterior.
• Preparar los datos: Informar sobre la necesidad de limpieza y transformación
de datos.
• Comunicar hallazgos: Presentar de forma clara las primeras percepciones
sobre los datos.
2. Prerrequisitos y Configuración del Entorno
Bibliotecas de Python Esenciales
Para realizar EDA en Python, utilizaremos principalmente las siguientes bibliotecas:
• Pandas: Para la manipulación y análisis de datos estructurados
(principalmente DataFrames).
• NumPy: Para operaciones numéricas eficientes, especialmente con arrays.
• Matplotlib: Para la creación de gráficos estáticos, animados e interactivos.
• Seaborn: Construida sobre Matplotlib, proporciona una interfaz de alto nivel
para dibujar gráficos estadísticos atractivos e informativos.
Instalación de Bibliotecas
Si no tienes estas bibliotecas instaladas, puedes hacerlo usando pip:
Bash
pip install pandas numpy matplotlib seaborn jupyterlab
(Se recomienda jupyterlab o notebook para un entorno interactivo ideal para EDA).
Importación de Bibliotecas
En tu script de Python o Jupyter Notebook, comienza importando las bibliotecas:
Python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Configuraciones adicionales (opcional pero recomendado)
# Para que los gráficos de Matplotlib se muestren en el notebook
%matplotlib inline
# Estilo de los gráficos de Seaborn
sns.set_theme(style="whitegrid")
# Ignorar advertencias (usar con precaución)
# import warnings
# warnings.filterwarnings('ignore')
3. Carga de Datos
Los datos pueden provenir de diversas fuentes. El formato más común es CSV
(Comma Separated Values).
Python
# Cargar datos desde un archivo CSV
try:
df = pd.read_csv('tu_archivo_de_datos.csv')
print("Datos cargados exitosamente!")
except FileNotFoundError:
print("Error: El archivo 'tu_archivo_de_datos.csv' no fue encontrado.")
print("Por favor, asegúrate de que el archivo está en el directorio correcto o
proporciona la ruta completa.")
# Para fines de demostración, crearemos un DataFrame de ejemplo si el archivo no
se encuentra
print("Creando un DataFrame de ejemplo para continuar con el tutorial...")
data_ejemplo = {
'ID_Estudiante': range(1, 101),
'Edad': np.random.randint(18, 25, size=100),
'Genero': np.random.choice(['Masculino', 'Femenino', 'Otro'], size=100, p=[0.45,
0.45, 0.1]),
'Calificacion_Matematicas': np.random.normal(loc=75, scale=10,
size=100).clip(0, 100),
'Calificacion_Lenguaje': np.random.normal(loc=80, scale=8, size=100).clip(0,
100),
'Horas_Estudio_Semanal': np.random.uniform(5, 20, size=100),
'Asistencia_Porcentaje': np.random.uniform(70, 100, size=100)
df = pd.DataFrame(data_ejemplo)
# Introducir algunos valores nulos para el ejemplo de manejo de nulos
for col in ['Calificacion_Matematicas', 'Horas_Estudio_Semanal']:
idx_nulos = np.random.choice(df.index, size=5, replace=False)
df.loc[idx_nulos, col] = np.nan
df.loc[np.random.choice(df.index, size=3, replace=False), 'Genero'] = np.nan #
Nulos en categórica
print("DataFrame de ejemplo creado.")
Nota: Reemplaza 'tu_archivo_de_datos.csv' con el nombre o la ruta de tu archivo. Si
no tienes un archivo CSV a mano, el código anterior creará un DataFrame de ejemplo
para que puedas seguir el tutorial.
4. Inspección Inicial de los Datos
Una vez cargados los datos, el primer paso es obtener una visión general.
df.head() y df.tail()
Muestra las primeras o últimas filas del DataFrame.
Python
print("Primeras 5 filas del DataFrame:")
print(df.head())
print("\nÚltimas 5 filas del DataFrame:")
print(df.tail())
df.info()
Proporciona un resumen conciso del DataFrame, incluyendo el tipo de datos de cada
columna, el número de valores no nulos y el uso de memoria.
Python
print("\nInformación del DataFrame:")
df.info()
df.describe()
Genera estadísticas descriptivas para las columnas numéricas (como media,
desviación estándar, mínimo, máximo, cuartiles). Para columnas categóricas (si se
incluye include='object' o include='all'), mostrará conteo, valores únicos, el más
frecuente (top) y su frecuencia (freq).
Python
print("\nEstadísticas descriptivas (columnas numéricas):")
print(df.describe())
print("\nEstadísticas descriptivas (columnas categóricas):")
print(df.describe(include=['object'])) # O include='all' para todos los tipos
df.shape
Devuelve una tupla con las dimensiones del DataFrame (número de filas, número de
columnas).
Python
print(f"\nDimensiones del DataFrame (filas, columnas): {df.shape}")
df.dtypes
Muestra el tipo de dato de cada columna.
Python
print("\nTipos de datos de cada columna:")
print(df.dtypes)
5. Manejo de Valores Faltantes (Missing Values)
Los valores faltantes son comunes en los conjuntos de datos y pueden afectar el
análisis.
Identificar Valores Faltantes
Usa .isnull().sum() para contar el número de valores nulos por columna.
Python
print("\nConteo de valores faltantes por columna:")
print(df.isnull().sum())
Estrategias para Manejar Valores Faltantes
1. Eliminación:
o df.dropna(): Elimina filas (por defecto) o columnas (axis=1) que
contienen valores nulos.
o Pros: Simple.
o Contras: Puede perderse información valiosa si se eliminan muchas
filas/columnas.
Python
# df_sin_nulos_filas = df.dropna() # Elimina filas con al menos un nulo
# df_sin_nulos_columnas = df.dropna(axis=1) # Elimina columnas con al menos un
nulo
# print(f"Shape original: {df.shape}, Shape después de dropna (filas):
{df_sin_nulos_filas.shape}")
2. Imputación:
o df.fillna(): Rellena los valores nulos con un valor específico.
▪ Variables numéricas: Usar la media (df['columna'].mean()),
mediana (df['columna'].median()) o un valor constante (ej. 0). La
mediana es generalmente preferible si hay outliers.
▪ Variables categóricas: Usar la moda (df['columna'].mode()[0]) o
una categoría específica (ej. "Desconocido").
Python
# Imputar numéricas con la mediana
if 'Calificacion_Matematicas' in df.columns and
df['Calificacion_Matematicas'].isnull().any():
mediana_cal_mat = df['Calificacion_Matematicas'].median()
df['Calificacion_Matematicas'].fillna(mediana_cal_mat, inplace=True)
print(f"\nValores nulos en 'Calificacion_Matematicas' imputados con la mediana:
{mediana_cal_mat}")
if 'Horas_Estudio_Semanal' in df.columns and
df['Horas_Estudio_Semanal'].isnull().any():
media_horas_estudio = df['Horas_Estudio_Semanal'].mean()
df['Horas_Estudio_Semanal'].fillna(media_horas_estudio, inplace=True)
print(f"Valores nulos en 'Horas_Estudio_Semanal' imputados con la media:
{media_horas_estudio}")
# Imputar categóricas con la moda
if 'Genero' in df.columns and df['Genero'].isnull().any():
moda_genero = df['Genero'].mode()[0]
df['Genero'].fillna(moda_genero, inplace=True)
print(f"Valores nulos en 'Genero' imputados con la moda: {moda_genero}")
print("\nConteo de valores faltantes después de la imputación:")
print(df.isnull().sum())
o inplace=True modifica el DataFrame directamente. Sin él, se devuelve
un nuevo DataFrame.
6. Limpieza Básica de Datos
Identificar y Manejar Duplicados
Python
# Verificar si hay filas duplicadas
duplicados = df.duplicated().sum()
print(f"\nNúmero de filas duplicadas: {duplicados}")
# Eliminar filas duplicadas (conservando la primera aparición)
if duplicados > 0:
df.drop_duplicates(inplace=True)
print(f"Filas duplicadas eliminadas. Nuevo shape: {df.shape}")
Corregir Tipos de Datos
A veces, los datos se cargan con tipos incorrectos (ej. números como texto).
Python
# Ejemplo: Si una columna 'Edad' se cargara como objeto (string)
# df['Edad'] = df['Edad'].astype(int)
# Verificar tipos de datos nuevamente si se hicieron cambios
# print("\nTipos de datos después de posibles correcciones:")
# print(df.dtypes)
7. Análisis Univariado
Análisis de variables individuales para entender su distribución.
Variables Numéricas
• Histogramas: Muestran la frecuencia de los valores en intervalos (bins).
Python
if 'Edad' in df.columns:
plt.figure(figsize=(8, 5))
sns.histplot(df['Edad'], kde=True, bins=10) # kde=True añade una estimación de
densidad del kernel
plt.title('Distribución de Edades')
plt.xlabel('Edad')
plt.ylabel('Frecuencia')
plt.show()
if 'Calificacion_Matematicas' in df.columns:
plt.figure(figsize=(8, 5))
sns.histplot(df['Calificacion_Matematicas'], kde=True, color='skyblue')
plt.title('Distribución de Calificaciones de Matemáticas')
plt.xlabel('Calificación')
plt.ylabel('Frecuencia')
plt.show()
• Box Plots (Diagramas de Caja): Visualizan la mediana, cuartiles, rango
intercuartílico (IQR) y posibles outliers.
Python
if 'Calificacion_Lenguaje' in df.columns:
plt.figure(figsize=(8, 5))
sns.boxplot(x=df['Calificacion_Lenguaje'], color='lightgreen')
plt.title('Box Plot de Calificaciones de Lenguaje')
plt.xlabel('Calificación')
plt.show()
• Density Plots (Gráficos de Densidad): Similar al histograma pero muestra
una estimación continua de la distribución.
Python
if 'Horas_Estudio_Semanal' in df.columns:
plt.figure(figsize=(8, 5))
sns.kdeplot(df['Horas_Estudio_Semanal'], fill=True, color='salmon')
plt.title('Distribución de Horas de Estudio Semanal')
plt.xlabel('Horas de Estudio')
plt.ylabel('Densidad')
plt.show()
Variables Categóricas
• Count Plots / Bar Charts (Gráficos de Barras): Muestran la frecuencia de
cada categoría.
Python
if 'Genero' in df.columns:
plt.figure(figsize=(8, 5))
sns.countplot(data=df, x='Genero', palette='pastel')
plt.title('Distribución por Género')
plt.xlabel('Género')
plt.ylabel('Conteo')
plt.show()
# También se puede obtener el conteo con value_counts()
print("\nConteo de valores para 'Genero':")
print(df['Genero'].value_counts())
• Pie Charts (Gráficos de Pastel): Muestran la proporción de cada categoría.
Útil para pocas categorías.
Python
if 'Genero' in df.columns and df['Genero'].nunique() < 7: # Útil para pocas categorías
plt.figure(figsize=(7, 7))
df['Genero'].value_counts().plot(kind='pie', autopct='%1.1f%%', startangle=90,
colors=sns.color_palette('pastel'))
plt.title('Proporción por Género')
plt.ylabel('') # Ocultar la etiqueta del eje y
plt.show()
8. Análisis Bivariado
Análisis de la relación entre dos variables.
Numérica vs. Numérica
• Scatter Plots (Diagramas de Dispersión): Muestran la relación entre dos
variables numéricas.
Python
if 'Horas_Estudio_Semanal' in df.columns and 'Calificacion_Matematicas' in
df.columns:
plt.figure(figsize=(10, 6))
sns.scatterplot(data=df, x='Horas_Estudio_Semanal', y='Calificacion_Matematicas',
hue='Genero' if 'Genero' in df.columns else None)
plt.title('Horas de Estudio vs. Calificación en Matemáticas')
plt.xlabel('Horas de Estudio Semanal')
plt.ylabel('Calificación en Matemáticas')
plt.legend(title='Género')
plt.show()
• Matriz de Correlación y Heatmap: La correlación mide la relación lineal entre
variables numéricas (rango de -1 a 1). Un heatmap visualiza la matriz de
correlación con colores.
Python
# Seleccionar solo columnas numéricas para la correlación
df_numeric = df.select_dtypes(include=np.number)
if not df_numeric.empty:
correlation_matrix = df_numeric.corr()
print("\nMatriz de Correlación:")
print(correlation_matrix)
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt=".2f",
linewidths=.5)
plt.title('Heatmap de Correlación entre Variables Numéricas')
plt.show()
else:
print("\nNo hay suficientes columnas numéricas para calcular la matriz de
correlación.")
Categórica vs. Numérica
• Box Plots (Agrupados): Compara la distribución de una variable numérica a
través de diferentes categorías.
Python
if 'Genero' in df.columns and 'Calificacion_Lenguaje' in df.columns:
plt.figure(figsize=(10, 6))
sns.boxplot(data=df, x='Genero', y='Calificacion_Lenguaje', palette='Set2')
plt.title('Calificación en Lenguaje por Género')
plt.xlabel('Género')
plt.ylabel('Calificación en Lenguaje')
plt.show()
• Violin Plots: Combinan características de box plots y density plots.
Python
if 'Genero' in df.columns and 'Horas_Estudio_Semanal' in df.columns:
plt.figure(figsize=(10, 6))
sns.violinplot(data=df, x='Genero', y='Horas_Estudio_Semanal', palette='husl')
plt.title('Horas de Estudio Semanal por Género')
plt.xlabel('Género')
plt.ylabel('Horas de Estudio Semanal')
plt.show()
• Bar Plots (con agregación): Muestran una medida de tendencia central (ej.
media) de una variable numérica para cada categoría.
Python
if 'Genero' in df.columns and 'Asistencia_Porcentaje' in df.columns:
plt.figure(figsize=(10, 6))
# Por defecto, Seaborn calcula la media. Se puede cambiar con el parámetro
'estimator'
sns.barplot(data=df, x='Genero', y='Asistencia_Porcentaje', palette='viridis',
estimator=np.mean, errorbar='sd')
plt.title('Media de Asistencia (%) por Género (con desviación estándar)')
plt.xlabel('Género')
plt.ylabel('Media de Asistencia (%)')
plt.show()
Categórica vs. Categórica
• Tablas de Contingencia (Cross-tabulation): Muestran la frecuencia conjunta
de dos variables categóricas.
Python
# Suponiendo que tenemos otra variable categórica, ej. 'Nivel_Socioeconomico'
# Para el ejemplo, la crearemos si no existe
if 'Nivel_Socioeconomico' not in df.columns:
df['Nivel_Socioeconomico'] = np.random.choice(['Bajo', 'Medio', 'Alto'],
size=df.shape[0], p=[0.3, 0.5, 0.2])
if 'Genero' in df.columns and 'Nivel_Socioeconomico' in df.columns:
contingency_table = pd.crosstab(df['Genero'], df['Nivel_Socioeconomico'])
print("\nTabla de Contingencia (Género vs. Nivel Socioeconómico):")
print(contingency_table)
# Visualizar con un heatmap
plt.figure(figsize=(8, 6))
sns.heatmap(contingency_table, annot=True, fmt='d', cmap='Blues')
plt.title('Tabla de Contingencia: Género vs. Nivel Socioeconómico')
plt.show()
• Gráficos de Barras Agrupados o Apilados:
Python
if 'Genero' in df.columns and 'Nivel_Socioeconomico' in df.columns:
plt.figure(figsize=(10, 6))
sns.countplot(data=df, x='Genero', hue='Nivel_Socioeconomico', palette='Spectral')
plt.title('Conteo de Género Agrupado por Nivel Socioeconómico')
plt.xlabel('Género')
plt.ylabel('Conteo')
plt.legend(title='Nivel Socioeconómico')
plt.show()
9. (Opcional) Detección Básica de Outliers (Valores Atípicos)
Los outliers son puntos de datos que difieren significativamente de otras
observaciones.
• Usando Box Plots: Los puntos fuera de los "bigotes" del box plot suelen
considerarse outliers. Ya los hemos visto en el análisis univariado.
• Método del Rango Intercuartílico (IQR): Un outlier se define comúnmente
como un valor que está:
o Menor que Q1−1.5×IQR
o Mayor que Q3+1.5×IQR Donde Q1 es el primer cuartil, Q3 es el tercer
cuartil, e IQR=Q3−Q1.
Python
def detectar_outliers_iqr(dataframe, columna):
if columna not in dataframe.columns or not
pd.api.types.is_numeric_dtype(dataframe[columna]):
print(f"La columna '{columna}' no es numérica o no existe.")
return pd.Series(dtype=bool) # Devuelve una Serie vacía o de False
Q1 = dataframe[columna].quantile(0.25)
Q3 = dataframe[columna].quantile(0.75)
IQR = Q3 - Q1
limite_inferior = Q1 - 1.5 * IQR
limite_superior = Q3 + 1.5 * IQR
outliers = (dataframe[columna] < limite_inferior) | (dataframe[columna] >
limite_superior)
return outliers
if 'Calificacion_Matematicas' in df.columns:
outliers_matematicas = detectar_outliers_iqr(df, 'Calificacion_Matematicas')
print(f"\nNúmero de outliers en 'Calificacion_Matematicas' según IQR:
{outliers_matematicas.sum()}")
if outliers_matematicas.sum() > 0:
print("Outliers detectados:")
print(df[outliers_matematicas][['ID_Estudiante', 'Calificacion_Matematicas']])
# Qué hacer con los outliers depende del contexto:
# 1. Investigarlos: ¿Son errores de entrada o valores genuinos extremos?
# 2. Caparlos (winsorizing): Limitar los valores al límite inferior/superior.
# 3. Transformarlos (ej. logaritmo).
# 4. Eliminarlos (con precaución).
# 5. Considerar modelos robustos a outliers.
10. Conclusión y Próximos Pasos
El EDA es un proceso iterativo. A medida que exploras, puedes descubrir nuevas
preguntas o la necesidad de una limpieza de datos más profunda.
Resumen de los Pasos Clave del EDA:
1. Entender las variables: Tipos de datos, significados.
2. Limpieza de datos: Manejar nulos, duplicados, errores.
3. Análisis univariado: Distribución de cada variable.
4. Análisis bivariado (y multivariado): Relaciones entre variables.
5. Identificar patrones y anomalías.
6. Formular hipótesis y guiar los siguientes pasos.
Próximos Pasos Después del EDA:
• Ingeniería de Características (Feature Engineering): Crear nuevas variables
a partir de las existentes.
• Preprocesamiento de Datos: Escalar, codificar variables categóricas para
modelos de Machine Learning.
• Modelado Predictivo: Construir y evaluar modelos de Machine Learning.
• Comunicación de Resultados: Crear informes o dashboards más elaborados.
¡Este tutorial cubre los fundamentos! La práctica constante con diferentes conjuntos
de datos te ayudará a dominar el arte del EDA.