Шпаргалка по NumPy: массивы, операции, линейная алгебра
NumPy (Numerical Python) — фундамент научного Python. N-мерные массивы (ndarray) в 10–100 раз быстрее списков за счёт векторизованных операций на C. Без NumPy не работают Pandas, Matplotlib, Scikit-learn.
Импорт и создание массивов
import numpy as np
# Из списка
a = np.array([1, 2, 3, 4, 5]) # 1D
b = np.array([[1, 2, 3], [4, 5, 6]]) # 2D
# Нули, единицы, константы
np.zeros((2, 3)) # матрица 2×3 из нулей
np.ones((3, 2)) # матрица 3×2 из единиц
np.full((2, 2), 7) # матрица 2×2 из семёрок
np.eye(3) # единичная матрица 3×3
np.empty((2, 2)) # без инициализации (быстро, но мусор)
np.zeros_like(a) # как a, но из нулей
np.ones_like(a) # как a, но из единиц
# Последовательности
np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
np.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1.0]
np.logspace(0, 2, 5) # [10^0, ..., 10^2] — логарифмическая шкала
# Случайные (новый API, рекомендуется)
rng = np.random.default_rng(seed=42)
rng.random((2, 3)) # равномерное [0, 1)
rng.normal(0, 1, (2, 3)) # нормальное μ=0, σ=1
rng.integers(0, 10, (3, 3)) # целые [0, 10)
rng.choice([1, 2, 3], size=4) # выборка
# Старый API (до сих пор встречается)
np.random.rand(2, 3) # равномерное [0, 1)
np.random.randn(2, 3) # нормальное
np.random.randint(0, 10, (3, 3)) # целые
# Специальные
np.diag([1, 2, 3]) # диагональная матрица
np.fromfunction(lambda i, j: i + j, (3, 3)) # по функции от индексов
Атрибуты и изменение формы
a.shape # кортеж размеров: (3, 4)
a.ndim # число измерений: 2
a.size # общее число элементов: 12
a.dtype # тип: float64, int32...
a.itemsize # байт на элемент: 8
# Изменение формы
a.reshape(3, -1) # -1 = авто-вычисление
a.flatten() # в 1D (всегда копия)
a.ravel() # в 1D (view, если возможно)
a.T # транспонирование
a.reshape(-1, 1) # вектор-столбец (n, 1)
a.reshape(1, -1) # вектор-строка (1, n)
a[:, np.newaxis] # добавить ось: (3,) → (3, 1)
np.expand_dims(a, axis=0) # добавить ось
np.squeeze(a) # убрать оси размера 1
Индексация и срезы
a = np.arange(12).reshape(3, 4)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# Базовые срезы (view — не копия!)
a[0] # [0 1 2 3] — первая строка
a[1, 2] # 6 — элемент (строка 1, столбец 2)
a[:, 0] # [0 4 8] — первый столбец
a[0:2, 1:3] # [[1 2] [5 6]] — подматрица
# Fancy indexing (всегда копия)
a[[0, 2]] # строки 0 и 2
a[:, [1, 3]] # столбцы 1 и 3
a[[0, 1, 2], [1, 2, 3]] # элементы (0,1), (1,2), (2,3) → [1, 6, 11]
# Булева индексация
mask = a > 5
a[mask] # [6 7 8 9 10 11] — все > 5
a[a > 5] = 0 # присвоение по маске
# Условная замена
np.where(a > 5, a, -1) # где > 5 — оставить, иначе -1
np.clip(a, 2, 8) # ограничить диапазоном [2, 8]
Арифметика и операции
# Поэлементные (векторизованные)
a + b, a - b, a * b, a / b # + - * / — поэлементно
a ** 2 # квадрат каждого элемента
np.sqrt(a), np.exp(a), np.log(a)
np.sin(a), np.cos(a), np.tan(a)
np.abs(a)
# ⚠️ * — это поэлементное умножение, НЕ матричное!
# Матричное умножение:
A @ B # оператор @ (Python 3.5+) — рекомендуется
np.dot(A, B) # классический способ
A.dot(B) # метод
# Сравнения — возвращают булевы массивы
a > 2 # [False, False, True, ...]
a == b
np.allclose(a, b) # похожи с учётом погрешности (лучше чем ==)
np.array_equal(a, b) # точное равенство
# Трансляция (broadcasting) скаляров
a + 5 # прибавить 5 ко всем элементам
a * 2 # умножить все на 2
Broadcasting (трансляция)
Три правила broadcasting:
- Если размерности разные — дополнить единицами слева
- Размеры совместимы, если равны или один из них равен 1
- Размер 1 растягивается до размера другой оси
# (3, 4) + (4,) → OK: (3, 4) + (1, 4) — растягивается по строкам
a = np.ones((3, 4))
b = np.array([1, 2, 3, 4])
a + b
# [[2 3 4 5]
# [2 3 4 5]
# [2 3 4 5]]
# (3, 1) + (4,) → (3, 4) — оба растягиваются
col = np.array([[1], [2], [3]]) # (3, 1)
row = np.array([10, 20, 30, 40]) # (4,)
col + row
# [[11 21 31 41]
# [12 22 32 42]
# [13 23 33 43]]
# Z-score нормализация через broadcasting
X_norm = (X - X.mean(axis=0, keepdims=True)) / X.std(axis=0, keepdims=True)
# Внешнее произведение через newaxis
outer = a[:, np.newaxis] * b[np.newaxis, :] # (n, m)
outer = a[:, np.newaxis] * b # то же (короче)
outer = np.outer(a, b) # ещё короче
# Сетки координат
x = np.linspace(-5, 5, 11)
y = np.linspace(-5, 5, 11)[:, np.newaxis] # (11, 1)
z = np.sin(np.sqrt(x**2 + y**2)) # (11, 11)
# ogrid — открытая сетка (экономит память)
x, y = np.ogrid[-5:5:11j, -5:5:11j]
# x.shape = (11, 1), y.shape = (1, 11)
Статистика и агрегация
a.sum() # сумма всех элементов
a.mean() # среднее
a.std() # стандартное отклонение
a.var() # дисперсия
a.min(), a.max() # минимум, максимум
a.argmin(), a.argmax() # индексы минимума/максимума
np.median(a) # медиана
np.percentile(a, 50) # медиана (50-й перцентиль)
np.percentile(a, [25, 50, 75]) # квартили
# По осям (axis)
a.sum(axis=0) # сумма по строкам → агрегация в столбцы
a.sum(axis=1) # сумма по столбцам → агрегация в строки
a.max(axis=0) # максимум каждого столбца
# keepdims — сохранить размерность
a.sum(axis=1, keepdims=True) # (3,) → (3, 1)
Линейная алгебра (np.linalg)
# Умножение матриц
A @ B # рекомендуется
# Транспонирование, след, определитель
A.T # транспонирование
np.trace(A) # след (сумма диагонали)
np.linalg.det(A) # определитель (только квадратные)
# Обратная матрица
np.linalg.inv(A) # A⁻¹ (если det ≠ 0)
np.linalg.pinv(A) # псевдообратная (Мура-Пенроуза)
# Решение системы Ax = b
x = np.linalg.solve(A, b) # для квадратной невырожденной
np.allclose(A @ x, b) # проверка: True
# МНК (метод наименьших квадратов) для переопределённых систем
x, residuals, rank, s = np.linalg.lstsq(A, b, rcond=None)
# Собственные значения и векторы
eigenvals, eigenvecs = np.linalg.eig(A)
# SVD — сингулярное разложение
U, s, Vt = np.linalg.svd(A)
# A = U @ np.diag(s) @ Vt
# QR-разложение
Q, R = np.linalg.qr(A) # A = Q @ R
# Ранг и норма
np.linalg.matrix_rank(A) # ранг матрицы
np.linalg.norm(v) # L2-норма (по умолчанию)
np.linalg.norm(v, ord=1) # L1-норма
np.linalg.norm(v, ord=np.inf) # L∞-норма
Объединение и разделение
# Объединение
np.concatenate([a, b], axis=0) # по строкам (вертикально)
np.concatenate([a, b], axis=1) # по столбцам (горизонтально)
np.vstack([a, b]) # shortcut: axis=0
np.hstack([a, b]) # shortcut: axis=1
np.column_stack([a, b]) # 1D → столбцы
# Разделение
np.split(a, 3, axis=0) # на 3 равные части по строкам
np.hsplit(a, 2) # горизонтально на 2
np.vsplit(a, 2) # вертикально на 2
Полезные функции
# Поиск и фильтрация
np.where(cond, x, y) # cond → x иначе y
np.unique(a) # уникальные значения
np.unique(a, return_counts=True) # + частоты
np.unique(a, return_inverse=True) # + индексы для восстановления
np.isnan(a) # булева маска NaN
np.isfinite(a) # конечные значения (не NaN, не inf)
np.isinf(a) # бесконечности
# NaN-безопасные агрегации
np.nanmean(a), np.nanstd(a), np.nansum(a), np.nanmedian(a)
# Сортировка
np.sort(a) # отсортированная копия
a.sort() # сортировка на месте
np.argsort(a) # индексы для сортировки
# Округление
np.round(a, 2) # до 2 знаков
np.ceil(a), np.floor(a) # вверх/вниз
np.rint(a) # до ближайшего целого
# Типы данных
a.astype(np.float32) # преобразование типа
np.array([1, 2], dtype=np.int64) # задать при создании
Продвинутые техники
einsum — тензорные операции в одной строке
Сокращение от Einstein Summation. Позволяет записать сложные тензорные операции (умножение матриц, свёртки, след, транспонирование) в одной строке без циклов. Быстрее, чем комбинация @ и transpose, и гораздо читаемее для многомерных массивов. Синтаксис: 'входные_индексы->выходные_индексы'.
# Синтаксис: np.einsum('индексы->выход', A, B)
# Умножение матриц
np.einsum('ij,jk->ik', A, B) # A @ B
# След матрицы
np.einsum('ii->', A) # np.trace(A)
# Поэлементное умножение
np.einsum('ij,ij->ij', A, B) # A * B
# Сумма всех элементов
np.einsum('ij->', A) # A.sum()
# Транспонирование
np.einsum('ij->ji', A) # A.T
# Внешнее произведение
np.einsum('i,j->ij', u, v) # np.outer(u, v)
# Batch matrix multiplication
np.einsum('bij,bjk->bik', A3d, B3d) # batch @
Sliding window view
Скользящее окно по массиву — взгляд на данные через «окошко» фиксированного размера, которое двигается с шагом 1. НЕ копирует данные — возвращает view. Используется для скользящих средних, выделения патчей из изображений, анализа временных рядов.
from numpy.lib.stride_tricks import sliding_window_view
# Скользящее среднее
x = np.arange(10)
w = sliding_window_view(x, window_shape=3)
w.mean(axis=1) # [1, 2, 3, 4, 5, 6, 7, 8]
# 2D окна (для изображений)
img = np.random.rand(100, 100)
patches = sliding_window_view(img, (3, 3)) # (98, 98, 3, 3)
C-order vs F-order
Порядок хранения элементов в памяти: C-order (строка за строкой, по умолчанию) — быстрый доступ по строкам. F-order (столбец за столбцом, Fortran-style) — быстрый доступ по столбцам. Для больших матриц разница в скорости может быть 2-5x.
a = np.ones((1000, 1000))
b = np.asfortranarray(a.copy()) # Fortran-order (column-major)
# Доступ по строкам: C-order быстрее
# Доступ по столбцам: F-order быстрее
Структурированные массивы
Массив, где каждый элемент — структура с именованными полями разных типов (как таблица БД или DataFrame без Pandas). Позволяет хранить разнородные данные в одном ndarray и обращаться к полям по имени.
dtype = np.dtype([('city', 'U20'), ('population', 'i4'), ('area', 'f8')])
cities = np.array([
('Москва', 12_000_000, 2561.5),
('Питер', 5_000_000, 1439.0),
], dtype=dtype)
cities['city'] # ['Москва' 'Питер']
cities['population'] # [12000000 5000000]
cities[cities['population'] > 5_000_000]
np.memmap — работа с файлами на диске как с массивами
Memory-mapped file: массив, который живёт на диске, а в память загружаются только используемые фрагменты. Позволяет работать с массивами больше оперативной памяти (100+ GB) без MemoryError. Чтение и запись как с обычным ndarray.
# Создание
mmap = np.memmap('data.dat', dtype='float32', mode='w+', shape=(1000, 1000))
mmap[:] = np.random.randn(1000, 1000)
mmap.flush()
# Чтение (только нужное в памяти)
mmap = np.memmap('data.dat', dtype='float32', mode='r')
chunk = mmap[0:100] # загружены только 100 строк
Векторизация vs циклы
Одна и та же операция над всеми элементами массива через NumPy выполняется на C-уровне в десятки раз быстрее, чем Python-цикл. Всегда заменяй for по элементам на векторизованные операции — это главный источник производительности NumPy.
# Медленно (Python loop) — O(n) накладных расходов
result = [x**2 + 2*x + 1 for x in range(1_000_000)]
# Быстро (NumPy) — вычисления на C
x = np.arange(1_000_000)
result = x**2 + 2*x + 1 # ~50x быстрее
Прикладные примеры
# Z-score нормализация
data = np.random.randn(100, 5)
normalized = (data - data.mean(axis=0)) / data.std(axis=0)
# Поиск ближайшего элемента
v = np.array([1, 4, 8, 12, 15])
target = 10
closest = v[np.argmin(np.abs(v - target))]
# N наибольших значений
v = np.array([1, 9, 3, 7, 2, 8])
top_n = v[np.argsort(v)[-3:]] # последние 3 после сортировки
# Расстояние между векторами
dist_l1 = np.linalg.norm(a - b, ord=1) # манхэттенское
dist_l2 = np.linalg.norm(a - b, ord=2) # евклидово
# Косинусное сходство
cos_sim = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# Матрица расстояний
# (n, d) → (n, n) — все попарные расстояния
diff = X[:, np.newaxis, :] - X[np.newaxis, :, :] # (n, n, d)
dist_matrix = np.linalg.norm(diff, axis=2) # (n, n)
# Ковариационная и корреляционная матрицы
cov_matrix = np.cov(X, rowvar=False) # столбцы — переменные
corr_matrix = np.corrcoef(X, rowvar=False)
# One-hot encoding
n_classes = 5
labels = np.array([0, 2, 1, 4])
one_hot = np.eye(n_classes)[labels] # (4, 5)
Частые ошибки
| Проблема | Причина | Решение |
|---|---|---|
(n,) != (n, 1) |
Одномерный массив не столбец | a.reshape(-1, 1) |
a * b вместо A @ B |
* — поэлементное, не матричное |
A @ B или np.dot(A, B) |
| Срез = view, не копия | b = a[:5] меняет a |
b = a[:5].copy() |
a == b с float |
Ошибки округления | np.allclose(a, b) |
and/or в маске |
Python-операторы не векторизованы | & / ` |
axis перепутан |
axis=0 = агрегация по строкам |
Визуализируй: axis=0 сжимает строки |
| NaN в данных | mean() возвращает NaN |
np.nanmean(a) |
| Broadcasting не работает | Размеры несовместимы | Дополни размерность через None/np.newaxis |
Ресурсы
- Сайт NumPy — документация, руководства, новости
- NumPy на GitHub — исходный код
Что дальше
- Шпаргалка по Python: от основ до профи
- Шпаргалка по Pandas: DataFrame, groupby, временные ряды
- Шпаргалка по Matplotlib: графики от простых до публикационных
- Шпаргалка по Seaborn: статистическая визуализация
- Шпаргалка по Scikit-learn: машинное обучение на Python
- Data Science инструменты: стек, roadmap, с чего начать
Связанные шпаргалки: Python, Pandas, Matplotlib, Seaborn, SQL для начинающих