Análisis de Datos Categóricos con Pandas: Más Allá de lo Numérico 📊
Este tutorial te guiará a través de las técnicas esenciales para trabajar con datos categóricos en Pandas, desde su identificación y codificación hasta la exploración y visualización avanzada. Descubre cómo transformar variables cualitativas en información procesable para tus modelos de Machine Learning y análisis. Prepárate para desentrañar el poder de los datos no numéricos.
Introducción: El Poder Oculto de los Datos Categóricos ✨
En el vasto universo de la ciencia de datos, a menudo nos centramos en los números: medias, medianas, desviaciones estándar. Sin embargo, una gran parte de la información crucial en nuestros conjuntos de datos reside en las variables categóricas. Estas variables, que representan cualidades o características sin un valor numérico inherente (como colores, géneros, estados civiles, tipos de producto), son vitales para entender el contexto y las relaciones dentro de nuestros datos.
Ignorar o manejar incorrectamente los datos categóricos puede llevar a modelos de Machine Learning subóptimos y a análisis superficiales. Pandas, la biblioteca de manipulación de datos por excelencia en Python, ofrece herramientas robustas para lidiar con estos datos, permitiéndonos transformarlos en un formato que tanto los humanos como los algoritmos puedan comprender y utilizar eficazmente.
En este tutorial, exploraremos en profundidad cómo identificar, limpiar, codificar y analizar variables categóricas utilizando Pandas y algunas herramientas complementarias. ¡Prepárate para llevar tus habilidades de análisis de datos al siguiente nivel!
1. ¿Qué Son los Datos Categóricos y Por Qué Importan? 🤔
Los datos categóricos, también conocidos como datos cualitativos, son aquellos que representan categorías o grupos. Se dividen principalmente en dos tipos:
- Nominales: Categorías sin un orden intrínseco (ej. color de ojos, país de origen, tipo de fruta). No tiene sentido decir que 'azul' es mayor que 'verde'.
- Ordinales: Categorías con un orden natural o una jerarquía (ej. nivel educativo (primaria, secundaria, universidad), talla de ropa (S, M, L, XL), satisfacción del cliente (mala, regular, buena)). Aquí, 'universidad' es 'mayor' que 'primaria'.
La Importancia de los Datos Categóricos
Entender y procesar correctamente los datos categóricos es crucial por varias razones:
- Contexto y Significado: Aportan un contexto rico a los datos numéricos. Por ejemplo, saber que el precio de una casa es alto es una cosa, pero saber que es una casa en una 'zona exclusiva' (categórico) proporciona mucha más información.
- Modelos de Machine Learning: La mayoría de los algoritmos de ML no pueden trabajar directamente con texto. Necesitan que las categorías se conviertan en representaciones numéricas.
- Análisis Descriptivo: Permiten segmentar y comparar grupos, revelando patrones y anomalías que no serían evidentes solo con datos numéricos.
- Reducción de Dimensionalidad: Un buen manejo puede reducir la complejidad del conjunto de datos.
2. Identificación y Exploración de Variables Categóricas en Pandas 🔍
El primer paso es identificar qué columnas de nuestro DataFrame son categóricas. Pandas nos ofrece varias formas de hacerlo.
Comencemos cargando algunos datos de ejemplo. Usaremos el dataset 'Titanic', clásico para ilustrar el manejo de datos.
import pandas as pd
import numpy as np
# Cargar el dataset de Titanic
df = pd.read_csv('https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv')
# Mostrar las primeras filas
print(df.head())
# Información general del DataFrame
print(df.info())
Identificando Columnas Categóricas
Podemos identificar las columnas categóricas observando su dtype (tipo de dato) y el número de valores únicos.
object: Generalmente indica cadenas de texto, que suelen ser categóricas.category: Es un tipo de dato específico de Pandas para variables categóricas, muy eficiente en memoria.
# Obtener tipos de datos
print(df.dtypes)
# Contar valores únicos por columna
for col in df.columns:
print(f"Columna '{col}': {df[col].nunique()} valores únicos")
# Identificar columnas con dtype 'object'
object_cols = df.select_dtypes(include='object').columns
print(f"Columnas de tipo 'object': {list(object_cols)}")
# Considerar columnas con pocos valores únicos como categóricas (ej. menos de 10-20)
# ¡Cuidado! Un ID con muchos valores únicos también es 'object' pero no categórico.
low_cardinality_cols = [col for col in df.columns if df[col].nunique() < 10 and df[col].dtype != 'int64' and df[col].dtype != 'float64']
print(f"Columnas con baja cardinalidad (posiblemente categóricas): {list(low_cardinality_cols)}")
Exploración de Datos Categóricos
Una vez identificadas, es crucial explorar la distribución de estas variables.
value_counts(): Para ver la frecuencia de cada categoría.crosstab(): Para analizar la relación entre dos o más variables categóricas.
# Explorar 'Sex'
print("\nDistribución de 'Sex':")
print(df['Sex'].value_counts())
print(f"Porcentajes de 'Sex':\n{df['Sex'].value_counts(normalize=True) * 100}")
# Explorar 'Embarked'
print("\nDistribución de 'Embarked':")
print(df['Embarked'].value_counts(dropna=False)) # Incluir NaN
# Explorar 'Pclass' (aunque es numérica, es ordinal categórica en este contexto)
print("\nDistribución de 'Pclass':")
print(df['Pclass'].value_counts())
# Usando crosstab para ver la relación entre 'Sex' y 'Survived'
print("\nRelación entre 'Sex' y 'Survived':")
print(pd.crosstab(df['Sex'], df['Survived']))
# Crosstab con porcentajes (por fila)
print("\nPorcentaje de Supervivencia por Sexo (por fila):")
print(pd.crosstab(df['Sex'], df['Survived'], normalize='index') * 100)
# Visualización básica con Matplotlib/Seaborn
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(10, 5))
sns.countplot(data=df, x='Sex', hue='Survived')
plt.title('Supervivencia por Sexo')
plt.show()
plt.figure(figsize=(10, 5))
sns.countplot(data=df, x='Pclass', hue='Survived')
plt.title('Supervivencia por Clase de Pasaje')
plt.show()
3. Preprocesamiento: Limpieza y Transformación de Datos Categóricos 🧹
Antes de codificar, es esencial limpiar y estandarizar las variables categóricas. Esto incluye manejar valores faltantes y corregir inconsistencias.
Manejo de Valores Faltantes (NaN)
Los valores NaN en columnas categóricas pueden ser tratados de varias maneras:
- Eliminación: Eliminar filas o columnas con
NaN. (¡Usar con precaución para no perder datos importantes!) - Imputación por Moda: Reemplazar
NaNcon la categoría más frecuente. - Crear una nueva categoría: Asignar
NaNa una categoría como 'Desconocido' o 'Faltante'.
# Revisar valores faltantes en 'Embarked'
print("\nValores faltantes en 'Embarked':")
print(df['Embarked'].isnull().sum())
# Imputar 'Embarked' con la moda
mode_embarked = df['Embarked'].mode()[0]
df['Embarked_Imputed'] = df['Embarked'].fillna(mode_embarked)
print(f"Moda de 'Embarked': {mode_embarked}")
print(f"Valores faltantes en 'Embarked_Imputed': {df['Embarked_Imputed'].isnull().sum()}")
# Crear una nueva categoría para 'Cabin' (muchos NaN, por simplicidad para el ejemplo)
df['Cabin_Category'] = df['Cabin'].apply(lambda x: 'Missing' if pd.isna(x) else x[0]) # Solo la primera letra
print("\nDistribución de 'Cabin_Category':")
print(df['Cabin_Category'].value_counts())
Estandarización y Normalización de Categorías
A menudo, una misma categoría puede aparecer con diferentes grafías (ej. 'male', 'Male', 'M'). Es vital unificarlas.
# Ejemplo de estandarización (creando un problema artificial)
df['Sex_Typos'] = df['Sex'].replace({'male': 'Male', 'MALE': 'Male', 'Female': 'Female'})
# Si el dataset original ya está limpio, esto no hará mucho, pero es un ejemplo.
# Supongamos que había errores, los corregimos.
print("\nDistribución de 'Sex_Typos' (después de corrección ficticia):")
print(df['Sex_Typos'].value_counts())
# Convertir a tipo 'category' de Pandas (eficiente en memoria)
df['Sex'] = df['Sex'].astype('category')
df['Pclass'] = df['Pclass'].astype('category') # Aunque es numérica, la tratamos como ordinal
df['Embarked_Imputed'] = df['Embarked_Imputed'].astype('category')
print("\nTipos de datos después de conversión a 'category':")
print(df[['Sex', 'Pclass', 'Embarked_Imputed']].dtypes)
4. Codificación de Variables Categóricas para Modelos de ML 🧑💻
Los algoritmos de Machine Learning requieren entradas numéricas. Por lo tanto, necesitamos convertir nuestras categorías en números. Hay varias estrategias, cada una con sus ventajas y desventajas.
4.1. Codificación One-Hot (One-Hot Encoding) 🚦
Este es uno de los métodos más comunes, especialmente para variables nominales. Crea nuevas columnas binarias (0 o 1) para cada categoría única. Una fila tendrá un 1 en la columna correspondiente a su categoría y 0 en las demás.
# Usando pd.get_dummies()
df_encoded = pd.get_dummies(df, columns=['Sex', 'Embarked_Imputed'], prefix=['Sex', 'Embarked'], drop_first=True)
# drop_first=True evita la trampa de la variable ficticia (multicolinealidad)
print("\nDataFrame después de One-Hot Encoding de 'Sex' y 'Embarked_Imputed':")
print(df_encoded[['Sex_male', 'Embarked_Q', 'Embarked_S']].head())
print(df_encoded.head())
print(df_encoded.shape) # Observa el aumento de columnas
Ventajas y Desventajas del One-Hot Encoding
- Ventajas:
- No asume ningún orden entre categorías.
- Funciona bien con la mayoría de los algoritmos de ML.
- Fácil de entender.
- Desventajas:
- Aumenta la dimensionalidad del dataset (más columnas).
- Puede causar la 'maldición de la dimensionalidad' para variables con muchas categorías (alta cardinalidad).
- Puede llevar a la colinealidad si no se usa
drop_first=Trueo se gestiona adecuadamente.
4.2. Codificación Ordinal (Ordinal Encoding) 🔢
Ideal para variables categóricas ordinales. Asigna un número entero a cada categoría basándose en su orden. ¡Es crucial que el orden sea significativo!
from sklearn.preprocessing import OrdinalEncoder
# Crear un DataFrame de ejemplo para Ordinal Encoding
data_ordinal = {'Talla': ['S', 'M', 'L', 'M', 'XL', 'S'],
'Nivel_Educacion': ['Primaria', 'Universidad', 'Secundaria', 'Primaria', 'Universidad', 'Secundaria']}
df_ordinal = pd.DataFrame(data_ordinal)
# Definir el orden para 'Talla'
talla_order = ['S', 'M', 'L', 'XL']
# Definir el orden para 'Nivel_Educacion'
educacion_order = ['Primaria', 'Secundaria', 'Universidad']
# Inicializar OrdinalEncoder con el orden especificado
encoder_talla = OrdinalEncoder(categories=[talla_order])
df_ordinal['Talla_Encoded'] = encoder_talla.fit_transform(df_ordinal[['Talla']])
encoder_educacion = OrdinalEncoder(categories=[educacion_order])
df_ordinal['Nivel_Educacion_Encoded'] = encoder_educacion.fit_transform(df_ordinal[['Nivel_Educacion']])
print("\nDataFrame después de Ordinal Encoding:")
print(df_ordinal)
Ventajas y Desventajas del Ordinal Encoding
- Ventajas:
- Mantiene la dimensionalidad baja.
- Preserva el orden entre las categorías.
- Desventajas:
- Asume un orden, lo cual es incorrecto para variables nominales y puede inducir a errores en el modelo.
- La 'distancia' numérica entre categorías (ej. 1, 2, 3) no siempre refleja la verdadera distancia conceptual.
4.3. Codificación Target (Target Encoding / Mean Encoding) 🎯
Útil para variables categóricas con alta cardinalidad. Reemplaza cada categoría con la media de la variable objetivo (target) para esa categoría. Por ejemplo, en un problema de clasificación de ventas, una categoría 'País' podría ser reemplazada por el 'promedio de ventas' de ese país.
# Target Encoding para 'Cabin_Category' (con la variable 'Survived' como target)
# Necesitamos un DataFrame con el target para este ejemplo
# Usamos el df original con 'Survived' y nuestra 'Cabin_Category'
# Calcular la media de 'Survived' para cada 'Cabin_Category'
target_mean_encoding = df.groupby('Cabin_Category')['Survived'].mean()
df['Cabin_Target_Encoded'] = df['Cabin_Category'].map(target_mean_encoding)
print("\nTarget Encoding para 'Cabin_Category':")
print(df[['Cabin_Category', 'Survived', 'Cabin_Target_Encoded']].head())
print(df['Cabin_Target_Encoded'].unique())
Otras Técnicas de Codificación (Breve Mención)
- Frequency Encoding: Reemplaza cada categoría con la frecuencia o el recuento de su aparición en el dataset.
- Binary Encoding: Combina el hashing con el One-Hot Encoding. Convierte las categorías a enteros, luego esos enteros a código binario, y luego cada bit del código binario se convierte en una columna.
- Hashing Encoding: Convierte las categorías a un número de bits de longitud fija. Puede haber colisiones (diferentes categorías mapeadas al mismo hash).
5. Análisis Avanzado y Visualización de Categóricos 📈
Una vez codificados o preparados, podemos profundizar en el análisis y la visualización para extraer insights.
Análisis Bivariado con Variables Categóricas
Comparar una variable categórica con una numérica (ej. Pclass vs Fare) o con otra categórica (ya visto con crosstab).
# Relación entre 'Pclass' y 'Fare'
plt.figure(figsize=(10, 6))
sns.boxplot(data=df, x='Pclass', y='Fare')
plt.title('Distribución de Tarifas por Clase de Pasaje')
plt.xlabel('Clase de Pasaje')
plt.ylabel('Tarifa')
plt.show()
# Relación entre 'Embarked' y 'Age' (si 'Age' no tiene muchos NaNs)
# Primero, vamos a imputar 'Age' para este ejemplo
df['Age_Imputed'] = df['Age'].fillna(df['Age'].median())
plt.figure(figsize=(10, 6))
sns.violinplot(data=df, x='Embarked_Imputed', y='Age_Imputed', hue='Survived', split=True)
plt.title('Distribución de Edad por Puerto de Embarque y Supervivencia')
plt.xlabel('Puerto de Embarque')
plt.ylabel('Edad')
plt.show()
Mapas de Calor (Heatmaps) para Correlación Categórica
Aunque la correlación de Pearson es para variables numéricas, podemos usar chi-cuadrado para la asociación entre categóricas y visualizarlas con heatmaps.
from scipy.stats import chi2_contingency
# Función para calcular la tabla de contingencia y el p-valor de chi-cuadrado
def chi2_test(col1, col2, dataframe):
contingency_table = pd.crosstab(dataframe[col1], dataframe[col2])
chi2, p_value, _, _ = chi2_contingency(contingency_table)
return p_value
# Seleccionar algunas columnas categóricas codificadas (o el dtype 'category')
# Para este ejemplo, usaremos las columnas originales para ilustrar la asociación.
# Si quisieras correlación entre las columnas binarias de One-Hot, usarías las codificadas.
# Usaremos las columnas 'Sex', 'Pclass', 'Embarked_Imputed', 'Survived'
categorical_cols_for_analysis = ['Sex', 'Pclass', 'Embarked_Imputed', 'Survived']
# Convertir 'Survived' a categoría para el análisis si no lo está
df['Survived'] = df['Survived'].astype('category')
# Crear una matriz para almacenar los p-valores
p_value_matrix = pd.DataFrame(index=categorical_cols_for_analysis, columns=categorical_cols_for_analysis)
for col1 in categorical_cols_for_analysis:
for col2 in categorical_cols_for_analysis:
if col1 == col2:
p_value_matrix.loc[col1, col2] = 0 # No hay asociación consigo misma
else:
# Asegurarse de que no haya NaNs para chi2_contingency
temp_df = df[[col1, col2]].dropna()
if not temp_df.empty:
p_value = chi2_test(col1, col2, temp_df)
p_value_matrix.loc[col1, col2] = p_value
else:
p_value_matrix.loc[col1, col2] = np.nan
# Convertir la matriz a tipo numérico para el heatmap
p_value_matrix = p_value_matrix.astype(float)
plt.figure(figsize=(8, 7))
sns.heatmap(p_value_matrix, annot=True, cmap='viridis_r', fmt=".2f", linewidths=.5)
plt.title('P-valores de Prueba Chi-cuadrado entre Variables Categóricas')
plt.show()
6. Consideraciones Avanzadas y Mejores Prácticas 🚀
Alta Cardinalidad
Las variables con alta cardinalidad (muchos valores únicos, como IDs o nombres) son un desafío. El One-Hot Encoding puede crear un número excesivo de columnas, llevando a problemas de rendimiento y overfitting. Estrategias como el Target Encoding, Frequency Encoding, Binary Encoding o incluso el binning (agrupamiento) si hay un significado subyacente, pueden ser más apropiadas.
Combinación de Variables Categóricas
A veces, combinar dos o más variables categóricas puede crear una nueva característica más potente. Por ejemplo, (Sexo, Edad_Grupo) podría ser más informativo que Sexo y Edad_Grupo por separado.
# Creación de una nueva característica combinando 'Pclass' y 'Sex'
df['Pclass_Sex'] = df['Pclass'].astype(str) + '_' + df['Sex'].astype(str)
print("\nDistribución de la nueva característica 'Pclass_Sex':")
print(df['Pclass_Sex'].value_counts())
Binning de Variables Categóricas (Agrupamiento)
Cuando una variable categórica tiene demasiadas categorías pero algunas son muy raras, podemos agrupar las categorías menos frecuentes en una sola categoría 'Otros'.
# Ejemplo de Binning para 'Cabin_Category' (si tuviera muchas categorías raras)
# Identificar categorías con poca frecuencia
value_counts_cabin = df['Cabin_Category'].value_counts()
rare_categories = value_counts_cabin[value_counts_cabin < 10].index # Ej. menos de 10 apariciones
df['Cabin_Category_Binned'] = df['Cabin_Category'].replace(rare_categories, 'Otros')
print("\nDistribución de 'Cabin_Category_Binned' después del agrupamiento:")
print(df['Cabin_Category_Binned'].value_counts())
Usando el Dtype category de Pandas
Pandas ofrece un tipo de dato category optimizado para este tipo de variables. No solo ahorra memoria, sino que también permite especificar un orden para categorías ordinales.
# Ejemplo de definición de orden para una columna 'category'
df['Pclass_Ordered'] = df['Pclass'].astype(pd.CategoricalDtype(categories=[3, 2, 1], ordered=True))
print("\nTipo y categorías de 'Pclass_Ordered':")
print(df['Pclass_Ordered'].dtype)
print(df['Pclass_Ordered'].head())
# Esto permite operaciones como comparación:
print(f"\nNúmero de pasajeros en Pclass 1 o 2: {(df['Pclass_Ordered'] <= 2).sum()}")
Conclusión: Dominando el Mundo Categórico con Pandas ✅
¡Felicidades! Has completado un viaje exhaustivo por el fascinante mundo del análisis de datos categóricos con Pandas. Desde la identificación y la limpieza hasta la codificación avanzada y la visualización, ahora tienes las herramientas para transformar variables cualitativas en insights accionables y datos listos para tus modelos de Machine Learning.
Recuerda que la elección de la técnica de codificación y preprocesamiento depende en gran medida de la naturaleza de tus datos y los requisitos de tu problema. Experimenta, explora y visualiza siempre tus transformaciones para asegurarte de que estás extrayendo el máximo valor de tus datos.
¡Ahora estás listo para abordar cualquier conjunto de datos con confianza, sabiendo que no dejarás ninguna gema categórica sin pulir!
Tutoriales relacionados
- Uniendo el Universo de Datos: Guía Completa de `merge()`, `join()` y `concat()` en Pandas ✨intermediate15 min
- Aprende a Crear y Gestionar Tablas Dinámicas con Pandas: Un Enfoque Práctico para el Análisis de Datos 📊intermediate15 min
- Optimización del Rendimiento de Operaciones Numéricas con NumPy: ¡Velocidad y Eficiencia! 🚀intermediate18 min
- Análisis de Datos Incompletos con Pandas y NumPy: Estrategias para Datos Faltantes 🕵️♀️intermediate20 min
- Optimización de Memoria y Rendimiento con Pandas: Estrategias Avanzadasintermediate20 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!