pandas 3.1: read_csv в 2-3 раза быстрее, новый select() и прощание с inplace

22 октября 2026 года вышла pandas 3.1.0, первый выпуск с новыми функциями после pandas 3.0. Разработчики советуют обновиться всем, а главными в списке изменений называют два пункта: read_csv() стал заметно быстрее и научился читать большие файлы в несколько потоков, а у DataFrame появился метод select().

Для тех, у кого pandas крутится в скриптах выгрузки, отчётах и ETL на сервере, выпуск полезен прежде всего скоростью. Но есть и изменения, из-за которых старый код может начать вести себя иначе или сыпать предупреждениями, так что обновлять лучше с прогоном тестов.

DataFrame.select() и filter() с маской

Новый метод select() возвращает таблицу только с нужными столбцами. Кроме имён существующих колонок ему можно передать выражение pd.col() или функцию, и тогда он посчитает новый столбец прямо на ходу. Как и в assign(), следующий аргумент может опираться на столбец, посчитанный в этом же вызове.

df.select("first_name", pd.col("age"), age_months=pd.col("age") * 12)
df.filter(pd.col("a") > 1)      # строки, где a больше 1
df.filter(cond=df["a"] > 1)     # то же самое через cond

Метод filter() теперь принимает булеву маску через новый аргумент cond, а также функцию или выражение pd.col() позиционно. Это удобно в цепочках методов. Пропуски в маске по умолчанию считаются False. Если передать позиционно список из True и False, pandas выдаст UserWarning: такой список почти наверняка задумывался как маска. Выбор колонок через items, like и regex в будущих версиях объявят устаревшим.

read_csv() в 2-3 раза быстрее и в несколько потоков

Чтение CSV штатным движком engine="c" ускорили на всех этапах: разбор строк, определение типов, преобразование целых, дробных чисел, строк и дат. По данным разработчиков, однопоточное чтение обычно в два-три раза быстрее, чем в 3.0.

Сверху добавили параллельное чтение. Оно включено по умолчанию и срабатывает, если файл локальный, не сжатый, весит не меньше 5 МБ и в вызове нет опций, которым нужен разбор всего файла целиком. Результат совпадает с обычным чтением. Число потоков задаёт новая настройка mode.max_threads: по умолчанию это число доступных процессу ядер, но не больше шести.

На сервере это стоит учитывать. Если pandas работает внутри приложения, которое уже само раскидывает задачи по процессам, например в воркерах Celery или под gunicorn, лишние потоки на каждый read_csv() будут только мешать. Тогда параллельное чтение лучше выключить:

import pandas as pd

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

# или для всего процесса
pd.set_option("mode.max_threads", 1)

Почтовые индексы и номера счетов в dtype=”category”

Тихое изменение, которое легко пропустить. Раньше read_csv() с dtype="category" на движках c и python делал категории строками даже для числовых колонок. Теперь все движки определяют числовые и логические категории. Так же ведут себя read_table(), read_fwf() и read_excel().

Разработчики прямо предупреждают про колонки-идентификаторы: номера счетов или почтовые индексы с ведущими нулями станут целыми числами, и нули пропадут. Чтобы оставить строки, колонку надо читать как str и уже потом переводить в категории:

df = pd.read_csv("clients.csv", dtype={"zip": "str"})
df["zip"] = df["zip"].astype("category")

inplace=True объявлен устаревшим

По PDEP-8 аргумент inplace объявлен устаревшим у методов, которые на самом деле никогда не меняли данные на месте: drop(), rename(), rename_axis(), reset_index(), set_index(), dropna(), drop_duplicates(), sort_values(), sort_index(), eval() и query(). Под капотом там всё равно создавался новый объект и присваивался обратно, а с Copy-on-Write из pandas 3.0 лишней копии и так нет. У fillna() и replace(), которые действительно умеют менять значения на месте, аргумент остаётся.

# было
df.reset_index(inplace=True)

# стало
df = df.reset_index()

Устарело и .values для колонок с часовым поясом, периодами и интервалами: сейчас оно теряет данные, например часовой пояс, а в pandas 4.0 будет возвращать ExtensionArray. Вместо него разработчики советуют .array или .to_numpy().

NumPy 2 и другие зависимости

pandas 3.1 больше не работает с NumPy 1.x: минимальная версия теперь 2.0.2, а python-dateutil нужен не ниже 2.9.0. Подняли и минимальные версии необязательных библиотек, например pyarrow 16.0.0, SQLAlchemy 2.0.42 и matplotlib 3.10.5. Для сборки из исходников нужен компилятор с C++20.

Ещё одна деталь для серверов: pandas перестал использовать numexpr 2.14.1 для ускорения арифметики. Отдельные сборки этой версии иногда молча возвращали неверный результат, например колонку из нулей. Теперь pandas в этом случае откатывается на NumPy и один раз предупреждает, а eval() и query() с явным engine="numexpr" падают с ImportError. Чтобы вернуть ускорение, нужен numexpr 2.14.2 или новее.

python -m pip install --upgrade pandas
python -c "import pandas, numpy; print(pandas.__version__, numpy.__version__)"

Итог: pandas 3.1 стоит ставить ради read_csv(), особенно если вы гоняете большие выгрузки. Перед обновлением проверьте, что в окружении NumPy 2, нет numexpr 2.14.1, а колонки с ведущими нулями не читаются через dtype="category". Предупреждения про inplace код не ломают, но заменить такие вызовы на присваивание лучше сразу.

Источники и ссылки


Комментарии загружаются…