Lección 0.5.2 · 45 min

pandas 3.0: Series, DataFrame, índices temporales, Copy-on-Write, tipo str y pd.col()

pandas es la tabla de trabajo del research en Python: precios con fechas, operaciones con columnas de texto, resultados alineados por índice. La versión 3.0 cambia tres cosas que rompen código antiguo —Copy-on-Write, el tipo str y la resolución de las fechas— y añade pd.col(). Esta lección las enseña con los datos del curso.

02/05
Módulo
0.5 · Computación numérica y datos
Duración
45 min
Lecciones del nivel 0
  1. 0.1 · Bienvenida y expectativas

    1. 0.1.1 Trading sistemático, algorítmico y cuantitativo frente al discrecional
    2. 0.1.2 Expectativas realistas: costes, tiempo y riesgo de pérdida
    3. 0.1.3 Estructura del curso, tests de entrada, repositorio personal y aprendizaje con IA
  2. 0.2 · Entorno de trabajo moderno

    1. 0.2.1 Instalación de Python con uv: versiones y ciclo de soporte
    2. 0.2.2 Entornos virtuales y dependencias: uv init, uv add, pyproject.toml y lockfiles
    3. 0.2.3 Editores (VS Code, Cursor, PyCharm) y notebooks (Jupyter, JupyterLab, marimo)
    4. 0.2.4 Terminal básica, rutas, variables de entorno y ficheros .env
    5. 0.2.5 Git y GitHub: commits, ramas, pull requests y .gitignore
    6. 0.2.6 Introducción a los asistentes de código con IA: prompts, revisión y tests
  3. 0.3 · Fundamentos del lenguaje

    1. 0.3.1 Variables, tipos y operadores; coma flotante y decimal para precios y ticks
    2. 0.3.2 Cadenas, f-strings y formato de números y fechas
    3. 0.3.3 Listas, tuplas, diccionarios, conjuntos y comprensiones
    4. 0.3.4 Control de flujo: if, elif, else, for, while y match-case
    5. 0.3.5 Funciones: argumentos, *args y **kwargs, lambdas y ámbito
    6. 0.3.6 Excepciones; módulos y paquetes, con los imports perezosos de Python 3.15
  4. 0.4 · Python intermedio

    1. 0.4.1 Programación orientada a objetos: clases, herencia, composición y dataclasses
    2. 0.4.2 Tipado: type hints, mypy o ty y Pydantic para configuraciones
    3. 0.4.3 Iteradores, generadores, decoradores y context managers
    4. 0.4.4 Ficheros (CSV, JSON, YAML, TOML) y fechas con zonas horarias
    5. 0.4.5 Logging profesional y testing con pytest
    6. 0.4.6 Concurrencia: threading, multiprocessing, asyncio y free-threading
  5. 0.5 · Computación numérica y datos

    1. 0.5.1 NumPy 2: arrays, broadcasting, vectorización, álgebra lineal y generadores aleatorios
    2. 0.5.2 pandas 3.0: Series, DataFrame, índices temporales, Copy-on-Write, tipo str y pd.col()
    3. 0.5.3 Selección, filtrado, groupby, merge, pivot, resample y ventanas móviles
    4. 0.5.4 Polars: API lazy frente a eager, expresiones, rendimiento y cuándo elegirlo frente a pandas
    5. 0.5.5 DuckDB y SQL esencial: SELECT, JOIN, GROUP BY y funciones de ventana sobre Parquet
  6. 0.6 · Visualización

    1. 0.6.1 Matplotlib orientado a objetos, Seaborn y Plotly: gráficos de precios, drawdown y velas
    2. 0.6.2 Dashboards rápidos con Streamlit: un panel de seguimiento en un solo script
  7. 0.7 · Matemáticas y estadística mínimas

    1. 0.7.1 Rentabilidades simples y logarítmicas, interés compuesto y estadística descriptiva
    2. 0.7.2 Probabilidad y distribuciones, correlación frente a causalidad y regresión simple
    3. 0.7.3 Álgebra lineal intuitiva: vectores y matrices como preparación para carteras

Series y DataFrame

La lección 0.5.1 trabajaba con arrays de NumPy: números sin nombre y sin fecha. Para research hace falta más: saber a qué sesión corresponde cada cierre, qué columna es el volumen y qué operación es de qué activo. pandas añade eso sobre NumPy con dos estructuras:

  • Series: un array de una dimensión con un índice (las etiquetas de las filas) y un nombre. Los cierres de SINT son una Series indexada por fecha.
  • DataFrame: varias columnas que comparten el mismo índice, cada una con su tipo. Las barras diarias de SINT (apertura, máximo, mínimo, cierre y volumen) son un DataFrame.

Las versiones del curso son las de curso-code/uv.lock: pandas 3.0.6, la serie 3.0 que se publicó en enero de 2026. Todo lo que sigue funciona con ella, y las diferencias con pandas 2 se señalan donde aparecen.

IN [01]python
import numpy as np
import pandas as pd

precios = pd.read_csv("sint_diario.csv", parse_dates=["fecha"],
                      index_col="fecha")
print(pd.__version__, type(precios).__name__, precios.shape)
print(precios.dtypes)
precios.head(3)
OUT [01]Las barras diarias de SINT
3.0.6 DataFrame (1303, 5)
apertura    float64
maximo      float64
minimo      float64
cierre      float64
volumen       int64
dtype: object
            apertura  maximo  minimo  cierre  volumen
fecha
2021-01-04    100.02  100.64   99.07   99.14  2322609
2021-01-05     99.32   99.79   98.63   99.25  1517954
2021-01-06     99.10   99.24   97.37   97.38  3245658

parse_dates convierte la columna de texto en fechas e index_col la usa como índice. Sin esas dos opciones, las fechas serían texto y el índice, los números de fila 0, 1, 2…: todo lo que viene después dejaría de funcionar. Cada columna tiene su tipo: los precios son float64 y el volumen, int64, porque es un número entero de títulos.

Una columna de un DataFrame es una Series. Las operaciones vectorizadas de NumPy siguen funcionando, y pandas añade métodos con nombre para las más comunes en finanzas, como pct_change (la rentabilidad simple de la lección 0.5.1):

IN [02]python
cierre = precios["cierre"]
print(type(cierre).__name__, cierre.name, cierre.dtype)
r = cierre.pct_change()
print(r.head(3))
print(f"Media diaria: {r.mean():.4%}  ·  sesiones: {r.count()}")
OUT [02]Una columna es una Series
Series cierre float64
fecha
2021-01-04         NaN
2021-01-05    0.001110
2021-01-06   -0.018841
Name: cierre, dtype: float64
Media diaria: 0.0342%  ·  sesiones: 1302

La primera rentabilidad es NaN: el primer día no tiene cierre anterior. pandas no la elimina y, a diferencia de np.mean, mean() y count() ignoran los NaN por defecto, por eso salen 1.302 rentabilidades de 1.303 cierres. Es cómodo y peligroso a la vez: un hueco en los datos no rompe la media, así que tampoco avisa.

Alineación por índice

La diferencia de fondo entre pandas y NumPy es la alineación: cuando dos Series operan juntas, pandas empareja los valores por su etiqueta, no por su posición. Con dos activos que cotizan en fechas distintas —un mercado cierra por festivo, un proveedor no tiene un día—, el resultado tiene la unión de las fechas y NaN donde falta uno de los dos. Un ejemplo con SINT y RVAR, el activo de renta variable de cartera_sint.csv, en dos tramos que solo se solapan dos días:

IN [03]python
cartera = pd.read_csv("cartera_sint.csv", parse_dates=["fecha"],
                      index_col="fecha")
sint = cierre["2024-01-02":"2024-01-05"]
rvar = cartera["RVAR"]["2024-01-04":"2024-01-09"]
(sint / rvar).round(4)
OUT [03]Dos activos, dos tramos de fechas
fecha
2024-01-02       NaN
2024-01-03       NaN
2024-01-04    0.7574
2024-01-05    0.7577
2024-01-08       NaN
2024-01-09       NaN
dtype: float64

Con arrays de NumPy, dividir dos vectores de cuatro elementos habría emparejado el 2 de enero de SINT con el 4 de enero de RVAR sin dar ningún error: un ratio entre precios de días distintos. pandas lo impide: solo divide lo que corresponde a la misma fecha y marca el resto como ausente. Qué hacer con esos ausentes (descartarlos, arrastrar el último precio) es una decisión que la lección 0.5.3 trata con merge, join y reindex.

Índices temporales

Un índice de fechas, DatetimeIndex, permite seleccionar por fechas escritas como texto. La selección parcial —solo el año o el año y el mes— devuelve todas las sesiones de ese periodo, y los rangos con loc incluyen los dos extremos (al contrario que los cortes por posición de Python):

IN [04]python
print(precios.index.dtype, precios.index.min().date(),
      precios.index.max().date())
print(len(precios.loc["2024"]), "sesiones en 2024")
print(precios.loc["2022-03", "cierre"].iloc[[0, -1]])
precios.loc["2022-06-01":"2022-06-07", ["cierre", "volumen"]]
OUT [04]Selección por fechas
datetime64[us] 2021-01-04 2025-12-31
262 sesiones en 2024
fecha
2022-03-01    117.20
2022-03-31    111.28
Name: cierre, dtype: float64
            cierre  volumen
fecha
2022-06-01  108.39  1463683
2022-06-02  105.80  1832370
2022-06-03  104.44  1709652
2022-06-06  102.97  2301634
2022-06-07  102.23  1637924

El índice es datetime64[us]: fechas con resolución de microsegundo. Es un cambio de pandas 3.0: al leer fechas de texto, la resolución por defecto pasa de nanosegundo a microsegundo (la de nanosegundo solo se usa si el texto lo necesita). Para seleccionar o comparar fechas no cambia nada; donde muerde es al convertir fechas en números, algo habitual al enviar marcas de tiempo a la API de un bróker, que suele esperarlas en milisegundos desde 1970:

IN [05]python
tres = precios.index[:3]
print(tres.astype("int64").tolist())
print(tres.as_unit("ns").astype("int64").tolist())
print(tres.as_unit("ms").astype("int64").tolist())
OUT [05]Fechas como enteros: la unidad importa
[1609718400000000, 1609804800000000, 1609891200000000]
[1609718400000000000, 1609804800000000000, 1609891200000000000]
[1609718400000, 1609804800000, 1609891200000]

El mismo 4 de enero de 2021 se convierte en un número mil veces menor que en pandas 2. Un código antiguo que dividía entre 10**6 para pasar de nanosegundos a milisegundos ahora produce fechas de 1970 sin dar ningún error. La regla: antes de convertir a entero, fija la unidad con as_unit.

El índice sabe qué día de la semana es cada sesión, cuántas hay por año y si hay huecos. Un vistazo rápido al calendario de los datos antes de calcular nada evita sorpresas:

IN [06]python
print(precios.index.year.value_counts().sort_index().to_dict())
print((precios.index.dayofweek >= 5).sum(), "sesiones en fin de semana")
precios.loc["2025-12-23":"2025-12-29", "cierre"]
OUT [06]El calendario de los datos
{2021: 260, 2022: 260, 2023: 260, 2024: 262, 2025: 261}
0 sesiones en fin de semana
fecha
2025-12-23    139.43
2025-12-24    139.36
2025-12-25    139.32
2025-12-26    139.23
2025-12-29    139.56
Name: cierre, dtype: float64

No hay fines de semana, pero el 25 de diciembre de 2025 tiene cierre. No es un error de lectura: SINT es un activo ficticio que cotiza todos los días hábiles, sin festivos, como dice el README de los datos. Un mercado real cierra en Navidad, y los calendarios de cada bolsa (lección 0.4.4) son la referencia para saber qué sesiones deberían existir. Comprobar el calendario es el primer paso de cualquier limpieza de datos: un festivo con precio, o una sesión que falta, desplaza todas las rentabilidades de alrededor.

Copy-on-Write: cada objeto es suyo

En pandas 2, precios["cierre"] podía devolver una vista: un objeto que compartía memoria con el DataFrame, de modo que cambiarlo cambiaba también el original. Otras operaciones devolvían una copia. Cuál de las dos dependía de detalles internos, y el aviso SettingWithCopyWarning intentaba, sin mucho éxito, avisar de los casos dudosos.

pandas 3.0 elimina la ambigüedad con Copy-on-Write (CoW): el resultado de cualquier indexado o método se comporta como una copia. Por dentro no copia nada mientras nadie escriba (por eso es rápido); la copia real se hace en el momento de la primera escritura:

lecturalecturaescrituraprecioscierreMemoria comúnCopia de cierre
Fuente Mermaid
flowchart LR
  A[precios] --> B[cierre]
  B -->|lectura| C[Memoria común]
  A -->|lectura| C
  B -->|escritura| D[Copia de cierre]

En la práctica, solo hay una forma de cambiar un objeto: escribir sobre ese objeto directamente.

IN [07]python
df = precios.copy()
col = df["cierre"]
col.iloc[0] = 0.0          # se escribe en la Series extraída
print(col.iloc[0], df["cierre"].iloc[0])
OUT [07]Modificar una columna extraída
0.0 99.14

col cambia y df no. En pandas 2 sin CoW, esta misma celda habría puesto un cero en el primer cierre de df: un precio de cero que nadie escribió a propósito y que aparece después como una caída del 100 % en un backtest.

La otra cara es la asignación encadenada: dos indexados seguidos y una asignación al final. Es el error más frecuente al construir una columna de señal, y en pandas 3.0 no funciona nunca:

IN [08]python
import warnings

df["senal"] = 0
with warnings.catch_warnings(record=True) as avisos:
    warnings.simplefilter("always")
    df["senal"][df["cierre"] > 120] = 1     # encadenada
print([a.category.__name__ for a in avisos])
print("Con asignación encadenada:", df["senal"].sum())

df.loc[df["cierre"] > 120, "senal"] = 1     # un solo paso
print("Con .loc:", df["senal"].sum())
OUT [08]Asignación encadenada frente a .loc
['ChainedAssignmentError']
Con asignación encadenada: 0
Con .loc: 523

df["senal"] devuelve un objeto intermedio que se comporta como una copia; la asignación se hace sobre él y se pierde. pandas lo detecta y emite el aviso ChainedAssignmentError (en la celda se recoge con warnings para mostrar solo su nombre; en un notebook sale el mensaje completo). La versión con .loc indica filas y columna en una sola operación sobre df, y es la única que cambia el DataFrame: 523 sesiones con cierre por encima de 120.

Patrónpandas 2pandas 3.0
df["c"][mascara] = vCambiaba df (a veces con aviso)No cambia nada; ChainedAssignmentError
s = df["c"]; s.iloc[0] = vPodía cambiar dfSolo cambia s
df.loc[mascara, "c"] = vCambia dfCambia df
sub = df[mascara].copy().copy() para callar el aviso.copy() innecesario

El tipo str y PyArrow

Hasta pandas 2, una columna de texto era de tipo object: un array de punteros a cadenas de Python, cada una en su propio sitio de la memoria, igual que una lista. pandas 3.0 infiere por defecto un tipo de texto propio, str, guardado con PyArrow cuando está instalado (en el proyecto del curso lo está, porque lo usa Parquet). El libro de operaciones ficticias tiene dos columnas de texto:

IN [09]python
ops = pd.read_csv("operaciones_sint.csv",
                  parse_dates=["fecha_entrada", "fecha_salida"])
print(ops.dtypes)
print(ops["lado"].dtype.storage, ops["lado"].dtype.na_value)
ops.iloc[:3, :6]
OUT [09]El libro de operaciones
id                         int64
fecha_entrada     datetime64[us]
fecha_salida      datetime64[us]
simbolo                      str
lado                         str
cantidad                   int64
precio_entrada           float64
precio_salida            float64
comision                 float64
dtype: object
pyarrow nan
   id fecha_entrada fecha_salida simbolo   lado  cantidad
0   1    2021-03-04   2021-04-26     ORO  corto       200
1   2    2021-09-02   2021-10-12    SINT  largo       180
2   3    2021-10-27   2021-12-08    SINT  corto        10

simbolo y lado son str, con almacenamiento pyarrow y NaN como valor ausente (el mismo que en las columnas numéricas). Las fechas de entrada y salida, como el índice de precios, son datetime64[us].

El cambio es intencionado y rompe un patrón muy extendido: buscar las columnas de texto comparando su tipo con object.

IN [10]python
lado = ops["lado"]
print(lado.dtype == object)
print(lado.dtype == "str")
print(pd.api.types.is_string_dtype(lado))
print(ops.select_dtypes(include="str").columns.tolist())
OUT [10]Comprobar si una columna es de texto
False
True
True
['simbolo', 'lado']

dtype == object devuelve False sin dar error: el código que limpiaba las columnas de texto así simplemente deja de encontrarlas. Las dos formas robustas son pd.api.types.is_string_dtype, que acepta el tipo antiguo y el nuevo, y select_dtypes(include="str"). select_dtypes(include="object") todavía incluye las de texto por compatibilidad, pero con un aviso de que dejará de hacerlo.

El tipo str es además más estricto: solo admite cadenas o ausentes. Un número en la columna lado era posible con object y era un error latente; ahora se rechaza al escribirlo:

IN [11]python
ops.loc[0, "lado"] = 1
OUT [11]Una columna str solo admite texto
TypeError: Invalid value '1' for dtype 'str'. Value should be a string or missing value, got 'int' instead.

Las operaciones de texto siguen en el accesor .str (ops["simbolo"].str.upper(), .str.startswith("l")) y las comparaciones (ops["lado"] == "largo") funcionan igual. Con PyArrow, además, la columna ocupa menos memoria y esas operaciones suelen ser más rápidas que con object: en ops, lado ocupa 520 bytes como str y 2.160 como object (memory_usage(deep=True)). En tablas de millones de órdenes, la diferencia se nota.

Expresiones con pd.col()

Para crear columnas derivadas, assign aceptaba hasta ahora valores o funciones que reciben el propio DataFrame (lambda d: d["cierre"] / ...). pandas 3.0 añade pd.col("nombre"): una referencia a una columna que se puede combinar con operadores y con los métodos de Series, y que pandas evalúa sobre el DataFrame en el que se usa. Es la misma idea que las expresiones de Polars (lección 0.5.4):

IN [12]python
barras = precios.assign(
    r=pd.col("cierre").pct_change(),
    rango=(pd.col("maximo") - pd.col("minimo")) / pd.col("cierre"),
)
print(pd.col("cierre").pct_change())
barras[["cierre", "r", "rango"]].head(3).round(4)
OUT [12]Columnas derivadas con pd.col()
col('cierre').pct_change()
            cierre       r   rango
fecha
2021-01-04   99.14     NaN  0.0158
2021-01-05   99.25  0.0011  0.0117
2021-01-06   97.38 -0.0188  0.0192

Impreso, pd.col("cierre").pct_change() no es un número ni una columna: es una expresión pendiente de evaluar. Por eso se puede guardar en una variable y reutilizar. En el libro de operaciones, el signo de cada operación (1 para largo, −1 para corto) sirve para calcular su resultado neto de comisiones:

pnl=signo⋅(Psalida−Pentrada)⋅cantidad−comisiones\text{pnl} = \text{signo} \cdot (P_{\text{salida}} - P_{\text{entrada}}) \cdot \text{cantidad} - \text{comisiones}
IN [13]python
signo = pd.col("lado").map({"largo": 1, "corto": -1})
ops = ops.assign(
    pnl=signo * (pd.col("precio_salida") - pd.col("precio_entrada"))
    * pd.col("cantidad") - pd.col("comision"),
)
print(f"Resultado total: {ops['pnl'].sum():,.2f}")
ops.loc[pd.col("pnl") < -1000, ["id", "simbolo", "lado", "pnl"]]
OUT [13]Resultado de cada operación
Resultado total: 1,149.19
    id simbolo   lado      pnl
0    1     ORO  corto -3123.56
7    8    SINT  largo -1288.04
14  15    SINT  largo -1058.60

pd.col funciona en cualquier sitio que acepte una función del DataFrame: assign, loc y la selección con corchetes. En loc, la expresión pd.col("pnl") < -1000 devuelve la máscara de las operaciones con una pérdida mayor de 1.000 en la moneda del activo: tres de las cuarenta, una en ORO y dos en SINT. El libro en conjunto gana 1.149,19 netos de comisiones (Python formatea los miles con coma: 1,149.19). La ventaja frente a df["pnl"] < -1000 aparece al encadenar métodos: la expresión se refiere al DataFrame del paso en que se evalúa, no a una variable que puede haber quedado desfasada.

Ejercicios

  1. Este código intenta marcar con un 1 en alerta las sesiones de SINT con un volumen mayor que el doble de la mediana, pero la columna se queda a cero. Explica por qué y corrígelo.

    IN [14]python
    v = precios.copy()
    v["alerta"] = 0
    v[v["volumen"] > 2 * v["volumen"].median()]["alerta"] = 1
Solución
IN [15]python
v = precios.copy()
v["alerta"] = 0
v.loc[v["volumen"] > 2 * v["volumen"].median(), "alerta"] = 1
print(v["alerta"].sum(), "sesiones con volumen anómalo")
OUT [15]Alerta de volumen con .loc
51 sesiones con volumen anómalo

v[mascara] devuelve un DataFrame nuevo que se comporta como una copia; ["alerta"] = 1 lo modifica a él y se pierde. Con .loc[mascara, "alerta"], filas y columna se indican en una sola operación sobre v, y se marcan 51 sesiones.

  1. Con pd.col(), añade a precios una columna hueco con el gap de apertura (apertura de hoy entre cierre de ayer, menos uno) y selecciona con loc las sesiones de 2022 en que abrió más de un 1 % por debajo del cierre anterior.
Solución
IN [16]python
gaps = precios.assign(
    hueco=pd.col("apertura") / pd.col("cierre").shift(1) - 1,
)
bajistas = gaps.loc["2022"].loc[pd.col("hueco") < -0.01]
print(len(bajistas), "huecos bajistas de más del 1 %")
bajistas[["apertura", "cierre", "hueco"]].round(4)
OUT [16]Huecos de apertura de 2022
3 huecos bajistas de más del 1 %
            apertura  cierre   hueco
fecha
2022-08-22    109.40  108.59 -0.0127
2022-08-29    111.41  115.26 -0.0108
2022-09-02    113.75  117.11 -0.0109

shift(1) desplaza los cierres una sesión hacia delante, así que cada fila compara su apertura con el cierre anterior: información disponible en el momento de la apertura, sin anticipación. Salen tres sesiones, todas entre finales de agosto y principios de septiembre. El primer loc selecciona el año y el segundo filtra con la expresión, que se evalúa sobre el DataFrame ya recortado.

  1. Escribe una función columnas_texto(df) que devuelva los nombres de las columnas de texto de un DataFrame y que funcione igual con columnas str y con columnas object de pandas 2. Pruébala con ops y con ops.astype({"lado": object}).
Solución
IN [17]python
def columnas_texto(df: pd.DataFrame) -> list[str]:
    return [c for c in df.columns
            if pd.api.types.is_string_dtype(df[c])]

print(columnas_texto(ops))
print(columnas_texto(ops.astype({"lado": object})))
OUT [17]Columnas de texto en pandas 2 y 3
['simbolo', 'lado']
['simbolo', 'lado']

is_string_dtype reconoce el tipo str y también una columna object cuyos valores son cadenas, así que la función da el mismo resultado con los dos tipos. Comparar con object solo encontraría la segunda.

Respuesta plegada en cada pregunta

Autoevaluación

  1. En pandas 3.0, df[df["cierre"] > 120]["senal"] = 1. ¿Qué le pasa a df?

    • ASe pone 1 en senal en los días con cierre mayor que 120
    • BNada: la asignación se hace sobre un objeto intermedio que se comporta como una copia, y pandas avisa con ChainedAssignmentError
    • CLanza una excepción y el programa se para
    • DSe pone 1 en todas las filas
    Ver respuesta

    Respuesta B. Nada: la asignación se hace sobre un objeto intermedio que se comporta como una copia, y pandas avisa con ChainedAssignmentError

    Con Copy-on-Write, el resultado de cualquier indexado se comporta como una copia. La asignación encadenada nunca llega al original; la forma correcta es df.loc[df["cierre"] > 120, "senal"] = 1.

  2. Una columna lado con «largo» y «corto» se lee con pandas 3.0 y PyArrow instalado. ¿Qué devuelve df["lado"].dtype == object?

    • ATrue, como en pandas 2
    • BFalse: la columna es del tipo str
    • CUn error de tipo
    • DDepende del contenido de la primera fila
    Ver respuesta

    Respuesta B. False: la columna es del tipo str

    pandas 3.0 infiere el tipo str para el texto. Las comprobaciones de object dejan de encontrar esas columnas; lo robusto es pd.api.types.is_string_dtype.

  3. Se suman los cierres de dos activos con calendarios distintos: a + b. ¿Qué pasa con las fechas que solo están en uno de los dos?

    • ASe suman por posición, sin mirar las fechas
    • BSe descartan en silencio
    • CAparecen en el resultado con NaN
    • DSe rellenan con el último precio conocido
    Ver respuesta

    Respuesta C. Aparecen en el resultado con NaN

    pandas alinea por índice: el resultado tiene la unión de las fechas, y donde falta uno de los dos operandos el valor es NaN. Rellenar o descartar es una decisión explícita.

  4. En pandas 3.0, pd.read_csv(..., parse_dates=["fecha"]) crea fechas datetime64[us]. ¿Qué devuelve index.astype("int64")?

    • ANanosegundos desde 1970, como en pandas 2
    • BMicrosegundos desde 1970: números 1.000 veces menores que en pandas 2
    • CSegundos desde 1970
    • DDías desde 1970
    Ver respuesta

    Respuesta B. Microsegundos desde 1970: números 1.000 veces menores que en pandas 2

    La resolución por defecto al leer texto pasa a ser el microsegundo. Para obtener otra unidad hay que pedirla con as_unit("ns") o as_unit("ms") antes de convertir a entero.

Ideas clave

  • Una Series es una columna con índice; un DataFrame, varias columnas que comparten índice. Toda operación entre ellas se alinea por índice, no por posición.
  • Con un DatetimeIndex, loc["2022-03"] selecciona un mes y loc["2022-06-01":"2022-06-07"] un rango con los dos extremos incluidos.
  • Copy-on-Write: cualquier indexado se comporta como una copia. Para cambiar un DataFrame, se asigna sobre él en un solo paso con .loc; la asignación encadenada no hace nada.
  • El texto es del tipo str (PyArrow por debajo): las comprobaciones dtype == object fallan en silencio y la columna solo admite cadenas o NaN.
  • pd.col("cierre") construye una expresión que assign y loc evalúan sobre el propio DataFrame, sin lambda.

Recursos

Datos