Python: Numpy
Теория: Уход от циклов и векторизованные вычисления
Библиотека Numpy помогает придерживаться двух принципов:
- Упрощение разработки и поддержки готового кода
- Оптимизация вычислений
Для этого разработчики библиотеки использовали следующие подходы и механизмы:
- Одинаковый интерфейс методов для выполнения операций над массивами разной размерности и типа
- Укладывание и векторизация для замены циклов
В этом уроке мы рассмотрим примеры векторизованных функций и методов — причем и встроенных в Numpy, и реализованных самостоятельно.
Арифметические операции
Начнем с самой простой арифметической операции — сложения:
Интерфейс функции np.sum() позволяет применять ее к массивам разной размерности без изменения синтаксиса. Реализация без циклов делается в одну строчку кода как для векторов, так и для матриц.
В случае одномерной структуры (вектора) сложение элементов можно производить только по одному направлению. В случае матрицы все немного по-другому — их элементы можно складывать по двум осям:
- По строкам
- По столбцам
Для суммирования по каждой из осей достаточно параметризовать функцию np.sum() таким образом:
Интерфейс многих функций библиотеки Numpy сделан по аналогии с np.sum(). Например, чтобы найти средние значения в матрице по строкам и столбцам, можно воспользоваться функцией np.mean() с теми же параметрами:
Методы массивов также реализованы с учетом возможности действия по разным осевым направлением. Синтаксических отличий от функций нет. Рассмотрим на примере метода для нахождения минимального значения.
Векторизация не только присутствует в библиотеке Numpy. В более широком смысле это один из подходов к параллельным вычислениям, которые реализуются на уровне процессоров и видеокарт.
При этом важно понимать, что векторизация в Numpy не всегда помогает оптимизировать скорость выполнения функций. Иногда этого просто нельзя достичь.
Кроме ускорения, есть еще одна цель. Это унификация интерфейса написанных функций — она повышает читаемость кода. Это важно в разработке любых программ, ведь небольшое количество строк и единообразие кода снижают вероятность возникновения ошибок и упрощают поддержку.
Создание векторизованных функций
Чтобы создать собственную векторизованную функцию, можно использовать функцию np.vectorize().
Рассмотрим ее применение на примере функции fill_over_bound(). Она находит значения, выходящие за пределы некоторой границы, а затем меняет их на значение этой границы:
Векторизованный вариант исходной функции можно применять к массивам разной размерности:
При этом использование np.vectorize() необходимо. Попытка использовать не векторизованный вариант функции приведет к ошибке:
Представим, что нам нужно подготовить данные о продажах сети магазинов за неделю.
Предположим, что для оценки качества работы магазинов мы решили рассматривать только типичные значения продаж. Типичным будем называть только те значения, которые не отклоняются от среднего показателя на величину более одного стандартного отклонения.
Перед аналитиком стоит такая задача — преобразовать все значения к типичному диапазону, заменив выбивающиеся значения на значения ближайшей к нему границы. Так выглядит список продаж:
А типичный диапазон к нему будет выглядеть вот так:
Теперь посмотрим на векторизованную функцию, которая заменяет нетипичные значения на ближайшую границу диапазона:
Чтобы получить типичный массив продаж, остается только применить эту функцию к исходным данным:
Обратите внимание, что в примере выше функция не поменяла тип исходного массива. В итоге мы получили целочисленный массив типичных продаж.
Выводы
В этом уроке вы узнали больше о векторизации. Этот подход позволяет сократить время выполнения функций и упростить интерфейс для их использования. Теперь у вас есть необходимые знания, которые помогут создавать свои векторизированные функции и применять встроенные в библиотеку Numpy.



