Fundamentos de pandas: Series, DataFrames, el DatetimeIndex, selección y filtrado, y remuestreo de barras diarias a semanales
Comprende las Series y los DataFrames, indexa los datos de precios por fecha, selecciona filas con loc e iloc, filtra con máscaras booleanas y agrega barras diarias en barras semanales.
Qué aprenderás
- Qué son una
Seriesy unDataFramey por qué elDatetimeIndexes la columna vertebral del análisis de precios. - La diferencia entre
loc, basado en etiquetas, eiloc, basado en posiciones, y cómo filtran filas las máscaras booleanas. - Cómo construir barras semanales o mensuales a partir de barras diarias con
resampley la agregación correcta para cada columna. - Qué valores por defecto de pandas cambiaron en la versión 3.0 y cómo escribir código que se comporte igual en 2.2 y en 3.x.
Explicación
Una Series es un array unidimensional etiquetado: una columna de cierres con una fecha asociada a cada valor. Un DataFrame es una tabla de Series que comparten un mismo índice: apertura, máximo, mínimo, cierre y volumen uno al lado del otro. El índice contiene las etiquetas de las filas; para los datos de precios debería ser un DatetimeIndex, un índice especializado de marcas temporales que permite trocear por fechas, remuestrear y gestionar zonas horarias. Convención: import pandas as pd.
Crea un DataFrame con un diccionario de columnas y un índice de fechas:
idx = pd.bdate_range("2025-03-03", periods=10)
df = pd.DataFrame({"open": [...], "high": [...], "low": [...], "close": [...], "volume": [...]}, index=idx)
bdate_range genera solo días laborables y no sabe nada de los festivos bursátiles; los datos reales proceden de archivos (lección 4). Inspecciona con df.head(), df.info() (tipos y valores faltantes) y df.describe(). df.index.dtype debe mostrar datetime64; si muestra object, las fechas siguen siendo texto.
Selección. df["close"] devuelve una Series; df[["open", "close"]] (una lista) devuelve un DataFrame. df.loc[row_labels, columns] selecciona por etiqueta: df.loc["2025-03-05":"2025-03-07", "close"] da los cierres de esas tres fechas y, con loc, se incluyen ambos extremos del slice, a diferencia de las listas de Python. df.iloc[0:3] selecciona por posición entera (las tres primeras filas, excluido el final) y df.iloc[-1] es la última barra.
Filtrado. Una comparación sobre una Series produce una Series de booleanos, una máscara: up = df["close"] > df["open"]. Al pasar la máscara se seleccionan las filas que coinciden: df[up]. Combina máscaras con & (y), | (o) y ~ (no), cada condición entre sus propios paréntesis: df[(df["close"] > df["open"]) & (df["volume"] > 1200)]. up.sum() cuenta los valores True, y up.mean() es la proporción de días alcistas.
Añadir columnas es una asignación: df["range"] = df["high"] - df["low"]. Las operaciones están vectorizadas, es decir, se aplican a columnas enteras a la vez, y por eso pandas es rápido. shift(1) desplaza los valores una fila hacia abajo (el cierre de ayer en la fila de hoy) y pct_change() da las variaciones porcentuales; ambos dejan NaN (not a number, «no es un número») en la primera fila.
Remuestreo. resample agrupa las filas en intervalos de tiempo fijos. Cada columna necesita su propia regla: la apertura semanal es la primera apertura diaria, el máximo es el mayor valor, el mínimo el menor, el cierre el último y el volumen la suma:
rules = {"open": "first", "high": "max", "low": "min", "close": "last", "volume": "sum"}
weekly = df.resample("W-FRI").agg(rules)
El código "W-FRI" etiqueta cada semana con su viernes. Las barras mensuales usan "ME" (fin de mes). Los tutoriales antiguos escriben "M"; pandas 2.2 declaró obsoleto ese alias en favor de "ME" (igual que "QE" y "YE"), y el código escrito con los nuevos alias funciona tanto en 2.2 como en 3.x.
Cambios de pandas 3.0 que conviene conocer. Copy-on-Write (copia al escribir) es ahora el único modo: un DataFrame filtrado es una copia independiente, así que df[mask]["close"] = 0 nunca modifica df; escribe df.loc[mask, "close"] = 0. Las columnas de texto tienen ahora el dtype str en lugar de object.
Ejemplo práctico
Ejemplo hipotético: diez barras diarias del lunes 3 de marzo al viernes 14 de marzo de 2025, precios en euros, sin festivos.
| Fecha | Apertura | Máximo | Mínimo | Cierre | Volumen |
|---|---|---|---|---|---|
| 03 mar | 100 | 102 | 99 | 101 | 1,000 |
| 04 mar | 101 | 104 | 100 | 103 | 1,200 |
| 05 mar | 103 | 104 | 101 | 102 | 900 |
| 06 mar | 102 | 105 | 101 | 104 | 1,100 |
| 07 mar | 104 | 106 | 103 | 105 | 1,500 |
| 10 mar | 105 | 106 | 103 | 104 | 1,300 |
| 11 mar | 104 | 107 | 103 | 106 | 1,000 |
| 12 mar | 106 | 108 | 105 | 107 | 1,400 |
| 13 mar | 107 | 109 | 106 | 108 | 1,200 |
| 14 mar | 108 | 110 | 106 | 107 | 1,600 |
El remuestreo da dos filas. Semana que termina el 7 de marzo: apertura = primera apertura = 100; máximo = max(102, 104, 104, 105, 106) = 106; mínimo = min(99, 100, 101, 101, 103) = 99; cierre = último cierre = 105; volumen = 1,000 + 1,200 + 900 + 1,100 + 1,500 = 5,700. Semana que termina el 14 de marzo: apertura 105, máximo 110, mínimo 103, cierre 107, volumen = 1,300 + 1,000 + 1,400 + 1,200 + 1,600 = 6,500.
Filtrado: (df["close"] > df["open"]).sum() devuelve 7, porque solo el 5, el 10 y el 14 de marzo cerraron por debajo de su apertura. df.loc["2025-03-05":"2025-03-07", "close"] devuelve 102, 104, 105.
Error frecuente
df.iloc[0:5] devuelve cinco filas (posiciones 0 a 4), pero df.loc["2025-03-03":"2025-03-07"] devuelve todas las filas hasta el 7 de marzo inclusive. Usa iloc para «las N primeras» o «la última barra», y loc con fechas explícitas para los periodos.Actividad práctica
Usando la tabla de diez barras anterior y trabajando en papel: (a) escribe la expresión de pandas que selecciona las barras del 10 al 12 de marzo inclusive; (b) indica qué devuelve df.iloc[-3:]["close"].tolist(); (c) calcula la única barra mensual que produciría df.resample("ME").agg(rules); (d) explica qué sale mal si alguien escribe df.resample("ME").last() para todas las columnas.
Mostrar solución
(a) df.loc["2025-03-10":"2025-03-12"], tres filas, ambos extremos incluidos.
(b) Los tres últimos cierres: [107, 108, 107].
(c) Una fila etiquetada el 31 de marzo de 2025 (fin de mes): apertura 100 (primera), máximo 110 (mayor), mínimo 99 (menor), cierre 107 (último), volumen 12,200 (5,700 + 6,500).
(d) .last() toma el valor final de cada columna: el mínimo mensual sería 106 en lugar de 99, la apertura 108 en lugar de 100 y el volumen 1,600 en lugar de 12,200. Pasa siempre las reglas por columna.
Limitaciones: el ejemplo no tiene festivos ni días faltantes; con datos reales, una semana con un festivo simplemente agrega cuatro barras, y aparece la etiqueta de mes «31 de marzo» aunque el último día de negociación fuera el 28 de marzo.
Ideas clave
- Los datos de precios viven en un DataFrame con un DatetimeIndex; comprueba
df.index.dtypeantes que nada. locselecciona por etiqueta e incluye el final del slice;ilocselecciona por posición y lo excluye.- Las máscaras booleanas combinadas con
&,|y~sustituyen a los bucles para filtrar, y.sum()sobre una máscara cuenta las coincidencias. - Remuestrea OHLCV con first/max/min/last/sum, usa los alias
"W-FRI"y"ME", y recuerda el Copy-on-Write de pandas 3.0 al asignar.
Siguiente paso
La lección 4 introduce datos reales: leer archivos CSV y API gratuitas, entender los precios ajustados frente a los no ajustados, y gestionar los valores faltantes y las zonas horarias.
Autoevaluación
Responde a las preguntas y luego comprueba tus respuestas. Puedes volver a intentarlo tantas veces como quieras.
Autoevaluación automática para tu propio repaso. Tus respuestas se comprueban en tu navegador y no las vemos ni evaluamos. No es un examen ni una titulación.
Tu progreso
«Completada» solo significa que marcaste la lección como terminada. No es una prueba de tu comprensión. El progreso y las notas se guardan solo en este navegador y en este dispositivo, no en una cuenta.