🔢 NumPy

Полный справочник: массивы и dtype, создание, индексация и маски, reshape и оси, broadcasting, функции и редукции, сортировка и поиск, линейная алгебра, случайные числа, ввод-вывод, векторизация и типовые рецепты.

Шпаргалки · ИИ · #numpy #python #arrays #data-science #ml

Зачем

NumPy даёт n-мерный массив ndarray с однотипными элементами в непрерывной памяти и быстрые операции над ним целиком (на C). Это основа pandas, scikit-learn, SciPy, Matplotlib, PyTorch (тензоры похожи). Векторизованный код быстрее циклов Python в десятки и сотни раз.

pip install numpy
import numpy as np
np.__version__

Создание

np.array([1, 2, 3]); np.array([[1, 2], [3, 4]], dtype=np.float32)
np.zeros((2, 3)); np.ones((2, 3)); np.full((2, 2), 7); np.empty((2, 2)); np.eye(3); np.identity(3); np.diag([1, 2, 3])
np.arange(0, 10, 2); np.linspace(0, 1, 5); np.logspace(0, 3, 4); np.zeros_like(a); np.ones_like(a); np.full_like(a, 9)
np.meshgrid(x, y); np.tile([1, 2], 3); np.repeat([1, 2], 3); np.fromiter(gen, dtype=float); np.frombuffer(b, dtype=np.uint8)
rng = np.random.default_rng(42)                    # современный генератор, воспроизводимый seed
rng.random((2, 3)); rng.integers(0, 10, size=5); rng.normal(0, 1, 100); rng.choice(a, size=3, replace=False); rng.shuffle(a); rng.permutation(10)

Свойства и типы

a.shape; a.ndim; a.size; a.dtype; a.itemsize; a.nbytes; a.T; a.flags
a.astype(np.float32); a.view(np.uint8)             # astype копирует, view переинтерпретирует память
dtype Размер / смысл
int8/16/32/64, uint8… целые (по умолчанию int64)
float16/32/64 дробные (по умолчанию float64)
bool логический
complex64/128 комплексные
str_, object строки, произвольные объекты (медленно)
datetime64[ns], timedelta64 даты и время

Для ML и больших данных часто хватает float32. Переполнение целых молчаливо (np.uint8(250) + 10): следите за типами.

Индексация и срезы

a[0]; a[-1]; a[1:4]; a[::2]; a[::-1]              # одномерный: [start:stop:step]
b[0, 1]; b[:, 0]; b[1, :]; b[..., -1]; b[:2, 1:]   # многомерный
b[[0, 2]]; b[[0, 1], [1, 0]]                       # fancy-индексация (всегда копия)
a[a > 2]; a[(a > 1) & (a < 5)]; a[~mask]           # булевы маски (& | ~ вместо and or not, скобки обязательны)
a[a > 2] = 0; np.where(a > 2, a, 0); np.where(cond)  # условная замена / индексы
np.take(a, idx); np.put(a, idx, v); np.nonzero(a); np.argwhere(a > 2); np.clip(a, 0, 10)
a[:, np.newaxis]; a[None, :]                       # добавить ось

Срезы возвращают представление (view), изменение влияет на исходный массив. Fancy и булевы индексы возвращают копию. Явная копия: a.copy(). Проверка: np.shares_memory(a, b).

Форма и оси

a.reshape(3, -1); a.reshape(-1); a.flatten(); a.ravel(); a.T; np.transpose(a, (2, 0, 1)); np.swapaxes(a, 0, 1); np.moveaxis(a, 0, -1)
np.concatenate([a, b], axis=0); np.vstack([a, b]); np.hstack([a, b]); np.stack([a, b], axis=0); np.column_stack([a, b]); np.dstack
np.split(a, 3); np.array_split(a, 3); np.hsplit(a, 2); np.squeeze(a); np.expand_dims(a, 0); np.pad(a, 1, constant_values=0)
np.flip(a, axis=0); np.roll(a, 1); np.rot90(a); np.delete(a, 1); np.insert(a, 1, 99); np.append(a, 5)

Оси: для 2D axis=0 по строкам (вдоль столбцов), axis=1 по столбцам. keepdims=True сохраняет ось размером 1 для broadcasting.

Операции и broadcasting

a + b; a - b; a * b; a / b; a // b; a % b; a ** 2; -a; abs(a); a @ b           # поэлементно, @ — матричное умножение
a > 3; a == b; np.isclose(a, b); np.allclose(a, b); np.array_equal(a, b)        # не сравнивайте float через ==

Broadcasting: массивы разных форм выравниваются с конца; размеры должны совпадать или быть равны 1.

X = np.arange(6).reshape(2, 3)         # (2, 3)
X + np.array([10, 20, 30])             # (3,) → к каждой строке
X + np.array([[1], [2]])               # (2, 1) → к каждому столбцу
(x[:, None] - y[None, :])              # матрица попарных разностей (n, m)

Ошибка «operands could not be broadcast together» означает несовместимые формы.

Математика и редукции

np.sqrt(a); np.exp(a); np.log(a); np.log10(a); np.sin(a); np.cos(a); np.power(a, 2); np.round(a, 2); np.floor(a); np.ceil(a); np.sign(a)
np.sum(a); np.mean(a); np.median(a); np.std(a); np.var(a); np.min(a); np.max(a); np.prod(a); np.ptp(a)
a.sum(axis=0); a.mean(axis=1, keepdims=True); np.cumsum(a); np.cumprod(a); np.diff(a); np.gradient(a)
np.argmax(a); np.argmin(a, axis=1); np.percentile(a, [25, 50, 75]); np.quantile(a, 0.9); np.corrcoef(x, y); np.cov(x, y)
np.nansum(a); np.nanmean(a); np.isnan(a); np.isinf(a); np.nan_to_num(a, nan=0.0); np.any(a > 0); np.all(a > 0); np.count_nonzero(a)
np.average(a, weights=w); np.histogram(a, bins=10); np.bincount(ints); np.digitize(a, bins)

np.nan заражает вычисления, используйте nan*-версии или маски.

Сортировка, поиск, множества

np.sort(a); np.sort(a, axis=0); np.argsort(a); np.argsort(-a)[:5]; np.lexsort((b, a))      # lexsort: сначала по a, затем по b
np.searchsorted(sorted_a, x); np.partition(a, 3); np.argpartition(a, -5)[-5:]               # top-K без полной сортировки
np.unique(a); np.unique(a, return_counts=True, return_inverse=True); np.in1d(a, b); np.isin(a, b)
np.intersect1d(a, b); np.union1d(a, b); np.setdiff1d(a, b)

Линейная алгебра

A @ B; np.dot(A, B); np.matmul(A, B); np.outer(u, v); np.inner(u, v); np.cross(u, v); np.kron(A, B); np.trace(A); np.tril(A); np.triu(A)
np.linalg.inv(A); np.linalg.det(A); np.linalg.solve(A, b)           # решать системы лучше solve, а не inv
np.linalg.eig(A); np.linalg.eigh(S); np.linalg.svd(A); np.linalg.qr(A); np.linalg.norm(x, ord=2); np.linalg.matrix_rank(A); np.linalg.pinv(A)
np.linalg.lstsq(X, y, rcond=None)                                    # метод наименьших квадратов
np.einsum('ij,jk->ik', A, B); np.einsum('bi,bi->b', U, V)            # компактная запись сумм по индексам

Ввод-вывод

np.save('a.npy', a); np.load('a.npy'); np.savez_compressed('d.npz', x=x, y=y)         # бинарные форматы
np.savetxt('a.csv', a, delimiter=',', fmt='%.4f'); np.loadtxt('a.csv', delimiter=',', skiprows=1)
np.genfromtxt('a.csv', delimiter=',', names=True, filling_values=0)
a = np.memmap('big.dat', dtype='float32', mode='r', shape=(10**6, 128))                # данные больше памяти
np.set_printoptions(precision=3, suppress=True, linewidth=120)

Структурированные массивы и даты

dt = np.dtype([('name', 'U10'), ('age', 'i4')]); people = np.array([('Аня', 30)], dtype=dt); people['age']
d = np.array(['2026-01-01', '2026-01-31'], dtype='datetime64[D]'); d[1] - d[0]; np.arange('2026-01', '2026-04', dtype='datetime64[M]')

Для таблиц практичнее pandas или polars.

Векторизация и производительность

# медленно
res = [x ** 2 + 1 for x in a]
# быстро
res = a ** 2 + 1
np.vectorize(f)     # удобство, но не ускорение (внутри цикл Python)

Советы:

  • Избегайте циклов по элементам; используйте ufunc, маски, where, einsum.
  • Предвыделяйте память (np.empty), не растите массив через append в цикле.
  • Операции in-place: a += 1, np.add(a, b, out=a).
  • Выбирайте нужный dtype (float32, uint8), следите за непрерывностью (np.ascontiguousarray).
  • Для узких мест: Numba (@njit), Cython, JAX, CuPy (GPU), numexpr.
  • Многопоточные BLAS: переменные OMP_NUM_THREADS, OPENBLAS_NUM_THREADS.

Типовые рецепты

(x - x.mean(axis=0)) / x.std(axis=0)                                  # стандартизация столбцов
(x - x.min()) / (x.max() - x.min())                                   # нормализация 0..1
np.eye(10)[labels]                                                    # one-hot кодирование
np.convolve(x, np.ones(5) / 5, mode='valid')                          # скользящее среднее
np.sqrt(((a[:, None, :] - b[None, :, :]) ** 2).sum(-1))               # матрица евклидовых расстояний
np.exp(z - z.max(1, keepdims=True)) / np.exp(z - z.max(1, keepdims=True)).sum(1, keepdims=True)   # устойчивый softmax
np.random.default_rng(0).permutation(len(x))[:k]                      # случайная подвыборка
np.where(np.diff(np.sign(y)) != 0)[0]                                 # индексы смены знака
np.cumsum(x) / np.arange(1, len(x) + 1)                               # накопленное среднее
np.lib.stride_tricks.sliding_window_view(x, 3)                        # скользящие окна без копий

Подводные камни

  • Срез это view: изменили срез, изменили исходный.
  • np.array([1, 2.5]) приводит всё к float, смесь типов даёт object (медленно).
  • and / or / not не работают с массивами: &, |, ~ со скобками.
  • a == None и == для float: is None, np.isclose.
  • Переполнение целых без предупреждения; деление на ноль даёт inf/nan с предупреждением (np.errstate(all='ignore')).
  • np.matrix устарел: используйте ndarray и @.
  • np.random.seed глобален: используйте default_rng(seed).
  • NumPy 2.0: изменены некоторые типы и функции (np.float_, np.in1d помечены устаревшими): проверяйте совместимость библиотек.

Экосистема

SciPy (научные алгоритмы, разреженные матрицы), pandas и polars (таблицы), Matplotlib (графики), scikit-learn, PyTorch и TensorFlow (тензоры и GPU), JAX (автодифференцирование и XLA), Numba, Dask (большие массивы), xarray (помеченные n-мерные данные), CuPy (GPU-версия NumPy).