NumPy: шпаргалка для аналитика

Зачем аналитику NumPy

Pandas построен на NumPy. Любое «быстрое» решение в pandas — это под капотом NumPy-операция.

Для аналитика NumPy — фундамент. Его не спрашивают глубоко, но ожидают уверенного владения: массивы, векторизация, индексация, базовые агрегаты.

Создание массивов

import numpy as np

# Из списка
a = np.array([1, 2, 3])
b = np.array([[1, 2], [3, 4]])  # 2D

# Нули / единицы / range
np.zeros(5)           # [0, 0, 0, 0, 0]
np.ones((2, 3))       # 2x3 единиц
np.arange(0, 10, 2)   # [0, 2, 4, 6, 8]
np.linspace(0, 1, 5)  # [0, 0.25, 0.5, 0.75, 1]

# Случайные
np.random.rand(3)              # uniform [0, 1]
np.random.randn(3)             # normal(0, 1)
np.random.randint(1, 10, 5)    # целые [1, 10)
np.random.choice([1, 2, 3], 5) # выбрать из списка

# С seed для воспроизводимости
np.random.seed(42)

Свойства массивов

a = np.array([[1, 2, 3], [4, 5, 6]])
a.shape    # (2, 3)
a.ndim     # 2 (размерность)
a.size     # 6 (всего элементов)
a.dtype    # int64

Индексация

1D

a = np.array([10, 20, 30, 40, 50])
a[0]        # 10
a[-1]       # 50
a[1:3]      # [20, 30]
a[::-1]     # [50, 40, 30, 20, 10] — reverse

2D

m = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
m[0, 1]     # 2 (строка 0, столбец 1)
m[1, :]     # [4, 5, 6] (строка 1)
m[:, 2]     # [3, 6, 9] (столбец 2)
m[:2, 1:]   # [[2, 3], [5, 6]] (подматрица)

Boolean indexing

a = np.array([1, 2, 3, 4, 5])
a[a > 2]         # [3, 4, 5]
a[(a > 1) & (a < 4)]  # [2, 3]

Fancy indexing

a = np.array([10, 20, 30, 40, 50])
a[[0, 2, 4]]  # [10, 30, 50]

Векторизация

Главное преимущество NumPy — никаких Python-циклов:

# Плохо (медленно)
result = []
for x in arr:
    result.append(x ** 2 + 1)

# Хорошо (быстро)
result = arr ** 2 + 1

На собесе: «Как ускорить Python-цикл?» Почти всегда ответ: заменить на векторную NumPy/pandas-операцию.

Broadcasting

Операции между массивами разных форм:

a = np.array([[1, 2, 3], [4, 5, 6]])   # 2x3
b = np.array([10, 20, 30])              # 1x3

a + b
# [[11, 22, 33], [14, 25, 36]]  — b broadcasts к каждой строке

a + 100   # скаляр broadcasts ко всем элементам

Правило: формы совместимы, если одна из размерностей равна 1 или совпадает с другой.

Попробовать силы на подобных вопросах проще всего в тренажёре Карьерник — прямо в Telegram, без регистрации через сайт.

Агрегаты

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

a.sum()          # 21 (все)
a.sum(axis=0)    # [5, 7, 9]  (по столбцам)
a.sum(axis=1)    # [6, 15]    (по строкам)

a.mean(), a.std(), a.min(), a.max(), a.median()  # median отдельно: np.median(a)

np.percentile(a, 50)  # медиана
np.percentile(a, [25, 50, 75])  # квартили

Математические функции

np.sqrt(a)
np.exp(a)
np.log(a)
np.log2(a)
np.sin(a), np.cos(a)
np.abs(a)
np.round(a, 2)
np.ceil(a), np.floor(a)

Linear algebra

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

A @ B             # матричное умножение (PE или np.dot)
A * B             # поэлементное
A.T               # транспонирование
np.linalg.inv(A)  # обратная
np.linalg.det(A)  # детерминант
np.linalg.eig(A)  # собственные значения

Изменение формы

a = np.arange(12)  # [0, 1, ..., 11]
a.reshape(3, 4)    # 3x4 матрица
a.reshape(-1, 4)   # -1 = авто (3), 4 столбца
a.flatten()        # обратно в 1D

# Добавить измерение
a[:, np.newaxis]   # (12,) → (12, 1)
a[np.newaxis, :]   # (12,) → (1, 12)

Конкатенация

np.concatenate([a1, a2])         # по axis=0
np.concatenate([a1, a2], axis=1)  # по столбцам
np.vstack([a1, a2])              # вертикально
np.hstack([a1, a2])              # горизонтально
np.stack([a1, a2])               # новое измерение

Сортировка

a = np.array([3, 1, 4, 1, 5])
np.sort(a)           # [1, 1, 3, 4, 5]
np.argsort(a)        # [1, 3, 0, 2, 4] (индексы после сортировки)
a[np.argsort(a)]     # = np.sort(a)

Пройти 30–50 задач по теме за вечер можно в Telegram-тренажёре. Это то, что отличает «знаю» от «уверенно отвечу на собесе».

Уникальные и статистика

np.unique(a)             # уникальные
np.unique(a, return_counts=True)  # + счётчики
np.bincount(a)           # частоты (для integer)
np.histogram(a, bins=10) # гистограмма

NaN-обработка

a = np.array([1, 2, np.nan, 4])

np.isnan(a)            # [F, F, T, F]
np.nanmean(a)          # 2.33 (игнорирует NaN)
np.nansum(a)           # 7
np.nanmedian(a)

# Заменить NaN
np.nan_to_num(a, nan=0)  # [1, 2, 0, 4]

10 задач

1. Нормализовать массив

a_norm = (a - a.mean()) / a.std()

2. Применить функцию к каждому элементу

# Не циклом! Векторизованно
result = np.where(a > 0, a ** 2, 0)

3. Найти индексы всех элементов > 10

np.where(a > 10)[0]

4. Посчитать в диапазонах (binning)

bins = [0, 10, 100, 1000]
np.digitize(a, bins)

5. One-hot encoding

classes = np.unique(a)
one_hot = (a[:, np.newaxis] == classes).astype(int)

6. Топ-K значений

top_k_indices = np.argpartition(a, -k)[-k:]
top_k = a[top_k_indices]

7. Кумулятивная сумма

np.cumsum(a)

8. Проверить, все ли положительные

np.all(a > 0)

9. Скользящее среднее (np.convolve)

window = np.ones(7) / 7
ma = np.convolve(a, window, mode='valid')

10. Декартово произведение

# Все пары из двух массивов
np.array(np.meshgrid(a, b)).T.reshape(-1, 2)

Как тренироваться

NumPy учится через мини-задачи: создать, отфильтровать, агрегировать, заменить NaN. Каждая операция в pandas под капотом — NumPy, и понимание базы ускоряет мышление.

Тренажёр Карьерник содержит Python-блок с упражнениями на NumPy и pandas.

Совет: если вас просят ускорить Python-код на собесе — первым делом ищите циклы for и заменяйте на NumPy-векторизацию. Это 90% решений.

Читайте также

FAQ

NumPy или pandas?

Pandas для табличных данных с именованными столбцами. NumPy для низкоуровневых операций с массивами (матрицы, линейная алгебра, быстрая векторизация).

Как быстрее: NumPy или Python list?

NumPy в 10-100x быстрее на числовых операциях. Благодаря C-backend и векторизации.

Что такое broadcasting?

Правила NumPy для операций между массивами разных форм. Упрощает код и ускоряет — не нужно вручную делать reshape.

np.where или маска?

Маска (a[a > 0]) — если нужен фильтр. np.where — если нужна замена по условию: np.where(a > 0, a, -a).