Pandas 3.0 — Que cambio y por que importa
pandas 3.0 salio en enero de 2025. No es una actualizacion menor: consolida anos de cambios que venian siendo opcionales o experimentales en pandas 2.x y elimina APIs que llevaban tiempo deprecadas. Si tu codigo fue escrito para pandas 1.x o 2.x, probablemente tiene cosas que ya no funcionan igual.
Este capitulo documenta los cambios que mas afectan el trabajo cotidiano en ciencia de datos.
La idea central es esta: pandas 3.0 empuja a usar un estilo de codigo mas explicito, mas predecible y menos dependiente de comportamientos historicos ambiguos. Varios cambios pueden sentirse incomodos al principio, pero casi todos apuntan a lo mismo:
- menos magia implicita
- menos APIs redundantes
- menos ambiguedad entre vista y copia
- mejor integracion con tipos modernos como Arrow y
pd.NA
Copy-on-Write es ahora obligatorio
El cambio mas importante. En pandas 2.x, Copy-on-Write (CoW) era opt-in:
# pandas 2.x: habia que activarlo manualmente
pd.options.mode.copy_on_write = True
En pandas 3.0, CoW es el comportamiento default e inamovible. Ya no existe la opcion de desactivarlo.
Que significa esto en la practica
El problema que CoW resuelve: en pandas 1.x/2.x (sin CoW), modificar un subconjunto de un DataFrame a veces modificaba el original y a veces no, dependiendo de si pandas habia creado una vista o una copia interna. El resultado era impredecible y generaba el infame SettingWithCopyWarning.
# pandas < 3.0: comportamiento AMBIGUO
subset = df[df["edad"] > 30]
subset["score"] = 100 # ¿Modifica df? Depende. SettingWithCopyWarning.
# pandas 3.0: comportamiento PREDECIBLE
# subset es siempre una copia logica. Modificarla nunca afecta df.
subset = df[df["edad"] > 30]
subset["score"] = 100 # Modifica subset, df no cambia. Sin warning.
# Para modificar df, usa .loc explicitamente:
df.loc[df["edad"] > 30, "score"] = 100 # Esto si modifica df.
Si tu codigo dependia del comportamiento de modificar vistas, necesita ser revisado.
Lo importante no es solo “evitar warnings”. CoW cambia la forma de razonar sobre mutacion:
- seleccionar produce un objeto separado desde el punto de vista logico
- asignar sobre ese objeto ya no tiene efectos colaterales sorpresa
- si quieres modificar el original, debes decirlo explicitamente con
.loc,.iloco una reasignacion clara
Este cambio hace que los pipelines sean mas faciles de leer y de depurar, porque la mutacion deja de depender de detalles internos del motor.
Que patrones conviene migrar
df[cond]["col"] = valor-> usardf.loc[cond, "col"] = valor- modificar un subset esperando efectos sobre
df-> reasignar o usar.loc - confiar en
inplace=Truepara “ahorrar memoria” -> preferir reasignacion explicita
La intuicion correcta en pandas 3.0 es: seleccionar no equivale a abrir una ventana mutable al objeto original.
Strings con Arrow por default
pandas 3.0 cambia el tipo por default de columnas de texto. En pandas 2.x, leer un CSV con texto daba columnas de tipo object (arrays de punteros a objetos Python — lento y pesado). En pandas 3.0, el default es pd.StringDtype() respaldado por Apache Arrow.
# pandas 2.x
df = pd.read_csv("datos.csv")
df["nombre"].dtype # object
# pandas 3.0
df = pd.read_csv("datos.csv")
df["nombre"].dtype # string[python] o string[pyarrow]
Ventajas: mas rapido, menos memoria, mejor soporte para pd.NA. El comportamiento con valores faltantes cambia: antes una columna object podia mezclar strings y NaN; ahora usa pd.NA consistentemente.
Si necesitas el comportamiento legacy:
df = pd.read_csv("datos.csv", dtype_backend="numpy_nullable")
Este cambio importa porque object era una especie de “cajon de sastre”: ahi cabian strings, enteros Python, listas, NaN y casi cualquier cosa. Eso daba flexibilidad, pero tambien hacia mas dificil optimizar y razonar sobre tipos.
Con strings tipados, pandas puede:
- representar faltantes de forma mas consistente
- delegar operaciones a Arrow cuando existe soporte
- reducir memoria en muchas cargas de datos textuales
La consecuencia practica es que conviene revisar codigo que asumia object en columnas de texto o que mezclaba tipos arbitrariamente en la misma columna.
applymap eliminado — usar map
DataFrame.applymap() fue renombrado a DataFrame.map() en pandas 2.1 (dejando applymap como alias deprecado). En pandas 3.0 el alias fue eliminado. Codigo que use applymap lanzara AttributeError.
# pandas < 3.0 (funciona pero lanza DeprecationWarning en 2.1+)
df.applymap(lambda x: round(x, 2))
# pandas 3.0 (correcto)
df.map(lambda x: round(x, 2))
Este cambio conecta directamente con lo que viste en el notebook de apply/map/transform de este modulo.
La motivacion no es solo “cambiar el nombre”. map unifica mejor la idea semantica de una transformacion elemento a elemento:
Series.map()-> sobre una SerieDataFrame.map()-> sobre cada celda del DataFrame
Eso hace el API mas coherente y reduce una palabra historica (applymap) que ya no aportaba demasiado.
groupby con observed=True por default
Cambio que afecta a cualquiera que use groupby con columnas categoricas. En pandas 2.x, el default era observed=False, que incluia en el resultado todas las combinaciones de categorias aunque no hubiera datos para esa combinacion. En pandas 3.0 el default es observed=True.
cat = pd.Categorical(["a", "a", "b"], categories=["a", "b", "c"])
df = pd.DataFrame({"grupo": cat, "valor": [1, 2, 3]})
# pandas 2.x (observed=False por default)
df.groupby("grupo")["valor"].sum()
# grupo
# a 3
# b 3
# c 0 ← categoria "c" sin datos aparece con 0
# pandas 3.0 (observed=True por default)
df.groupby("grupo")["valor"].sum()
# grupo
# a 3
# b 3 ← "c" no aparece porque no hay datos
Si tu pipeline depende de que aparezcan todas las categorias (por ejemplo, para reportes, tablas comparativas o dashboards con categorias fijas), especifica observed=False explicitamente.
Este cambio refleja una tension comun entre dos objetivos:
- analisis exploratorio: normalmente quieres ver solo lo observado
- reporteo estructurado: a veces quieres ver tambien categorias vacias
pandas 3.0 escoge como default el caso mas comun en analisis practico, pero deja abierta la opcion de recuperar el comportamiento anterior cuando sea necesario.
APIs eliminadas
pandas 3.0 elimina varios metodos y parametros que llevaban versiones con DeprecationWarning. Los mas comunes:
| Eliminado en 3.0 | Reemplazo |
|---|---|
DataFrame.applymap() |
DataFrame.map() |
DataFrame.swapaxes() |
Transponer manualmente |
Series.swapaxes() |
Igual |
DataFrame.append() |
pd.concat([df1, df2]) |
Parametro inplace= en muchos metodos |
Reasignar: df = df.metodo() |
DataFrame.append() era el mas usado. Fue eliminado desde pandas 2.0 pero aun aparece en codigo legacy y tutoriales viejos. La alternativa correcta es pd.concat().
La leccion de fondo aqui es que pandas esta intentando reducir caminos redundantes para hacer lo mismo. Menos aliases implica menos carga cognitiva y menos tutoriales contradictorios.
Sobre inplace=, la recomendacion moderna es no pensar en pandas como si fuera una API “mutable por defecto”. En muchos casos inplace no daba las ventajas de performance que los usuarios suponian y hacia mas dificil encadenar operaciones o razonar sobre estados intermedios.
Mejoras de performance
pandas 3.0 aprovecha Arrow mas agresivamente internamente:
- Operaciones sobre strings hasta 3-5x mas rapidas con Arrow backend
- Menor uso de memoria para columnas de texto
- Mejoras en
read_csv,read_parquet, y operaciones degroupby - Copy-on-Write elimina copias innecesarias en pipelines encadenados
Conviene leer estas mejoras con cautela: no significan que cualquier notebook sera automaticamente mucho mas rapido. El beneficio depende del tipo de datos y del patron de uso. Donde mas suele notarse es en:
- tablas grandes con muchas columnas de texto
- cargas y escrituras frecuentes
- pipelines con slicing y reasignaciones
- operaciones que ya aprovechan Arrow internamente
La mejora importante no es solo velocidad bruta, sino tener un modelo mas consistente entre tipos, memoria y mutacion.
Checklist mental de migracion
Si vas a correr codigo viejo en pandas 3.0, revisa primero esto:
- Busca
applymapy cambialo pormap. - Busca chained indexing del tipo
df[cond]["col"] = .... - Revisa supuestos sobre
objecten columnas de texto. - Verifica
groupbysobre categoricas si esperabas categorias no observadas. - Reemplaza APIs viejas como
.append()porpd.concat().
No todos los notebooks se romperan, pero estos cinco puntos concentran una gran parte de los cambios que si alteran comportamiento o estilo recomendado.
El notebook
El notebook recorre estos cambios con codigo ejecutable: comparaciones de comportamiento, casos donde codigo de pandas 2.x falla en 3.0, y formas concretas de migrar a un estilo mas moderno y mas estable.
Toma cualquier notebook de pandas que hayas escrito (de los modulos 12, 13 o 14). Busca: (1) uso de applymap, (2) patrones de chained indexing como df[cond]["col"] = val, (3) uso de .append(), (4) groupby sobre columnas categoricas. Documenta que cambiaria al correr ese codigo en pandas 3.0.