NumPy
🎯 Цель
После прочтения этой главы:
- Поймёте отличие NumPy
ndarrayот Pythonlistи узнаете, когда какой использовать - Научитесь писать быстрый код без циклов с помощью vectorized operations
- Сможете работать с массивами разных размерностей, используя broadcasting
- Будете знать NumPy-паттерны, встречающиеся в 90% ML-кода
Что нужно изучить
- Создание
ndarray:np.array,np.zeros,np.ones,np.arange,np.linspace,np.random - Атрибуты array:
shape,dtype,ndim,size - Indexing и slicing (1-D, 2-D, boolean, fancy)
- Reshape, transpose, concatenate, stack, split
- Arithmetic operations и broadcasting
- Universal functions (ufuncs):
np.sin,np.exp,np.logи т.д. - Aggregations:
sum,mean,max,min,argmax, параметрaxis - Linear algebra (
np.linalg) - Random sampling (
np.random)
Библиотеки
pip install numpy
Рекомендуется NumPy версии 1.26+ или 2.x.
Важные темы
Почему NumPy быстрее Python list?
# Python list — каждый элемент — отдельный PyObject (медленно)
py_list = [1, 2, 3, 1_000_000]
# NumPy — единый C-массив в памяти (быстро)
np_arr = np.array([1, 2, 3, 1_000_000], dtype=np.int64)
NumPy:
- Написан на C, использует SIMD-инструкции
- Единый
dtype(например, всёint64) —cache-friendly - Vectorized:
arr * 2— одна операция на весь массив
Bench: умножить 1M элементов на 2 — list ~50ms, NumPy ~1ms (быстрее в 50x).
Broadcasting
Самая мощная фишка NumPy. Работа с массивами разных размерностей:
# Добавление (3,) вектора к матрице (3, 3)
A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # shape (3, 3)
b = np.array([10, 20, 30]) # shape (3,)
result = A + b
# [[11, 22, 33], [14, 25, 36], [17, 28, 39]]
NumPy автоматически “broadcast” b к каждой строке.
**Правило:**размерности сравниваются с конца. Они должны быть либо равны, либо одна из них должна быть 1.
Концепция axis
Для 2-D array:
axis=0— строка (вертикально, “down the rows”)axis=1— столбец (горизонтально, “across the columns”)
A = np.array([[1, 2], [3, 4], [5, 6]]) # shape (3, 2)
A.sum(axis=0) # [9, 12] — сумма каждого столбца
A.sum(axis=1) # [3, 7, 11] — сумма каждой строки
Примеры кода
Создание array и основные операции
import numpy as np
# Способы создания
a = np.array([1, 2, 3, 4])
zeros = np.zeros((3, 4)) # 3×4 нулей
ones = np.ones((2, 2)) # 2×2 единиц
rng = np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
lin = np.linspace(0, 1, 5) # 5 равномерно распределённых чисел [0, 0.25, 0.5, 0.75, 1]
random_arr = np.random.rand(3, 3) # 3×3 random [0, 1)
# Атрибуты
print(a.shape, a.dtype, a.ndim, a.size) # (4,) int64 1 4
Indexing и boolean filtering
arr = np.array([10, 20, 30, 40, 50, 60])
# Slicing
print(arr[1:4]) # [20, 30, 40]
print(arr[::-1]) # обратный порядок
# Boolean indexing — очень часто используется в ML
mask = arr > 30
print(arr[mask]) # [40, 50, 60]
# Одновременная фильтрация и изменение
arr[arr < 30] = 0
print(arr) # [0, 0, 30, 40, 50, 60]
# 2-D indexing
M = np.arange(12).reshape(3, 4)
print(M[1, 2]) # 6
print(M[:, 1]) # 2-й столбец
print(M[1:, :2]) # 1-я строка, столбцы 0 и 1
Vectorized operations (без циклов)
# С циклом (МЕДЛЕННО — так не делайте)
arr = np.arange(1_000_000)
result = []
for x in arr:
result.append(x ** 2 + 3 * x - 5)
# Vectorized (БЫСТРО — всегда так)
arr = np.arange(1_000_000)
result = arr ** 2 + 3 * arr - 5
# Conditional vectorization
prices = np.array([100, 50, 200, 75, 300])
discounted = np.where(prices > 100, prices * 0.9, prices)
# [100, 50, 180, 75, 270]
Интеграция с backend
В backend NumPy удобен в таких местах:
1. Быстрая JSON-агрегация
from fastapi import FastAPI
import numpy as np
app = FastAPI()
@app.post("/metrics/")
def process_metrics(values: list[float]):
arr = np.array(values, dtype=np.float64)
# В pure Python для 1M элементов ~500ms, в NumPy ~5ms
return {
"sum": float(arr.sum()),
"mean": float(arr.mean()),
"p50": float(np.percentile(arr, 50)),
"p95": float(np.percentile(arr, 95)),
"p99": float(np.percentile(arr, 99)),
}
2. Endpoint обработки изображений
import numpy as np
from PIL import Image
from io import BytesIO
@app.post("/image/grayscale/")
async def to_grayscale(file: UploadFile):
img = Image.open(file.file)
arr = np.array(img)
# RGB → grayscale: формула luminance
gray = (0.299 * arr[..., 0] + 0.587 * arr[..., 1] + 0.114 * arr[..., 2]).astype(np.uint8)
out = Image.fromarray(gray)
buf = BytesIO()
out.save(buf, format="PNG")
return Response(content=buf.getvalue(), media_type="image/png")
3. Embedding similarity (для RAG)
def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# Batch search — сравнение 1000 эмбеддингов с query
def find_similar(query: np.ndarray, embeddings: np.ndarray, top_k: int = 5):
# embeddings shape: (1000, 384), query shape: (384,)
sims = embeddings @ query / (np.linalg.norm(embeddings, axis=1) * np.linalg.norm(query))
top_indices = np.argsort(sims)[::-1][:top_k]
return top_indices, sims[top_indices]
Ресурсы
- Official NumPy quickstart — numpy.org/doc/stable/user/quickstart.html
- “From Python to NumPy” — Nicolas Rougier (бесплатно, advanced patterns)
- NumPy Illustrated: The Visual Guide — Lev Maximov (Medium)
- “100 NumPy exercises” — GitHub-репо (сборник упражнений): github.com/rougier/numpy-100
🏋️ Упражнения
🟢 Easy
- Создайте массив, равный
np.arange(0, 100, 5), и отфильтруйте из него нечётные числа. - Создайте случайную матрицу
3x3, найдите максимальный элемент и его индекс (np.argmax). - Объедините два массива
[1, 2, 3, 4]и[5, 6, 7, 8]вертикально и горизонтально (np.vstack,np.hstack).
🟡 Medium
- Сгенерируйте 10000 случайных чисел и вычислите
x^2 + 2x + 1в варианте Pure Pythonforloop и в NumPy vectorized. Сравните их сtimeit. - Создайте случайную матрицу
(50, 50)и имитируйте шахматную доскуchess(np.indices+ broadcasting). - Normalize: приведите каждый столбец случайной матрицы к диапазону
0..1(min-max normalization).
🔴 Hard
- Cosine similarity API: создайте FastAPI endpoint. Пользователь отправляет
query: list[float]иdatabase: list[list[float]]. Верните Top-K наиболее похожих векторов. Всё должно быть NumPy vectorized (без циклов). - Sliding window: для 1-D array вычислите rolling mean с
window_size=kmemory-efficient черезnp.lib.stride_tricks.
Capstone
В файле notebooks/month-01/01_numpy_basics.ipynb:
- Симулируйте матрицу активности 1000 пользователей за 30 дней:
shape (1000, 30) - Вычислите среднюю недельную активность каждого пользователя (
shape (1000, 4)) - Найдите 10 самых активных пользователей
- Визуализируйте матрицу активности в виде heatmap (с Matplotlib)
✅ Чек-лист
- Понимаю разницу между
ndarrayи Pythonlist - Концепции
shape,dtype,axisясны - Использую boolean indexing, не пишу
for-циклы - Знаю правило broadcasting и применяю в небольших примерах
- Знаю матричные операции через
np.linalg(dot product, inverse, eigvals) - Измерял, во сколько раз мой vectorized-код быстрее Python loop
Время перейти к Pandas.