В каких ситуациях лучше использовать numpy, а в каких стандартные структуры данных Python
Что проверяют
Понимаете ли вы, откуда у NumPy берётся скорость и чем за неё приходится платить. Следом обычно просят объяснить, почему цикл по массиву NumPy бывает медленнее цикла по списку, или найти узкое место в чужом коде.
Как отвечать
Объясните устройство. ndarray хранит элементы одного dtype в непрерывном блоке памяти, а операции над ним выполняются в скомпилированном коде без интерпретатора на каждом элементе. Список Python хранит указатели на отдельные объекты, и каждое число тянет за собой заголовок объекта. Отсюда два выигрыша: память (float64 занимает 8 байт на элемент против указателя плюс объекта в списке) и скорость векторных операций, на больших массивах разница измеряется порядками.
NumPy выбирают, когда:
- данные числовые и однородные: признаки, матрицы, сигналы, изображения;
- операция векторизуется: арифметика над массивами, broadcasting, агрегаты вроде
sumиmeanпо осям, маски вместоifв цикле; - нужна линейная алгебра:
@,np.linalg, которые опираются на BLAS и LAPACK; - данные дальше идут в библиотеки: pandas, scikit-learn, PyTorch (
torch.from_numpyне копирует память).
Стандартные структуры лучше, когда:
- данных мало: создание массива и вызов функции NumPy на паре элементов дороже обычного Python;
- коллекция растёт по одному элементу:
list.appendдешёвый, аnp.appendкаждый раз копирует массив целиком; - нужен поиск по ключу или проверка вхождения:
dictиsetдают в среднем O(1); - данные разнородные или вложенные: JSON, записи разной длины, строки. Для таблиц со смешанными типами берут pandas или Polars.
Где спотыкаются
Собирают массив через np.append в цикле. Это квадратичная сложность, правильно копить в список и один раз вызвать np.array.
Считают np.vectorize векторизацией. Документация прямо говорит, что это удобная обёртка над циклом, скорости она не даёт.
Не знают про тихие ловушки. Целочисленный массив переполняется без ошибки, в отличие от числа Python. А срез возвращает представление, и правка среза меняет исходный массив.