2025, Dec 09 21:01
Polars read_excel и Calamine: ошибка с tuple в columns и решения
Разбираем TypeError в polars.read_excel с движком Calamine: почему tuple в columns не работает, как выбрать столбцы через list, диапазоны или callable
Polars read_excel, Calamine и не такой уж «последовательный» параметр columns
Порой дьявол кроется в деталях движка. На первый взгляд корректное использование аргумента columns в polars.read_excel при работе с движком Calamine приводит к TypeError, хотя в документации говорится, что должна подойти общая «последовательность». Ниже — что именно происходит, почему так выходит и как загрузить только нужные столбцы без лишних манёвров.
Минимальный воспроизводимый пример
Вызов ниже пытается прочитать конкретные столбцы по индексам, переданным кортежем. По документации это выглядит допустимо, но на деле выбрасывается исключение.
import polars as pl
sheet_df = pl.read_excel(
"/Volumes/Spare/foo.xlsx",
engine="calamine",
sheet_name="natsav",
read_options={"header_row": 2},
columns=(1, 2, 4, 5, 6, 7),
)
print(sheet_df.head())
Параметр задокументирован так:
Столбцы, которые нужно прочитать с листа; если не указано, читаются все. Можно передать в виде последовательности имён столбцов или индексов.
Однако вызов завершается ошибкой:
_fastexcel.InvalidParametersError: invalid parameters: `use_columns` callable could not be called (TypeError: 'tuple' object is not callable)
Что на самом деле происходит
С engine="calamine" (это и значение по умолчанию) вызов уходит в модуль fastexcel. В документации это отмечено явно:
этот движок может читать все основные типы Excel Workbook (.xlsx, .xlsb, .xls) и существенно быстрее других вариантов, используя модуль fastexcel для связки с написанным на Rust парсером Calamine.
Имя ошибки тоже на это указывает. Аргумент columns из Polars пробрасывается как fastexcel: use_columns, который определён как:
use_columns: Union[list[str], list[int], str, Callable[[ColumnInfoNoDtype], bool], NoneType] = None,
И описан следующим образом:
Задаёт используемые столбцы. Может быть None, чтобы выбрать все столбцы; список строк и чисел — имена и/или индексы столбцов (начиная с 0); строка — список букв Excel и диапазонов столбцов, разделённых запятой (например, “A:E” или “A,C,E:F”); или вызываемый объект — функция, принимающая столбец и возвращающая булево значение.
Иными словами, в связке Calamine/fastexcel не принимается абстрактная «последовательность», и кортеж не считается валидным значением для use_columns. Если движок трактует вход как вызываемый объект и пытается его вызвать, то кортеж закономерно приводит к TypeError: 'tuple' object is not callable. Это также объясняет, почему работает callable и почему параметр, который вы видите, имеет тип builtins.ColumnInfoNoDtype — он относится к fastexcel, а не к самому Polars.
Два корректных способа выбрать столбцы
Если вам ближе предикатный подход, передайте функцию, возвращающую bool для объекта столбца. Этот вариант выполняется без исключений и показывает входящий тип.
import polars as pl
def pick_any(col_obj):
print(type(col_obj))
return True
excel_df = pl.read_excel(
"/Volumes/Spare/foo.xlsx",
engine="calamine",
sheet_name="natsav",
read_options={"header_row": 2},
columns=pick_any,
)
print(excel_df.head())
Если нужен фиксированный поднабор по индексам, передайте список, а не кортеж. Формат списка явно поддерживается fastexcel.
import polars as pl
subset_df = pl.read_excel(
"/Volumes/Spare/foo.xlsx",
engine="calamine",
sheet_name="natsav",
read_options={"header_row": 2},
columns=[1, 2, 4, 5, 6, 7],
)
print(subset_df.head())
Это устраняет TypeError, поскольку use_columns принимает list[int] и list[str], но не tuple.
Почему эта разница важна
Документация верхнего уровня может сбивать с толку, когда поведение бэкенда расходится с ней. В данном случае Calamine идёт через fastexcel, который сужает допустимые формы спецификации столбцов до списков, строк или вызываемого объекта. На это уже заведён баг-репорт, и ситуация подчёркивает более широкий момент: то, что на одном уровне выглядит как «любой тип последовательности», на другом превращается в более жёсткий контракт. Понимание ожиданий конкретного движка экономит время и избавляет от загадочных ошибок.
Практические выводы
Используя polars.read_excel с engine="calamine", передавайте выбор столбцов в формате, понятном fastexcel. Это может быть список индексов или имён, строка с Excel-диапазонами, либо функция, возвращающая булево значение для каждого ColumnInfoNoDtype. Если, опираясь на описание «последовательности», вы сначала выбрали кортеж — просто переключитесь на список для предсказуемого поведения. А если требуется логика фильтрации или интроспекция, вариант с callable доступен и работает, как показано выше.