Cómo actualizar a pandas 3.1: corregir el Pandas4Warning de inplace, usar df.select() y pd.col(), y leer CSV más rápido
Actualizado el

La respuesta corta: pandas 3.1.0 salió el 22 de octubre de 2026, y la versión correctiva 3.1.1 llegó el 4 de noviembre. Actualiza directamente a 3.1.1:
pip install --upgrade "pandas>=3.1"
# or, in a uv project
uv add "pandas>=3.1"Después de actualizar van a pasar tres cosas:
- Tu código imprime
Pandas4Warningpor cadainplace=Trueendrop,rename,reset_index,set_index,sort_values,dropna,drop_duplicatesy algunos más. La corrección es mecánica:df = df.drop(...)en lugar dedf.drop(..., inplace=True). Muchas veces el aviso viene de una librería que usas, no de tu código. pd.read_csvcon archivos locales grandes es mucho más rápido. Un archivo de 72 MB pasó de 0,23 s en pandas 3.0.1 a 0,02 s en 3.1 en nuestra prueba. Algunos argumentos habituales desactivan la mejora; están listados más abajo.- Tienes una API al estilo Polars:
df.select("a", pd.col("b") * 2)ydf.filter(pd.col("a") > 1).
Si todavía estás en pandas 2.x, el salto a 3.x (Copy-on-Write, el dtype de cadena por defecto) es el cambio grande, y la guía de migración a pandas 3.0 (opens in a new tab) es el punto de partida. Esta guía cubre 3.0 → 3.1 y las trampas con las que nos topamos.
Qué se probó: todo lo marcado como "probado" se ejecutó el 1 de noviembre de 2026 en un Apple M4 Max (16 núcleos, macOS) en un entorno virtual
uvlimpio con CPython 3.14.2, pandas 3.1.0, NumPy 2.5.4 y PyArrow 26.0.0. Los números de comparación vienen de pandas 3.0.1 con NumPy 2.4.2 y sin PyArrow en la misma máquina. El comportamiento en Windows, Linux, conda y Docker se basa en la documentación oficial y no se probó aquí. El 4 de noviembre de 2026 se volvió a ejecutar cada afirmación probada con pandas 3.1.1, con los mismos resultados.
Qué cambia en 3.1 y vas a notar
| Cambio | Qué significa para ti | Acción |
|---|---|---|
inplace obsoleto en 11 métodos (PDEP-8) | Cada llamada emite un Pandas4Warning; se eliminará en pandas 4.0 | Reasigna el resultado: df = df.drop(...) |
read_csv paralelo para archivos locales sin comprimir ≥ 5 MB | 2–3x más rápido en un solo hilo, más hasta 6 hilos por defecto | Nada, salvo que pases usecols, index_col, nrows o un descriptor de archivo (ver abajo) |
DataFrame.select() (acepta expresiones pd.col(), disponibles desde 3.0) | Selecciona y calcula columnas en una sola llamada, al estilo Polars | Opcional; útil en cadenas de métodos |
filter(cond=...) acepta una máscara booleana | df.filter(pd.col("a") > 1) conserva filas | Cuidado: una lista posicional de booleanos sigue seleccionando etiquetas |
| NumPy ≥ 2.0.2 obligatorio (≥ 2.3.3 en Python 3.14) | Los entornos con NumPy 1.x no pueden instalar 3.1 | Actualiza NumPy primero, o fija pandas<3.1 |
| La entrada numérica usa microsegundos por defecto | pd.to_timedelta([1], unit="s") ahora es timedelta64[us], no [s] | Corrige un error de redondeo de 3.0; usa .as_unit() si necesitas una resolución concreta |
dtype="category" en read_csv infiere categorías numéricas | Los códigos postales y los IDs pierden los ceros iniciales | Lee esas columnas primero como str |
La lista completa está en el What's new in 3.1.0 oficial (opens in a new tab). El resto de esta guía recorre las tareas en orden: instalar, corregir los avisos, usar la API nueva, acelerar la carga de CSV y revisar los cambios de comportamiento que pueden alterar resultados sin lanzar ningún error.
- Actualizar a pandas 3.1: Pandas4Warning de inplace y read_csv más rápido
- Cómo actualizar a OpenCode 2.0: migra tu configuración, plugins y sesiones de v1
- Cómo instalar y actualizar a Python 3.15 con uv: importaciones diferidas (lazy imports) y compatibilidad de paquetes
- Tutorial de Claude Code Mods: cómo crear, probar e instalar tu primer mod, con 15 ejemplos de la comunidad
- Cómo usar GPT Image 2.5: Flare vs. Sunburst y precios de la API
- Cómo usar DeepSeek Harness: instalación, configuración y tu primer agente
- Runcell Science: alternativa open source a Claude Science para investigación
- Cómo evitar que Mac se duerma: mantener Codex, Claude Code y agentes de IA corriendo
- OpenClaw vs ZeroClaw vs Pi Agent vs Nanobot: ¿Qué stack de agentes de IA deberías elegir en 2026?
- Cómo Claude Code analiza notebooks de Jupyter en Data Science: lo que sí hace y lo que no
- Claude Code routines: qué son y por qué importan los cron jobs de agentes de IA
- Claude Code Desktop Bypass Permissions: cómo activarlo
- Cómo Construir Dos Agentes en Python con el Protocolo A2A de Google - Tutorial Paso a Paso
- Las 10 principales bibliotecas de visualización de datos en Python que crecen en 2025
Paso 1: instalar pandas 3.1
Requisitos (probado)
pandas 3.1.0 publica wheels para CPython 3.11 a 3.15 (requires_python >= 3.11) y está tanto en PyPI como en conda-forge. El mínimo de NumPy depende de tu versión de Python:
| Python | NumPy mínimo |
|---|---|
| 3.11 – 3.13 | 2.0.2 |
| 3.14 – 3.15 | 2.3.3 |
La página de whatsnew solo menciona 2.0.2. En Python 3.14 los metadatos del wheel piden 2.3.3, que es lo que el resolutor exige. Fijar NumPy 1.x falla directamente:
× No solution found when resolving dependencies:
╰─▶ Because pandas==3.1.1 depends on numpy>=2.3.3 and you require
pandas==3.1.1, we can conclude that you require numpy>=2.3.3.
And because you require numpy<2, we can conclude that your requirements
are unsatisfiable.Si una dependencia todavía fija numpy<2, es esa dependencia la que bloquea la actualización, no pandas. Actualízala o mantén pandas<3.1 hasta que lo haga.
Actualizar (probado con uv)
Instala 3.1.1, no 3.1.0: 3.1.1 corrige una regresión por la que read_csv y to_csv trataban objetos de ruta como el tmpdir de pytest como descriptores de archivo abiertos y podían lanzar un error o escribir archivos incompletos (What's new in 3.1.1 (opens in a new tab)).
# pip
pip install --upgrade "pandas>=3.1,<3.2"
# uv project
uv add "pandas>=3.1,<3.2"
# uv, throwaway environment to test first
uv venv p31 --python 3.14
uv pip install --python p31/bin/python "pandas==3.1.1" pyarrow
p31/bin/python -c "import pandas as pd; print(pd.__version__)"conda-forge tiene 3.1.1 (comprobado el 4 de noviembre de 2026; conda install -c conda-forge "pandas>=3.1"), no probado aquí.
Instala también PyArrow si aún no lo tienes. pandas 3 lo usa para el dtype de cadena por defecto cuando está presente, y el lector CSV con engine="pyarrow" lo necesita.
Paso 2: corregir el Pandas4Warning de inplace
Después de actualizar, un código como este:
df.drop(columns=["rain"], inplace=True)imprime:
Pandas4Warning: The inplace keyword in DataFrame.drop is deprecated and will be removed in a future version (PDEP-8).
See https://pandas.pydata.org/docs/dev/whatsnew/v3.1.0.html#deprecation-inplace for more details.Qué métodos están afectados (probado)
La deprecación cubre los métodos en los que inplace=True nunca cambió los valores en el sitio. En dropna, sort_values y los demás métodos que reordenan filas siempre fue una reasignación, y la copia que drop, rename o set_index evitaban antes ya no se produce de todos modos con Copy-on-Write, que llegó en pandas 3.0.
| Método | inplace en 3.1 | Corrección |
|---|---|---|
drop, rename, rename_axis | Obsoleto | df = df.drop(...) |
reset_index, set_index | Obsoleto | df = df.reset_index() |
sort_values, sort_index | Obsoleto | df = df.sort_values("col") |
dropna, drop_duplicates | Obsoleto | df = df.dropna() |
eval, query | Obsoleto | df = df.query("a > 1") |
fillna, replace, where, mask, clip, interpolate | Se mantiene (pueden actualizar valores en el sitio) | No hace falta cambiar nada |
En nuestra prueba, los 11 métodos obsoletos avisaron al llamarlos con inplace=True; fillna y replace no, tal como está documentado.
El reemplazo es un cambio de una línea por llamada:
# before
df.rename(columns={"temp": "temp_c"}, inplace=True)
df.sort_values("temp_c", inplace=True)
df.reset_index(drop=True, inplace=True)
# after (same cost, chains cleanly)
df = (
df.rename(columns={"temp": "temp_c"})
.sort_values("temp_c")
.reset_index(drop=True)
)Encontrar todas las llamadas en tu código (probado)
Convierte el aviso en error durante una ejecución y el traceback te señala la línea exacta. python -W no acepta una clase de terceros como pandas.errors.Pandas4Warning: imprime Invalid -W option ignored: invalid module name: 'pandas.errors' y ejecuta el script de todos modos. Filtra por el mensaje en su lugar:
python -W "error:The inplace keyword:DeprecationWarning" your_script.pyPara convertir en error cualquier Pandas4Warning, no solo los de inplace:
python -c "import runpy, warnings, pandas as pd; warnings.simplefilter('error', pd.errors.Pandas4Warning); runpy.run_path('your_script.py', run_name='__main__')"Ambos terminan con un traceback en la primera línea con inplace. El filtro por mensaje no toca los demás avisos de deprecación.
Para una suite de tests, pytest aplica sus filtros -W cuando pandas ya se puede importar, así que ahí el nombre de la clase sí funciona:
pytest -W error::pandas.errors.Pandas4WarningPandas4Warning es una subclase de PandasDeprecationWarning, así que un filtro sobre cualquiera de las dos funciona. Un grep también te lleva casi hasta el final:
grep -rn "inplace=True" --include="*.py" --include="*.ipynb" .Cuando el aviso viene de una librería, no de ti
Es el caso habitual en las primeras semanas tras una versión nueva. Entre el 22 de octubre y el 1 de noviembre de 2026, GitHub sumó unos 300 issues y pull requests que mencionan Pandas4Warning y unos 3500 que mencionan "pandas 3.1", en proyectos como yfinance (29 llamadas inplace en 8 archivos, yfinance #3009 (opens in a new tab); corregido en yfinance 1.8.0 el 3 de noviembre), FLAML, marimo, cuDF, darts y category_encoders.
Eso no lo puedes corregir desde tu código. Opciones, por orden de preferencia:
- Actualiza la librería. Muchas publicaron la corrección en cuestión de días.
- Silencia solo esta clase de aviso hasta que la librería se ponga al día, en lugar de silenciar todos los avisos:
import warnings
import pandas as pd
warnings.filterwarnings("ignore", category=pd.errors.Pandas4Warning)- Fija
pandas<3.1en el entorno que ejecuta la librería sin mantenimiento. No fijes pandas 3.0 para siempre; la palabra claveinplacese eliminará en 4.0 y el aviso es tu periodo de preaviso.
Otras deprecaciones con las que puedes toparte en 3.1 (probado salvo que se indique)
| Código que ahora avisa | Reemplazo |
|---|---|
s.dt.dayofweek, .dayofyear, .daysinmonth | s.dt.day_of_week, .day_of_year, .days_in_month |
df.select_dtypes(include="object") en columnas de cadena (ya avisaba en 3.0; 3.1 añade exclude="object") | Pasa "str" de forma explícita; "object" ya no significa "texto" en pandas 3 |
s.fillna("missing") en una columna float | Convierte primero a un dtype común; esto lanzará un error en una versión futura |
ser.values en una columna con zona horaria | ser.array, ser.to_numpy(), o ser.dt.tz_convert(None).to_numpy() para conservar el array UTC antiguo |
pd.infer_freq(idx) devolviendo "D" | Devolverá un objeto offset; usa .freqstr o define future.infer_freq_returns_offset |
read_parquet(engine="fastparquet") (docs) | fastparquet está retirado; usa el motor PyArrow por defecto |
pd.set_eng_float_format() (docs) | pd.set_option("display.precision", n) |
Paso 3: usar df.select(), pd.col() y filter(cond=)
DataFrame.select() y filter(cond=) son la API nueva visible en 3.1. pd.col() en sí llegó en pandas 3.0 para assign() y .loc; en 3.1 pasa a ser el argumento natural de los dos métodos nuevos. Si has usado Polars, te resultarán familiares.
select selecciona y calcula columnas en una sola llamada (probado)
import pandas as pd
df = pd.DataFrame({
"city": ["Tokyo", "Seoul", "Osaka"],
"temp": [18.2, 15.1, 19.5],
"rain": [12, 30, 8],
})
df.select("city", "temp") # same as df[["city", "temp"]]
df.select("city", temp_f=pd.col("temp") * 1.8 + 32) # keyword names the new column city temp_f
0 Tokyo 64.76
1 Seoul 59.18
2 Osaka 67.10Los argumentos posteriores pueden referirse a columnas creadas antes en la misma llamada, como en assign:
df.select(temp_c=pd.col("temp"), temp_f=pd.col("temp_c") * 1.8 + 32)pd.col("temp") es un pandas.api.typing.Expression. Admite los operadores y accesores que usarías sobre una Series, incluidos .str y .round():
df.select(pd.col("city").str.upper(), pd.col("temp").round(0))Tres cosas que nos hicieron tropezar (probado):
- No hay
.alias(). Nombra una columna calculada con la forma de palabra clave (temp_f=...).pd.col("temp").alias("x")se construye sin quejarse, pero luegodf.select(...)falla conAttributeError: 'Series' object has no attribute 'alias'. - Una expresión que se reduce a un escalar, como
pd.col("temp").mean(), debe pasarse como palabra clave (mean_temp=pd.col("temp").mean()); entonces se propaga a todas las filas. Pasada de forma posicional lanzaTypeError. - Una columna inexistente lanza
KeyError, igual quedf[["nope"]].
filter(cond=) conserva filas con una máscara booleana (probado)
DataFrame.filter antes solo seleccionaba etiquetas (items=, like=, regex=). En 3.1 también acepta una máscara:
df.filter(cond=df["temp"] > 17) # a boolean Series
df.filter(pd.col("temp") > 17) # an expression, positional is fine
df.filter(lambda d: d["rain"] < 20) # a callableEncadenado, se lee como Polars sin dejar de ser un DataFrame de pandas:
(
df.filter(pd.col("rain") < 20)
.select("city", "temp")
.sort_values("temp")
)Los valores faltantes en la máscara cuentan como False por defecto; pasa na=True para conservarlos o na="raise" para que lance un error.
La trampa: una lista simple de booleanos pasada de forma posicional sigue yendo por la ruta de etiquetas. En nuestra prueba, df.filter([True, False, True]) devolvió un DataFrame con cero columnas y un UserWarning que nos indicaba usar cond=. Escribe siempre cond= cuando la máscara sea una lista o un array.
Polars y pandas 3.1 lado a lado
| Tarea | Polars | pandas 3.1 |
|---|---|---|
| Seleccionar columnas | df.select("a", "b") | df.select("a", "b") |
| Calcular una columna | df.select(pl.col("a") * 2) | df.select(pd.col("a") * 2) |
| Nombrarla | (pl.col("a") * 2).alias("a2") | df.select(a2=pd.col("a") * 2) |
| Filtrar filas | df.filter(pl.col("a") > 1) | df.filter(pd.col("a") > 1) |
| Agregar | df.select(pl.col("a").mean()) → 1 fila | df.select(m=pd.col("a").mean()) → se propaga a todas las filas |
pandas no se está convirtiendo en Polars: no hay evaluación perezosa ni optimizador de consultas detrás de select. Es sintaxis que hace legibles las cadenas y te evita repetir el nombre del DataFrame. Si te planteas cambiar de motor, Polars vs pandas cubre esa decisión.
Paso 4: conseguir el read_csv más rápido, y saber qué lo desactiva
El benchmark (probado)
Un archivo: 3 000 000 filas × 3 columnas (float, int, cadena), 72,5 MB, sin comprimir, en SSD local. Mejor de tres ejecuciones.
| Lector | Tiempo | vs pandas 3.0.1 |
|---|---|---|
pandas 3.0.1, engine="c" | 0,233 s | 1x |
pandas 3.1.0, mode.max_threads = 1 (serie) | 0,089 s | 2,6x |
| pandas 3.1.0, 2 hilos | 0,049 s | 4,8x |
| pandas 3.1.0, 4 hilos | 0,025 s | 9,3x |
| pandas 3.1.0, por defecto (6 hilos) | 0,018 s | 13x |
pandas 3.1.0, engine="pyarrow" | 0,030 s | 7,8x |
La fila en serie coincide con las notas de la versión ("dos a tres veces más rápido"); las filas en paralelo se suman a eso. Tu archivo será distinto (más columnas de cadena se parsean más lento), pero la forma del resultado es lo importante: el lector engine="c" por defecto ahora es más rápido que PyArrow para un archivo local normal.
Las condiciones (docs, confirmadas por los tiempos de arriba)
La ruta paralela se activa automáticamente cuando se cumplen todas estas condiciones:
filepath_or_bufferes una ruta a un archivo local sin comprimir- se usa el motor C por defecto
- las filas de datos suman al menos 5 MB
mode.max_threadses mayor que 1 (por defecto: número de CPUs, con tope en 6, y 1 cuando el proceso está limitado a una CPU)- ninguna opción que necesite el archivo completo de una vez:
iterator,chunksize,nrows,usecols,index_col,parse_dates, unskiprowsde tipo lista o callable, cabeceras de varias filas, o una codificación distinta de UTF-8
Cualquier otro caso cae en el lector en serie, y el resultado siempre es idéntico al de una lectura en serie.
Cómo se vio en nuestra prueba (probado)
| Llamada | Tiempo | ¿Paralelo? |
|---|---|---|
pd.read_csv("big.csv") | 0,018 s | sí |
pd.read_csv("big.csv", usecols=["a", "c"]) | 0,079 s | no |
pd.read_csv("big.csv", index_col=0) | 0,091 s | no |
pd.read_csv("big.csv", nrows=1_000_000) | 0,032 s | no (en serie, un tercio del archivo) |
pd.read_csv("big.csv", converters={"b": int}) | 0,255 s | no (más lento que una lectura simple, pero aún más rápido que la misma llamada en 3.0) |
pd.read_csv(open("big.csv", "rb")) | 0,091 s | no (descriptor de archivo, no una ruta) |
pd.read_csv(io.StringIO(text)) | 0,131 s | no |
pd.read_csv("big.csv.gz") | 0,163 s | no (comprimido) |
pd.concat(pd.read_csv("big.csv", chunksize=500_000)) | 0,099 s | no |
pd.read_csv("big.csv", dtype={...}) | 0,034 s | sí (un dtype explícito no molesta) |
pd.read_csv("big.csv", na_values=["z"]) | 0,025 s | sí |
Dos consecuencias prácticas:
usecolsahora puede hacer una lectura más lenta, no más rápida. Durante años el consejo fue "pasausecolspara saltarte las columnas que no necesitas". En un archivo local ≥ 5 MB con 3.1, leer todo en paralelo y descartar columnas después fue 4x más rápido queusecolsen nuestra prueba. Mide antes de conservar la costumbre.- Pasa una ruta, no un descriptor.
pd.read_csv("file.csv")es paralelo;pd.read_csv(open("file.csv"))no. Los wrappers que entregan a pandas un objeto de archivo abierto en lugar de una ruta pierden la mejora.
Bajar el paralelismo dentro de una aplicación ya paralela
Si pandas corre dentro de Dask, Ray, un pool de multiprocessing o un worker web que ya usa todos los núcleos, limita los hilos para que no compitan entre sí:
import pandas as pd
with pd.option_context("mode.max_threads", 1):
df = pd.read_csv("large.csv")
# or globally
pd.set_option("mode.max_threads", 2)En Docker y Kubernetes el tope sigue la cuota de CPU del cgroup (docs, no probado aquí), así que un contenedor limitado a una CPU lee en serie sin que hagas nada.
Paso 5: revisar los cambios de comportamiento que no lanzan errores
Estos son los cambios con más probabilidad de alterar un resultado en silencio. Cada uno se reprodujo en 3.0.1 y 3.1.0 (probado).
Timedelta y datetime a partir de números ahora usan microsegundos por defecto
En 3.0, pd.to_timedelta([1], unit="s") producía timedelta64[s], y la aritmética conservaba esa resolución. Al dividirlo, redondeaba a segundos enteros:
td = pd.to_timedelta([1], unit="s")
print(td / 3)
# pandas 3.0.1 → TimedeltaIndex(['0 days'], dtype='timedelta64[s]', freq=None)
# pandas 3.1.x → TimedeltaIndex(['0 days 00:00:00.333333'], dtype='timedelta64[us]', freq=None)3.1 lo corrige usando microsegundos por defecto, sea cual sea el unit con el que conviertas. Si tu código comparaba dtypes o dependía de la resolución [s], usa .as_unit("s") de forma explícita. Timestamp y to_datetime con entrada numérica cambiaron del mismo modo.
Relacionado: Timedelta.total_seconds() ahora incluye los nanosegundos. pd.Timedelta(nanoseconds=1500).total_seconds() devuelve 1.5e-06 en lugar de 1e-06.
dtype="category" en read_csv infiere categorías numéricas
from io import StringIO
data = "zip\n00501\n00501\n10001"
pd.read_csv(StringIO(data), dtype="category")["zip"].cat.categories.tolist()
# pandas 3.0.1 → ['00501', '10001']
# pandas 3.1.0 → [501, 10001] leading zeros goneEl motor C ahora hace lo que el motor PyArrow ya hacía. Para códigos postales, números de cuenta y cualquier identificador con ceros a la izquierda, lee la columna como str y convierte después:
df = pd.read_csv("file.csv", dtype={"zip": "str"})
df["zip"] = df["zip"].astype("category")Cosas que antes pasaban en silencio y ahora lanzan errores
pd.Timestamp(datetime(2020, 12, 31), hour=5)lanzóValueErroren nuestra prueba; en 3.0 ignorabahoursin avisar. Pasa todos los componentes como palabras clave.pd.to_timedelta("3:25:00 PM")lanza un error; 3.0 lo interpretaba como tres horas y descartaba elPM.- La aritmética de DataFrame y las comparaciones de orden con una Series
categorya lo largo delaxis="columns"por defecto (por ejemplodf + cat_series) ahora lanzanTypeErroren lugar de operar sobre las categorías; la igualdad no cambia (docs, reproducido). Index.valueseIndex.arraydevuelven arrays de solo lectura, así que el código que mutaba un índice a través de ellos ahora falla de forma visible en lugar de corromper las búsquedas (docs).
Regex en cadenas respaldadas por PyArrow (probado)
Con PyArrow instalado, \w, \d, \s, \b en .str.replace, .str.count y similares solo coincidían con ASCII en pandas 3.0. En 3.1 vuelven a coincidir con Unicode, en línea con el re de Python y con pandas 2.x. Con PyArrow 26.0.1, pd.Series(["héllo wörld", "naïve 東京"]).str.count(r"\w") devolvió [8, 4] en pandas 3.0.6 y [10, 7] en 3.1.1. Si procesas texto no inglés y viste caer los conteos al pasar a 3.0, esta es la corrección. 3.1.1 también corrige que \s no coincidiera con \x0b ni con \x1c en cadenas de PyArrow.
¿Deberías actualizar ya?
| Situación | Recomendación |
|---|---|
| Proyecto nuevo, Python 3.11+ | Sí. Empieza en 3.1 y escribe sin inplace |
| Scripts y notebooks en 3.0 | Sí. Ejecuta una vez con -W "error:The inplace keyword:DeprecationWarning" (o pytest -W error::pandas.errors.Pandas4Warning), corrige las líneas y quédate con las lecturas CSV más rápidas |
Librería o pipeline que carga IDs con ceros iniciales con dtype="category" | Actualiza, pero añade antes dtype={"id": "str"} |
| Depende de FLAML, darts o similares | Actualiza cuando publiquen sus correcciones para 3.1 (yfinance ya lo hizo, en 1.8.0), o filtra Pandas4Warning mientras tanto |
| Todavía en pandas 2.x | Haz primero la migración 2 → 3 (Copy-on-Write, dtype de cadena); 3.1 es pequeño encima de eso |
| Todavía en NumPy 1.x | Bloqueado. Actualiza NumPy primero |
Usar pandas 3.1 en Jupyter
En el notebook en sí no cambia nada. Dos notas:
-W errores una opción de línea de comandos y no se aplica a un kernel en ejecución. Ponwarnings.simplefilter("error", pd.errors.Pandas4Warning)en la primera celda mientras limpias el código, y quítalo después.- El lector CSV paralelo no necesita la build free-threaded de Python; funciona en el intérprete estándar.
Cuando un archivo grande carga en una fracción de segundo, la parte lenta pasa a ser mirarlo. PyGWalker (opens in a new tab) 0.5.0.1 funcionó con pandas 3.1 en nuestra prueba (pyg.to_html(df) renderizó un DataFrame de 50 000 filas en 0,23 s), así que una pasada de arrastrar y soltar sobre el DataFrame recién cargado es un siguiente paso razonable antes de escribir el código de groupby.
Solución de problemas
ResolutionImpossible / "requires numpy>=2.3.3" — estás en Python 3.14 o 3.15 con un pin antiguo de NumPy. Actualiza NumPy o relaja el pin. En Python 3.11–3.13 el mínimo es 2.0.2.
Cientos de líneas Pandas4Warning desde site-packages — es una dependencia, no tu código. Actualízala; si aún no hay corrección, warnings.filterwarnings("ignore", category=pd.errors.Pandas4Warning) y abre un issue con el traceback.
read_csv no es más rápido — revisa la lista del paso 4. Los culpables habituales son usecols, index_col, un archivo comprimido o un objeto de archivo en lugar de una ruta. Confirma también que pd.get_option("mode.max_threads") no es 1 (por defecto es None, que significa "auto").
df.filter([...]) no devolvió columnas — pasaste una lista de booleanos de forma posicional. Usa df.filter(cond=[...]).
AttributeError: 'Series' object has no attribute 'alias' — las expresiones pd.col no tienen un alias que funcione; nombra la columna con la forma de palabra clave de select.
Los códigos postales perdieron los ceros iniciales — dtype="category" ahora infiere enteros. Lee primero como str.
El dtype de Timedelta cambió de [s] a [us] — es lo esperado en 3.1. Usa .as_unit("s") si un sistema posterior necesita segundos.
Preguntas frecuentes
¿Qué es Pandas4Warning y cómo lo corrijo?
Es el aviso de deprecación que pandas 3.1 lanza por el comportamiento que cambia en 4.0, casi siempre por inplace=True en drop, rename, reset_index, set_index, sort_values, dropna y drop_duplicates. Reasigna el resultado en su lugar: df = df.drop(columns=["a"]). Ejecuta python -W "error:The inplace keyword:DeprecationWarning" your_script.py (o pytest -W error::pandas.errors.Pandas4Warning para una suite de tests) para que se detenga con un traceback en la primera llamada que quede; corrígela y vuelve a ejecutar.
¿Se ha eliminado inplace=True en pandas 3.1?
No. Sigue funcionando en 3.1 e imprime un aviso. La eliminación está prevista para pandas 4.0, y solo en los métodos que nunca modificaron los datos en el sitio. fillna, replace, where, mask, clip e interpolate conservan la palabra clave.
¿Por qué read_csv es más rápido, y por qué el mío no?
El motor C es 2–3x más rápido y los archivos locales grandes se leen en paralelo por defecto. Si no notas ningún cambio, probablemente estés pasando usecols, index_col, nrows, chunksize, un archivo comprimido o un objeto de archivo abierto. Consulta la tabla del paso 4.
¿df.select() es lo mismo que el select de Polars?
La sintaxis es parecida a propósito, pero pandas evalúa de forma inmediata y no tiene optimizador de consultas. Úsalo para cadenas legibles, no por rendimiento.
¿Funciona pandas 3.1 con Python 3.15?
Sí. Se publican wheels para CPython 3.11 a 3.15. En 3.14 y 3.15 necesitas NumPy 2.3.3 o posterior. Para la parte de Python de esa actualización, consulta nuestra guía para actualizar a Python 3.15.
Guías relacionadas
- Tutorial de pandas read_csv(): importa archivos CSV
- Cómo eliminar una columna en pandas
- Cómo filtrar filas en pandas
- Cómo renombrar columnas en pandas
- Polars vs pandas
- Cómo instalar y actualizar a Python 3.15
- Cómo actualizar paquetes de Python