Guía Completa de Pandas para Análisis de Datos#
1. Importación de la librería#
Para comenzar a utilizar pandas, es necesario importarla. La convención estándar en la comunidad de Python es importarla bajo el alias pd. También importamos numpy, ya que frecuentemente se usa en conjunto para el manejo de valores numéricos nulos o cálculos avanzados.
import pandas as pd
import numpy as np
2. Estructuras de Datos Principales#
Pandas opera principalmente con dos estructuras de datos: Series (unidimensional) y DataFrame (bidimensional).
2.1 Creación de Series#
Una Serie es un arreglo de una sola dimensión con etiquetas (índices), capaz de contener cualquier tipo de dato.
# Creación de una Serie a partir de una lista, definiendo índices personalizados
datos_serie = [10, 20, 30, 40, 50]
etiquetas = ['a', 'b', 'c', 'd', 'e']
serie = pd.Series(data=datos_serie, index=etiquetas)
print("Serie creada:\n", serie)
Serie creada:
a 10
b 20
c 30
d 40
e 50
dtype: int64
2.2 Creación de DataFrames#
Un DataFrame es una estructura bidimensional, tabular y con etiquetas en filas y columnas. Es el equivalente a una tabla en SQL o una hoja de cálculo.
# Creación de un DataFrame a partir de un diccionario de listas
datos_diccionario = {
'Nombre': ['Ana', 'Juan', 'Pedro', 'Laura'],
'Edad': [28, 34, 29, 42],
'Ciudad': ['Bogotá', 'Lima', 'Santiago', 'Madrid']
}
df = pd.DataFrame(datos_diccionario)
# display() renderiza el DataFrame con un formato HTML enriquecido en Jupyter
display(df)
| Nombre | Edad | Ciudad | |
|---|---|---|---|
| 0 | Ana | 28 | Bogotá |
| 1 | Juan | 34 | Lima |
| 2 | Pedro | 29 | Santiago |
| 3 | Laura | 42 | Madrid |
3. Exploración Inicial de los Datos#
Una vez instanciado un DataFrame, es fundamental inspeccionar su estructura, el tipo de datos que contiene y sus estadísticos básicos.
# Mostrar las primeras filas (por defecto 5, aquí especificamos 2)
print("--- Primeras 2 filas ---")
display(df.head(2))
# Mostrar información general del DataFrame (tipos de datos, recuento de no nulos y uso de memoria)
print("\n--- Información del DataFrame ---")
df.info()
# Estadísticas descriptivas de las columnas numéricas (conteo, media, desviación estándar, min, max, cuartiles)
print("\n--- Estadísticas descriptivas ---")
display(df.describe())
--- Primeras 2 filas ---
| Nombre | Edad | Ciudad | |
|---|---|---|---|
| 0 | Ana | 28 | Bogotá |
| 1 | Juan | 34 | Lima |
--- Información del DataFrame ---
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4 entries, 0 to 3
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Nombre 4 non-null object
1 Edad 4 non-null int64
2 Ciudad 4 non-null object
dtypes: int64(1), object(2)
memory usage: 228.0+ bytes
--- Estadísticas descriptivas ---
| Edad | |
|---|---|
| count | 4.000000 |
| mean | 33.250000 |
| std | 6.396614 |
| min | 28.000000 |
| 25% | 28.750000 |
| 50% | 31.500000 |
| 75% | 36.000000 |
| max | 42.000000 |
4. Selección e Indexación#
Existen múltiples formas de seleccionar subconjuntos de datos. Los métodos más robustos y recomendados son .loc[] (basado en etiquetas) e .iloc[] (basado en posiciones enteras indexadas desde 0).
# 1. Selección de una sola columna (retorna una Serie)
edades = df['Edad']
# 2. Selección por posición usando iloc (Fila 1, Columna 0 -> 'Juan')
primer_nombre = df.iloc[1, 0]
print(f"Valor en iloc[1, 0]: {primer_nombre}")
# 3. Selección por etiqueta usando loc (todas las filas, y específicamente las columnas 'Nombre' y 'Edad')
sub_df = df.loc[:, ['Nombre', 'Edad']]
# 4. Filtrado condicional (Aplicación de máscaras booleanas)
# Se crea una condición que retorna True/False y se pasa al DataFrame
mayores_de_30 = df[df['Edad'] > 30]
print("\n--- Usuarios mayores de 30 años ---")
display(mayores_de_30)
Valor en iloc[1, 0]: Juan
--- Usuarios mayores de 30 años ---
| Nombre | Edad | Ciudad | |
|---|---|---|---|
| 1 | Juan | 34 | Lima |
| 3 | Laura | 42 | Madrid |
5. Limpieza y Transformación de Datos#
El preprocesamiento de datos requiere tratar valores nulos y modificar la estructura para extraer información valiosa mediante agrupaciones.
# Insertamos un valor nulo (NaN) artificialmente para el ejemplo
df.loc[1, 'Edad'] = np.nan
# Detectar la cantidad de valores nulos por columna
print("--- Valores nulos por columna antes de imputar ---")
print(df.isna().sum())
# Rellenar valores nulos (imputación con el promedio de la columna)
promedio_edad = df['Edad'].mean()
df['Edad'] = df['Edad'].fillna(promedio_edad)
# === Agrupación de datos (GroupBy) ===
# Crearemos un nuevo DataFrame para ilustrar agregaciones
df_ventas = pd.DataFrame({
'Departamento': ['Ventas', 'Ventas', 'Marketing', 'Marketing', 'Ventas'],
'Empleado': ['Carlos', 'Diana', 'Elena', 'Fernando', 'Hugo'],
'Ventas': [200, 150, 300, 250, 400]
})
# Agrupar por departamento y sumar las ventas. reset_index() convierte el índice agrupado de nuevo en una columna.
ventas_por_depto = df_ventas.groupby('Departamento')['Ventas'].sum().reset_index()
print("\n--- Ventas totales por Departamento ---")
display(ventas_por_depto)
--- Valores nulos por columna antes de imputar ---
Nombre 0
Edad 1
Ciudad 0
dtype: int64
--- Ventas totales por Departamento ---
| Departamento | Ventas | |
|---|---|---|
| 0 | Marketing | 550 |
| 1 | Ventas | 750 |
6. Fusión de DataFrames (Merge / Concat)#
Es habitual cruzar información de diferentes tablas utilizando claves comunes, de manera análoga a los JOINs en bases de datos relacionales.
# DataFrames de ejemplo
df_empleados = pd.DataFrame({
'ID_Empleado': [1, 2, 3],
'Nombre': ['Ana', 'Juan', 'Pedro']
})
df_salarios = pd.DataFrame({
'ID_Empleado': [1, 2, 4], # Nota: El ID 4 no está en df_empleados, el ID 3 no está en df_salarios
'Salario': [5000, 6000, 5500]
})
# Fusión tipo 'inner' (mantiene solo las coincidencias exactas en 'ID_Empleado')
df_inner = pd.merge(df_empleados, df_salarios, on='ID_Empleado', how='inner')
print("--- Inner Join (Solo coincidencias) ---")
display(df_inner)
# Fusión tipo 'left' (mantiene todos los registros del DataFrame izquierdo, rellenando con NaN si no hay coincidencia)
df_left = pd.merge(df_empleados, df_salarios, on='ID_Empleado', how='left')
print("\n--- Left Join (Mantiene todos los empleados) ---")
display(df_left)
--- Inner Join (Solo coincidencias) ---
| ID_Empleado | Nombre | Salario | |
|---|---|---|---|
| 0 | 1 | Ana | 5000 |
| 1 | 2 | Juan | 6000 |
--- Left Join (Mantiene todos los empleados) ---
| ID_Empleado | Nombre | Salario | |
|---|---|---|---|
| 0 | 1 | Ana | 5000.0 |
| 1 | 2 | Juan | 6000.0 |
| 2 | 3 | Pedro | NaN |