Векторизованные операции: без циклов

Векторизованные операции: без циклов NumPy

Привет! Одно из главных преимуществ NumPy — это векторизованные операции. Вместо того чтобы писать циклы для обработки каждого элемента, вы можете применять операции ко всему массиву целиком. Это не только короче, но и значительно быстрее.

В этой статье мы разберём:

  • Что такое векторизованные операции
  • Арифметические операции с массивами
  • Универсальные функции (ufunc)
  • Broadcasting
  • Сравнение производительности

Основная часть

Что такое векторизованные операции

Векторизованные операции — это операции, которые применяются ко всем элементам массива одновременно, без явных циклов.

import numpy as np

# Python-цикл (медленно)
arr = np.array([1, 2, 3, 4, 5])
result = []
for x in arr:
    result.append(x * 2)
print(result)   # [2, 4, 6, 8, 10]

# Векторизованная операция (быстро)
result = arr * 2
print(result)   # [2 4 6 8 10]

Совет: Векторизованные операции не требуют циклов и работают значительно быстрее.

Арифметические операции с массивами

arr = np.array([1, 2, 3, 4, 5])

# Сложение
print(arr + 10)   # [11 12 13 14 15]

# Вычитание
print(arr - 1)    # [0 1 2 3 4]

# Умножение
print(arr * 3)    # [3 6 9 12 15]

# Деление
print(arr / 2)    # [0.5 1.  1.5 2.  2.5]

# Степень
print(arr ** 2)   # [1 4 9 16 25]

Совет: Все арифметические операции применяются поэлементно.

Операции между массивами

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

# Сложение массивов
print(a + b)   # [5 7 9]

# Вычитание
print(a - b)   # [-3 -3 -3]

# Умножение (поэлементное)
print(a * b)   # [4 10 18]

# Деление
print(a / b)   # [0.25 0.4  0.5 ]

Важно: Массивы должны быть одинаковой формы (или совместимы через broadcasting).

Универсальные функции (ufunc)

Ufunc — это функции, которые работают с массивами поэлементно.

arr = np.array([0, np.pi/2, np.pi])

# Тригонометрические функции
print(np.sin(arr))   # [0.0000000e+00 1.0000000e+00 1.2246468e-16]
print(np.cos(arr))   # [ 1.000000e+00  6.123234e-17 -1.000000e+00]

# Экспонента и логарифм
print(np.exp(arr))   # [ 1.          4.81047738 23.14069263]
print(np.log(np.exp(1)))   # 1.0

# Квадратный корень
print(np.sqrt(arr + 1))   # [1.         1.25331414 1.41421356]

# Округление
arr2 = np.array([1.2, 2.7, 3.5])
print(np.floor(arr2))   # [1. 2. 3.]
print(np.ceil(arr2))    # [2. 3. 4.]
print(np.round(arr2))   # [1. 3. 4.]

Совет: Ufunc работают быстрее, чем поэлементные операции в Python.

Статистические функции

arr = np.array([1, 2, 3, 4, 5])

# Сумма
print(np.sum(arr))     # 15

# Среднее
print(np.mean(arr))    # 3.0

# Медиана
print(np.median(arr))  # 3.0

# Минимум и максимум
print(np.min(arr))     # 1
print(np.max(arr))     # 5

# Стандартное отклонение
print(np.std(arr))     # 1.4142135623730951

# Сумма по оси
matrix = np.array([[1, 2], [3, 4]])
print(np.sum(matrix, axis=0))   # [4 6]  # по столбцам
print(np.sum(matrix, axis=1))   # [3 7]  # по строкам

Совет: Используй параметр axis для вычислений по строкам или столбцам.

Broadcasting

Broadcasting — это механизм, который позволяет выполнять операции с массивами разных форм.

# Скаляр + массив
arr = np.array([1, 2, 3])
print(arr + 10)   # [11 12 13]

# Вектор + матрица (строки)
matrix = np.array([[1, 2, 3], [4, 5, 6]])
vector = np.array([10, 20, 30])
print(matrix + vector)
# [[11 22 33]
#  [14 25 36]]

# Вектор-столбец + матрица
matrix = np.array([[1, 2, 3], [4, 5, 6]])
vector = np.array([[10], [20]])
print(matrix + vector)
# [[11 12 13]
#  [24 25 26]]

Совет: Broadcasting автоматически расширяет меньший массив до размера большего.

Сравнение производительности

import time

# Создаём большой массив
size = 10000000
arr = np.random.rand(size)

# Python-цикл
start = time.time()
result = []
for x in arr:
    result.append(x * 2)
print(f"Python цикл: {time.time() - start:.4f} сек")

# Векторизованная операция
start = time.time()
result = arr * 2
print(f"Векторизовано: {time.time() - start:.4f} сек")

Результат (примерный):

Python цикл: 0.85 сек
Векторизовано: 0.02 сек

Совет: Векторизованные операции в десятки раз быстрее!

Булевы операции

arr = np.array([1, 2, 3, 4, 5])

# Сравнение
print(arr > 3)    # [False False False  True  True]
print(arr == 3)   # [False False  True False False]

# Логические операции
mask = (arr > 2) & (arr < 5)
print(mask)       # [False False  True  True False]

# Сумма True
print(np.sum(arr > 3))   # 2 (количество элементов > 3)

# Все элементы > 0
print(np.all(arr > 0))   # True

# Любой элемент > 4
print(np.any(arr > 4))   # True

Полный пример

import numpy as np

# Создаём массив
arr = np.arange(1, 13).reshape(3, 4)
print("Исходный массив:\n", arr)

# Векторизованные операции
print("\nУмножение на 2:\n", arr * 2)
print("Квадрат:\n", arr ** 2)
print("Синус:\n", np.sin(arr))

# Статистика
print("\nСумма:", np.sum(arr))
print("Среднее:", np.mean(arr))
print("Максимум:", np.max(arr))

# Операции по осям
print("\nСумма по строкам:", np.sum(arr, axis=1))
print("Сумма по столбцам:", np.sum(arr, axis=0))

# Broadcasting
vector = np.array([10, 20, 30, 40])
print("\nПрибавление вектора:\n", arr + vector)

# Булевы операции
print("\nЭлементы > 5:", arr[arr > 5])
print("Количество элементов > 5:", np.sum(arr > 5))

Результат:

Исходный массив:
 [[ 1  2  3  4]
  [ 5  6  7  8]
  [ 9 10 11 12]]

Умножение на 2:
 [[ 2  4  6  8]
  [10 12 14 16]
  [18 20 22 24]]

Сумма: 78
Среднее: 6.5
Максимум: 12

Сумма по строкам: [10 26 42]
Сумма по столбцам: [15 18 21 24]

Прибавление вектора:
 [[11 22 33 44]
  [15 26 37 48]
  [19 30 41 52]]

Элементы > 5: [ 6  7  8  9 10 11 12]
Количество элементов > 5: 7

Задачи для закрепления

Задача 1. Умножь массив [1, 2, 3, 4, 5] на 3.

Задача 2. Вычисли сумму двух массивов [1, 2, 3] и [4, 5, 6].

Задача 3. Найди среднее значение массива [10, 20, 30, 40, 50].

Задача 4. Найди элементы массива, которые больше 5.

Задача 5. Вычисли сумму элементов по строкам матрицы 2×3.

Ответы:

Задача 1.

import numpy as np
arr = np.array([1, 2, 3, 4, 5])
result = arr * 3
print(result)   # [3 6 9 12 15]

Задача 2.

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = a + b
print(result)   # [5 7 9]

Задача 3.

arr = np.array([10, 20, 30, 40, 50])
mean = np.mean(arr)
print(mean)   # 30.0

Задача 4.

arr = np.array([1, 2, 3, 4, 5, 6, 7])
result = arr[arr > 5]
print(result)   # [6 7]

Задача 5.

matrix = np.array([[1, 2, 3], [4, 5, 6]])
row_sums = np.sum(matrix, axis=1)
print(row_sums)   # [6 15]

Нюансы и подводные камни

Broadcasting требует совместимых форм

Массивы должны быть совместимы по размерности. Если нет — ошибка.

Ufunc создают новые массивы

Ufunc не изменяют исходный массив, а возвращают новый.

Векторизованные операции используют больше памяти

При больших массивах результат занимает столько же памяти, сколько и исходный.

Частые ошибки и как их избежать

Ошибка 1: Неправильная размерность

Проверяй shape массивов перед операциями.

Ошибка 2: Ожидание изменения исходного массива

Ufunc возвращают новый массив. Используй arr += 1 для изменения на месте.

Ошибка 3: Сравнение с and/or

Используй & и | для булевых массивов.

Шпаргалка

ОперацияPythonNumPy
Умножение на скаляр[x*2 for x in arr]arr * 2
Сложение массивов[a+b for a,b in zip(x,y)]x + y
Суммаsum(arr)np.sum(arr)
Среднееnp.mean(arr)
Синус[math.sin(x) for x in arr]np.sin(arr)

Заключение

Сегодня мы:

  • Узнали, что такое векторизованные операции
  • Изучили арифметические операции и ufunc
  • Разобрали broadcasting
  • Сравнили производительность

КВИЗ

Что дальше?

Оцените статью
IMI-DS - PYTHON LERNEN
Содержание
Оглавление ×