📚 Pandas - Análisis de Datos
Pandas es la librería más popular para análisis y manipulación de datos en Python. Proporciona estructuras de datos flexibles (Series y DataFrame) y herramientas para trabajar con datos tabulares.
1. Instalación e Importación
# Instalación
# pip install pandas
# Importación (convención estándar)
import pandas as pd
import numpy as np
2. Estructuras de Datos
Series
Una Series es un array unidimensional etiquetado.
# Crear Series desde lista
s = pd.Series([10, 20, 30, 40, 50])
print(s)
# 0 10
# 1 20
# 2 30
# 3 40
# 4 50
# dtype: int64
# Con índices personalizados
s = pd.Series([10, 20, 30], index=["a", "b", "c"])
print(s)
# a 10
# b 20
# c 30
# Desde diccionario
datos = {"manzanas": 10, "naranjas": 20, "plátanos": 15}
s = pd.Series(datos)
print(s)
# Acceso a elementos
print(s["manzanas"]) # 10
print(s[0]) # 10 (por posición)
print(s.values) # array NumPy
print(s.index) # Index(['manzanas', 'naranjas', 'plátanos'])
DataFrame
Un DataFrame es una tabla 2D con filas y columnas etiquetadas.
# Desde diccionario de listas
datos = {
"nombre": ["Ana", "Luis", "María", "Pedro"],
"edad": [25, 30, 28, 35],
"ciudad": ["Madrid", "Barcelona", "Valencia", "Sevilla"]
}
df = pd.DataFrame(datos)
print(df)
# nombre edad ciudad
# 0 Ana 25 Madrid
# 1 Luis 30 Barcelona
# 2 María 28 Valencia
# 3 Pedro 35 Sevilla
# Con índice personalizado
df = pd.DataFrame(datos, index=["a", "b", "c", "d"])
# Desde lista de diccionarios
registros = [
{"nombre": "Ana", "edad": 25},
{"nombre": "Luis", "edad": 30}
]
df = pd.DataFrame(registros)
# Desde array NumPy
arr = np.random.randint(0, 100, (4, 3))
df = pd.DataFrame(arr, columns=["A", "B", "C"])
3. Leer y Escribir Datos
CSV
# Leer CSV
df = pd.read_csv("datos.csv")
# Con opciones
df = pd.read_csv("datos.csv",
sep=";", # Separador
header=0, # Fila de encabezados
index_col=0, # Columna como índice
usecols=["a", "b"], # Solo estas columnas
nrows=100, # Primeras N filas
encoding="utf-8", # Codificación
na_values=["N/A"]) # Valores nulos
# Escribir CSV
df.to_csv("salida.csv", index=False)
df.to_csv("salida.csv", sep=";", encoding="utf-8")
Excel
# Leer Excel (requiere openpyxl o xlrd)
df = pd.read_excel("datos.xlsx", sheet_name="Hoja1")
# Leer múltiples hojas
hojas = pd.read_excel("datos.xlsx", sheet_name=None) # Diccionario
# Escribir Excel
df.to_excel("salida.xlsx", index=False, sheet_name="Datos")
# Múltiples hojas
with pd.ExcelWriter("salida.xlsx") as writer:
df1.to_excel(writer, sheet_name="Hoja1")
df2.to_excel(writer, sheet_name="Hoja2")
Otros Formatos
# JSON
df = pd.read_json("datos.json")
df.to_json("salida.json", orient="records")
# SQL
import sqlite3
conn = sqlite3.connect("base_datos.db")
df = pd.read_sql("SELECT * FROM tabla", conn)
df.to_sql("tabla_nueva", conn, if_exists="replace")
# HTML
tablas = pd.read_html("https://ejemplo.com/tabla") # Lista de DataFrames
df.to_html("tabla.html")
# Pickle (binario Python)
df.to_pickle("datos.pkl")
df = pd.read_pickle("datos.pkl")
# Parquet (eficiente para big data)
df.to_parquet("datos.parquet")
df = pd.read_parquet("datos.parquet")
4. Explorar Datos
# Crear DataFrame de ejemplo
df = pd.DataFrame({
"nombre": ["Ana", "Luis", "María", "Pedro", "Carmen"],
"edad": [25, 30, 28, 35, 22],
"salario": [35000, 45000, 40000, 55000, 32000],
"departamento": ["IT", "Ventas", "IT", "RRHH", "Ventas"]
})
# Primeras/últimas filas
print(df.head()) # Primeras 5
print(df.head(3)) # Primeras 3
print(df.tail()) # Últimas 5
# Información general
print(df.info()) # Tipos, nulos, memoria
print(df.shape) # (5, 4)
print(df.columns) # Nombres de columnas
print(df.index) # Índice
print(df.dtypes) # Tipos por columna
# Estadísticas descriptivas
print(df.describe()) # Numéricas
print(df.describe(include="all")) # Todas
print(df.describe(include=["object"])) # Solo texto
# Valores únicos
print(df["departamento"].unique()) # Array de valores únicos
print(df["departamento"].nunique()) # Cantidad de únicos
print(df["departamento"].value_counts()) # Frecuencia
5. Selección de Datos
Seleccionar Columnas
# Una columna (Series)
print(df["nombre"])
print(df.nombre) # Equivalente si no tiene espacios
# Varias columnas (DataFrame)
print(df[["nombre", "edad"]])
Seleccionar Filas
# Por índice posicional (iloc)
print(df.iloc[0]) # Primera fila (Series)
print(df.iloc[0:3]) # Primeras 3 filas
print(df.iloc[[0, 2, 4]]) # Filas específicas
# Por etiqueta (loc)
print(df.loc[0]) # Fila con índice 0
print(df.loc[0:2]) # Filas 0 a 2 (incluido)
# Filas y columnas
print(df.iloc[0:3, 0:2]) # Primeras 3 filas, primeras 2 columnas
print(df.loc[0:2, ["nombre", "edad"]]) # Por etiquetas
Filtrar con Condiciones
# Condición simple
mayores_30 = df[df["edad"] > 30]
print(mayores_30)
# Condiciones múltiples
filtro = df[(df["edad"] > 25) & (df["salario"] > 35000)]
print(filtro)
# OR
filtro = df[(df["departamento"] == "IT") | (df["departamento"] == "Ventas")]
# isin() para múltiples valores
filtro = df[df["departamento"].isin(["IT", "RRHH"])]
# Texto con str
filtro = df[df["nombre"].str.startswith("M")]
filtro = df[df["nombre"].str.contains("ar")]
# query() - sintaxis más legible
filtro = df.query("edad > 25 and salario > 35000")
filtro = df.query("departamento == 'IT'")
6. Modificar Datos
Añadir/Modificar Columnas
# Nueva columna
df["bonus"] = df["salario"] * 0.1
df["email"] = df["nombre"].str.lower() + "@empresa.com"
# Columna condicional
df["senior"] = df["edad"] > 30
df["categoria"] = np.where(df["salario"] > 40000, "Alto", "Normal")
# apply() para funciones personalizadas
def categorizar_edad(edad):
if edad < 25:
return "Joven"
elif edad < 35:
return "Adulto"
return "Senior"
df["grupo_edad"] = df["edad"].apply(categorizar_edad)
# map() para reemplazar valores
mapa_dept = {"IT": "Tecnología", "RRHH": "Recursos Humanos", "Ventas": "Comercial"}
df["dept_largo"] = df["departamento"].map(mapa_dept)
Modificar Valores
# Modificar valor específico
df.loc[0, "salario"] = 36000
df.iloc[0, 2] = 36000
# Modificar con condición
df.loc[df["departamento"] == "IT", "salario"] *= 1.1 # Aumento 10%
# replace()
df["departamento"] = df["departamento"].replace("IT", "Tech")
df = df.replace({"IT": "Tech", "RRHH": "HR"})
Renombrar
# Renombrar columnas
df = df.rename(columns={"nombre": "empleado", "edad": "años"})
# Renombrar todas
df.columns = ["col1", "col2", "col3", "col4"]
# Renombrar índice
df = df.rename(index={0: "primero", 1: "segundo"})
Eliminar
# Eliminar columnas
df = df.drop(columns=["bonus", "email"])
df = df.drop("bonus", axis=1)
# Eliminar filas
df = df.drop([0, 1]) # Por índice
df = df.drop(df[df["edad"] < 25].index) # Por condición
7. Valores Nulos
# Crear DataFrame con nulos
df = pd.DataFrame({
"A": [1, 2, None, 4],
"B": [None, 2, 3, 4],
"C": [1, None, None, 4]
})
# Detectar nulos
print(df.isnull()) # DataFrame booleano
print(df.isna()) # Igual
print(df.isnull().sum()) # Cantidad por columna
print(df.isnull().sum().sum()) # Total
# Eliminar nulos
df_limpio = df.dropna() # Filas con algún nulo
df_limpio = df.dropna(how="all") # Filas completamente nulas
df_limpio = df.dropna(subset=["A", "B"]) # Solo si nulo en A o B
# Rellenar nulos
df_relleno = df.fillna(0) # Con valor
df_relleno = df.fillna(df.mean()) # Con media
df_relleno = df.fillna(method="ffill") # Forward fill
df_relleno = df.fillna(method="bfill") # Backward fill
# Interpolación
df_interp = df.interpolate()
8. Ordenar Datos
df = pd.DataFrame({
"nombre": ["Ana", "Luis", "María", "Pedro"],
"edad": [25, 30, 28, 35],
"salario": [35000, 45000, 40000, 55000]
})
# Ordenar por columna
df_ordenado = df.sort_values("edad")
df_ordenado = df.sort_values("edad", ascending=False)
# Por múltiples columnas
df_ordenado = df.sort_values(["edad", "salario"], ascending=[True, False])
# Por índice
df_ordenado = df.sort_index()
# nlargest / nsmallest
top3 = df.nlargest(3, "salario")
bottom3 = df.nsmallest(3, "edad")
# Ranking
df["rank_salario"] = df["salario"].rank(ascending=False)
9. Agrupar Datos (GroupBy)
df = pd.DataFrame({
"departamento": ["IT", "Ventas", "IT", "Ventas", "RRHH"],
"empleado": ["Ana", "Luis", "María", "Pedro", "Carmen"],
"salario": [35000, 45000, 40000, 55000, 38000],
"años_empresa": [2, 5, 3, 8, 4]
})
# Agrupar por una columna
grupo = df.groupby("departamento")
# Agregaciones
print(grupo["salario"].mean()) # Media por departamento
print(grupo["salario"].sum()) # Suma
print(grupo.size()) # Tamaño de cada grupo
# Múltiples agregaciones
print(grupo["salario"].agg(["mean", "sum", "count", "min", "max"]))
# Agregaciones diferentes por columna
resultado = grupo.agg({
"salario": ["mean", "sum"],
"años_empresa": "mean"
})
# agg() con funciones personalizadas
def rango(x):
return x.max() - x.min()
print(grupo["salario"].agg(rango))
# Agrupar por múltiples columnas
df["senior"] = df["años_empresa"] > 3
grupo2 = df.groupby(["departamento", "senior"])
print(grupo2["salario"].mean())
# transform() - mantiene forma original
df["salario_medio_dept"] = grupo["salario"].transform("mean")
df["salario_vs_media"] = df["salario"] - df["salario_medio_dept"]
10. Combinar DataFrames
Concatenar
df1 = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
df2 = pd.DataFrame({"A": [5, 6], "B": [7, 8]})
# Vertical (filas)
concat_v = pd.concat([df1, df2])
concat_v = pd.concat([df1, df2], ignore_index=True)
# Horizontal (columnas)
concat_h = pd.concat([df1, df2], axis=1)
Merge (JOIN)
empleados = pd.DataFrame({
"id": [1, 2, 3, 4],
"nombre": ["Ana", "Luis", "María", "Pedro"],
"dept_id": [10, 20, 10, 30]
})
departamentos = pd.DataFrame({
"dept_id": [10, 20, 30],
"departamento": ["IT", "Ventas", "RRHH"]
})
# Inner join (por defecto)
resultado = pd.merge(empleados, departamentos, on="dept_id")
# Left join
resultado = pd.merge(empleados, departamentos, on="dept_id", how="left")
# Right join
resultado = pd.merge(empleados, departamentos, on="dept_id", how="right")
# Outer join
resultado = pd.merge(empleados, departamentos, on="dept_id", how="outer")
# Columnas con nombres diferentes
df1 = pd.DataFrame({"id_emp": [1, 2], "nombre": ["A", "B"]})
df2 = pd.DataFrame({"emp_id": [1, 2], "salario": [1000, 2000]})
resultado = pd.merge(df1, df2, left_on="id_emp", right_on="emp_id")
Join
df1 = pd.DataFrame({"A": [1, 2]}, index=["a", "b"])
df2 = pd.DataFrame({"B": [3, 4]}, index=["a", "b"])
# Join por índice
resultado = df1.join(df2)
11. Pivot Tables y Reshape
Pivot Table
df = pd.DataFrame({
"fecha": ["2024-01", "2024-01", "2024-02", "2024-02"],
"producto": ["A", "B", "A", "B"],
"ventas": [100, 150, 120, 180],
"cantidad": [10, 15, 12, 18]
})
# Pivot table básico
pivot = df.pivot_table(
values="ventas",
index="fecha",
columns="producto",
aggfunc="sum"
)
# Con múltiples agregaciones
pivot = df.pivot_table(
values="ventas",
index="fecha",
columns="producto",
aggfunc=["sum", "mean"]
)
# Totales
pivot = df.pivot_table(
values="ventas",
index="fecha",
columns="producto",
aggfunc="sum",
margins=True,
margins_name="Total"
)
Melt (de ancho a largo)
df_ancho = pd.DataFrame({
"id": [1, 2],
"nombre": ["Ana", "Luis"],
"enero": [100, 150],
"febrero": [110, 160]
})
df_largo = pd.melt(
df_ancho,
id_vars=["id", "nombre"],
value_vars=["enero", "febrero"],
var_name="mes",
value_name="ventas"
)
print(df_largo)
# id nombre mes ventas
# 0 1 Ana enero 100
# 1 2 Luis enero 150
# 2 1 Ana febrero 110
# 3 2 Luis febrero 160
Stack/Unstack
# Stack: de columnas a filas (MultiIndex)
df = pd.DataFrame({"A": [1, 2], "B": [3, 4]}, index=["x", "y"])
stacked = df.stack()
# Unstack: de filas a columnas
unstacked = stacked.unstack()
12. Trabajar con Fechas
# Crear columna datetime
df = pd.DataFrame({
"fecha_str": ["2024-01-15", "2024-02-20", "2024-03-25"]
})
df["fecha"] = pd.to_datetime(df["fecha_str"])
# Crear rango de fechas
fechas = pd.date_range("2024-01-01", periods=10, freq="D")
fechas = pd.date_range("2024-01-01", "2024-12-31", freq="M")
# Extraer componentes
df["año"] = df["fecha"].dt.year
df["mes"] = df["fecha"].dt.month
df["dia"] = df["fecha"].dt.day
df["dia_semana"] = df["fecha"].dt.dayofweek # 0=Lunes
df["nombre_dia"] = df["fecha"].dt.day_name()
df["trimestre"] = df["fecha"].dt.quarter
# Filtrar por fechas
df = df[df["fecha"] > "2024-02-01"]
df = df[df["fecha"].between("2024-01-01", "2024-06-30")]
# Índice temporal
df = df.set_index("fecha")
df_2024 = df.loc["2024"]
df_enero = df.loc["2024-01"]
# Resample (agrupar por tiempo)
df_mensual = df.resample("M").sum()
df_semanal = df.resample("W").mean()
13. Operaciones con Strings
df = pd.DataFrame({
"nombre": [" Ana García ", "LUIS PÉREZ", "maría lópez"],
"email": ["ana@email.com", "luis@empresa.es", "maria@otro.net"]
})
# Acceso a métodos string con .str
df["nombre_limpio"] = df["nombre"].str.strip()
df["nombre_upper"] = df["nombre"].str.upper()
df["nombre_lower"] = df["nombre"].str.lower()
df["nombre_title"] = df["nombre"].str.title()
# Dividir
df["dominio"] = df["email"].str.split("@").str[1]
# Reemplazar
df["email_nuevo"] = df["email"].str.replace(".com", ".org")
# Contiene
df["es_empresa"] = df["email"].str.contains("empresa")
# Longitud
df["len_nombre"] = df["nombre"].str.len()
# Extraer con regex
df["usuario"] = df["email"].str.extract(r"(.+)@")
14. Ejemplo Completo: Análisis de Ventas
import pandas as pd
import numpy as np
# Crear datos de ejemplo
np.random.seed(42)
n = 1000
df = pd.DataFrame({
"fecha": pd.date_range("2024-01-01", periods=n, freq="H"),
"producto": np.random.choice(["Laptop", "Mouse", "Teclado", "Monitor"], n),
"cantidad": np.random.randint(1, 10, n),
"precio_unitario": np.random.choice([999, 29, 79, 299], n),
"region": np.random.choice(["Norte", "Sur", "Este", "Oeste"], n)
})
df["total"] = df["cantidad"] * df["precio_unitario"]
# Análisis
print("=== ANÁLISIS DE VENTAS ===\n")
# Resumen general
print("Resumen estadístico:")
print(df[["cantidad", "total"]].describe())
# Ventas por producto
print("\nVentas por producto:")
print(df.groupby("producto")["total"].agg(["sum", "mean", "count"]))
# Ventas por región
print("\nVentas por región:")
print(df.groupby("region")["total"].sum().sort_values(ascending=False))
# Ventas mensuales
df["mes"] = df["fecha"].dt.to_period("M")
print("\nVentas mensuales:")
print(df.groupby("mes")["total"].sum())
# Top productos por región
print("\nProducto más vendido por región:")
print(df.groupby(["region", "producto"])["total"].sum().unstack().idxmax(axis=1))
# Pivot: productos vs regiones
print("\nTabla resumen (productos x regiones):")
pivot = df.pivot_table(
values="total",
index="producto",
columns="region",
aggfunc="sum",
margins=True
)
print(pivot)
15. Resumen de Funciones
| Función | Descripción |
|---|---|
pd.read_csv() |
Leer CSV |
df.to_csv() |
Escribir CSV |
df.head(), df.tail() |
Ver filas |
df.info(), df.describe() |
Información |
df.loc[], df.iloc[] |
Seleccionar |
df.query() |
Filtrar |
df.groupby() |
Agrupar |
pd.merge() |
Combinar |
pd.concat() |
Concatenar |
df.pivot_table() |
Tabla pivote |
df.fillna(), df.dropna() |
Manejar nulos |
df.sort_values() |
Ordenar |
📅 Fecha de creación: Enero 2026
✍️ Autor: Fran García