Что такое NumPy?#
NumPy — это фундаментальный пакет для научных вычислений в Python. Это библиотека Python, которая предоставляет многомерный объект массива, различные производные объекты (такие как маскированные массивы и матрицы) и набор подпрограмм для быстрых операций с массивами, включая математические, логические, манипуляции с формой, сортировку, выбор, ввод-вывод, дискретные преобразования Фурье, базовую линейную алгебру, базовые статистические операции, случайное моделирование и многое другое.
В основе пакета NumPy находится ndarray объект. Это
инкапсулирует n-мерные массивы однородных типов данных, с
многими операциями, выполняемыми в скомпилированном коде для производительности.
Есть несколько важных различий между массивами NumPy и
стандартными последовательностями Python:
Массивы NumPy имеют фиксированный размер при создании, в отличие от списков Python (которые могут динамически расти). Изменение размера
ndarrayсоздаст новый массив и удалит исходный.Элементы в массиве NumPy должны быть одного типа данных и, следовательно, одного размера в памяти. Исключение: можно иметь массивы объектов (Python, включая NumPy), что позволяет создавать массивы элементов разного размера.
Массивы NumPy облегчают выполнение сложных математических и других операций над большими объемами данных. Обычно такие операции выполняются более эффективно и с меньшим количеством кода, чем это возможно с использованием встроенных последовательностей Python.
Растущее множество научных и математических пакетов на основе Python используют массивы NumPy; хотя они обычно поддерживают ввод в виде последовательностей Python, они преобразуют такой ввод в массивы NumPy перед обработкой и часто выводят массивы NumPy. Другими словами, для эффективного использования большей части (возможно, даже большинства) современного научного/математического программного обеспечения на основе Python недостаточно просто знать, как использовать встроенные типы последовательностей Python — также необходимо знать, как использовать массивы NumPy.
Вопросы размера последовательности и скорости особенно важны в научных вычислениях. В качестве простого примера рассмотрим случай умножения каждого элемента в 1-D последовательности на соответствующий элемент в другой последовательности той же длины. Если данные хранятся в двух списках Python, a и b, мы могли бы перебирать каждый элемент:
c = []
for i in range(len(a)):
c.append(a[i]*b[i])
Это дает правильный ответ, но если a и b каждый содержит
миллионы чисел, мы заплатим цену за неэффективность
циклов в Python. Мы могли бы выполнить ту же задачу гораздо
быстрее на C, написав (для ясности мы опускаем объявления переменных
и инициализации, выделение памяти и т.д.)
for (i = 0; i < rows; i++) {
c[i] = a[i]*b[i];
}
Это сохраняет все накладные расходы, связанные с интерпретацией кода Python и манипулированием объектами Python, но за счёт преимуществ, полученных от программирования на Python. Кроме того, требуемая работа по кодированию увеличивается с размерностью наших данных. В случае 2-D массива, например, код C (сокращённый, как и раньше) расширяется до
for (i = 0; i < rows; i++) {
for (j = 0; j < columns; j++) {
c[i][j] = a[i][j]*b[i][j];
}
}
NumPy дает нам лучшее из обоих миров: поэлементные операции являются «режимом по умолчанию», когда ndarray участвует, но поэлементная операция быстро выполняется предварительно скомпилированным C-кодом. В NumPy
c = a * b
делает то же, что и предыдущие примеры, со скоростью, близкой к C, но с простотой кода, которую мы ожидаем от чего-то основанного на Python. Действительно, идиома NumPy даже проще! Этот последний пример иллюстрирует две особенности NumPy, которые лежат в основе его мощи: векторизация и трансляция.
Почему NumPy быстрый?#
Векторизация описывает отсутствие явных циклов, индексации и т.д. в коде — эти операции, конечно, происходят, но «за кулисами» в оптимизированном, предварительно скомпилированном C-коде. Векторизованный код имеет много преимуществ, среди которых:
векторизованный код более лаконичен и легче читается
меньше строк кода обычно означает меньше ошибок
код больше напоминает стандартную математическую нотацию (что обычно упрощает правильное кодирование математических конструкций)
векторизация приводит к более «питоническому» коду. Без векторизации наш код был бы засорен неэффективными и трудными для чтения
forциклы.
Трансляция — это термин, используемый для описания неявного поэлементного поведения операций; как правило, в NumPy все операции, не только арифметические, но и логические, побитовые, функциональные и т.д., ведут себя таким неявным поэлементным образом, т.е. они транслируются. Более того, в примере выше, a и b могут быть многомерными массивами одинаковой формы, или скаляром и массивом, или даже двумя массивами с разными формами, при условии, что меньший массив "расширяем" до формы большего таким образом, чтобы результирующее вещание было однозначным. Подробные "правила" вещания см. в
Трансляция (Broadcasting).
Кто ещё использует NumPy?#
NumPy полностью поддерживает объектно-ориентированный подход, начиная, снова,
с ndarray. Например, ndarray является классом, обладающим
множеством методов и атрибутов. Многие из его методов дублируются
функциями в самом внешнем пространстве имён NumPy, позволяя программисту
кодировать в любой парадигме, которую он предпочитает. Эта гибкость позволила
диалекту массивов NumPy и NumPy ndarray класс, чтобы стать де-факто язык многомерного обмена данными, используемый в Python.