Learn  /  What's new

What's new in pandas 3.1: DataFrame.select, filter with conditions, and a much faster read_csv

pandas 3.1 brings Polars-style select and filter, a CSV reader that ran nearly seven times faster in our test, and the start of the end for inplace. What changed and what to check before upgrading.

What's new8 min readUpdated 28 October 2026
PythonWhat's new
All guides and cheat sheets

Nine months after the big 3.0 release, pandas 3.1 arrived on 22 October 2026. It is a smaller release, but three changes affect almost everyone who uses pandas: a new DataFrame.select method, boolean conditions in filter, and a CSV reader that is much faster and now reads large files in parallel. It also starts the removal of the inplace argument. We installed it and tested every example on this page. If you are still on pandas 2, read our pandas 3 upgrade guide first.

6.8Times faster read_csvour 90 MB test, 3.0 to 3.1
6Maximum CSV threadsby default, set with mode.max_threads
3.6Times faster on one thread3.0 to 3.1, same file

DataFrame.select: choose and create columns in one step

Until now, picking columns used df[["a", "b"]] and creating columns used assign. The new select does both, and works with pd.col, the expression helper added in pandas 3.0. Later columns can use ones created earlier in the same call.

Python
import pandas as pd

df = pd.DataFrame({
    "first_name": ["John", "Alice", "Bob"],
    "last_name": ["Smith", "Cooper", "Marley"],
    "age": [61, 22, 35],
})

df.select(
    "first_name",
    age_months=pd.col("age") * 12,
    age_days=pd.col("age_months") * 30,
)
#   first_name  age_months  age_days
# 0       John         732     21960
# 1      Alice         264      7920
# 2        Bob         420     12600

filter with conditions

DataFrame.filter used to select columns or rows by label only. It now takes a cond argument: a boolean mask, a function, or a pd.col expression. Missing values in the mask count as False unless you say otherwise.

Python
df.filter(pd.col("age") > 30)              # expression
df.filter(cond=df["age"] > 30)             # boolean mask
df.filter(lambda d: d["age"] < 40)         # function

mask = pd.array([True, None, False], dtype="boolean")
df.filter(cond=mask)            # missing value treated as False: 1 row
df.filter(cond=mask, na=True)   # missing value kept: 2 rows

One change to watch: passing a plain list of True and False values to filter now gives a warning. Use items= to select labels, or cond= for a mask.

A much faster read_csv

The default C engine was rewritten for speed, and large, uncompressed local files of 5 MB or more are now read in parallel. The result is identical to a single-threaded read. We timed a 90 MB file with 2 million rows on a small 2-core cloud machine, best of five runs:

read_csv on a 90 MB file (seconds, lower is better)
pandas 3.0, C engine1.02 s
pandas 3.1, 1 thread0.28 s
pandas 3.1, PyArrow engine0.21 s
pandas 3.1, default (parallel)0.15 s
Polars 2.10.15 s

Our test: 2 million rows, 5 columns, 2-core cloud machine, best of five runs. Your numbers will vary with hardware and file shape.

Even on one thread, 3.1 was 3.6 times faster than 3.0, and with parallel reading it matched Polars on this file. The new mode.max_threads option limits the threads, which is useful on shared servers:

Python
with pd.option_context("mode.max_threads", 1):
    df = pd.read_csv("large.csv")

Goodbye, inplace

The inplace argument is deprecated on methods that never really worked in place, including drop, rename, reset_index, set_index, dropna, sort_values, query and eval. In our test, pandas warned even when inplace=False was passed, so remove the argument entirely. fillna and replace keep it.

Python
# Before
df.drop(columns="age", inplace=True)

# After
df = df.drop(columns="age")

Breaking changes to check

Numeric dates
to_datetime, to_timedelta, Timestamp and Timedelta now default to microseconds for numbers, so pd.to_timedelta([1, 2], unit="s") gives timedelta64[us]. Use .as_unit() if you need nanoseconds.
Read-only index values
Index.values and Index.array are now read-only for every dtype.
total_seconds
Timedelta.total_seconds() now includes nanoseconds, so tiny durations are no longer 0.0.
Category columns from CSV
read_csv(dtype="category") now infers numbers and booleans. Read as "str" first if you need text categories.
select_dtypes
Passing Python classes such as decimal.Decimal now raises; pass object instead.
HDF5 files
Tables written by pandas 3.1 cannot be read by pandas 3.0 or earlier.
numexpr
Version 2.14.1 is ignored; install 2.14.2 or newer to keep the speed-up.

Smaller additions worth knowing

  • pd.concat gains union_categories=True to combine categorical columns with different categories.
  • Periods format in f-strings: f"{period:%Y-%m}" gives 2026-10.
  • Timestamp.round, floor and ceil accept a Timedelta.
  • Time strings with a space before AM or PM, such as "3:25:00 PM", now parse.
  • Index.replace works like Series.replace.

How to upgrade

  1. 1

    Upgrade in a test environment

    pandas 3.1 needs Python 3.11 or newer and NumPy 2.0.2 or newer. Run pip install -U pandas, or uv add pandas, in a separate environment first.

  2. 2

    Turn warnings on

    Run your tests with deprecation warnings as errors, for example pytest -W error::DeprecationWarning, to find inplace calls and other deprecated code. pandas uses its own Pandas4Warning, a kind of DeprecationWarning.

  3. 3

    Check dates and categories

    Look for code that depends on nanosecond resolution or on text categories from read_csv.

  4. 4

    Remove inplace

    Replace each inplace=True with an assignment; it also makes method chains easier to read.

  5. 5

    Try select and filter

    New code reads more clearly with them, but old code keeps working.

Check your understanding

1/3What does the new DataFrame.select method do?

2/3How are missing values in a boolean mask treated by filter by default?

3/3What should you do with inplace=True on drop or rename?

More on Python data tools: the pandas 3 upgrade guide, what's new in Polars 2.1, pandas vs Polars in 2026 and the pandas cheat sheet.

Sources: pandas 3.1.0 release notes, "What's new in 3.1.0" (22 October 2026), and our own tests with pandas 3.0.6 and 3.1.0, Polars 2.1.0 and PyArrow on Python 3.13. Timings are from one machine and are indicative only.

Alessandro Ecclesie AgazziWritten by Alessandro Ecclesie Agazzi, analytics engineer in London. Updated 28 October 2026. About me

Found this useful?

New guides and "what's new" explainers on data and AI land here regularly. Follow along on LinkedIn, or share this one with a colleague.