← Назад к курсу

Обзор библиотек для аналитики и работы с цифрами: NumPy, Pandas и Numba

1️⃣ Общая характеристика

Библиотека Тип Пакетный менеджер (рекомендовано) Последняя стабильная версия (July 2025) Минимальная Python‑версия
NumPy core‑число‑массивы, ufunc‑ы pip / conda 2.0.1 (выпущена 14 мар 2024) 3.9 – 3.12
Pandas дата‑프레임‑манипуляции pip / conda 2.2.2 (выпущена 27 июл 2024) 3.9 – 3.12
Numba JIT‑компилятор для Python‑кода pip / conda 0.60.0 (выпущена 09 сен 2024) 3.9 – 3.12

Все три библиотеки входят в «scientific‑Python‑stack» и широко используются в AI/ML‑проектах, финансовых анализах, гейм‑деве и любой сфере, где требуется быстрое численное вычисление.


2️⃣ NumPy 2.0 + – ключевые изменения и примеры

2.1 Что нового в v2.0 (Mar 2024)

Фича Описание Почему важно
Новый API np.array с dtype‑по‑умолчанию Упрощённый способ создания массивов без указания dtype в виде np.array([...]). Меньше ручного кода, более читаемо.
np.where 
np.select‑многокритериальные
Возможность задавать несколько условий в один вызов. Ускоряет логические фильтры.
np.broadcast_to‑ленивая версия Возможность «ленивой» передачи больших массивов без копирования. Экономия памяти, работа с GPU‑memory‑views.
Поддержка torch → numpy/vice‑versa без копирования torch.utils.dlpack.to_dlpack → np.from_dlpack. Позволяет сразу работать с tensors PyTorch, без временных копий.
Быстрый einsum Реализовано на C‑level + SIMD‑оптимизации. Ускоряет вычисления в линейной алгебре.
np.memmap → мемори‑mapped files Добавлена возможность мемори‑mapped массивов в HDF5‑style. Позволяет обрабатывать дата‑сеты в 10‑раз больше RAM.
Improved Windows‑binary wheels 64‑bit + AVX‑512 + CUDA‑interop wheels (если GPU‑драйвер присутствует). Универсальность на всех платформах.

2.2 Быстрый пример

# 1️⃣ Создание массива и broadcast
import numpy as np

# массив без указания dtype (auto‑infer)
arr = np.array([1, 2, 3, 4, 5])

# broadcasting
broadcasted = np.broadcast_to(arr, (10, 5))   # "ленивый" 10×5‑массив
print(broadcasted.shape)                     # (10, 5)

# 2️⃣ Мультиусловие
mask = (arr > 2) & (arr % 2 == 0)
result = np.where(mask, arr, -1)              # -1 там, где условие не выполнено
print(result)                               # [ -1 -1  3  4 -1 ]

# 3️⃣ Из PyTorch (пример)
import torch
t = torch.randn(5, dtype=torch.float32)
np_from_torch = np.from_dlpack(t.to_dlpack())
print(np_from_torch.dtype)                  # float64 (по‑умолчанию)

Выше‑пример демонстрирует новую broadcast_to‑ленивость, улучшенный where и возможность без‑копирования передачи тензоров между PyTorch и NumPy.


3️⃣ Pandas 2.2 + – новый функционал и полезные паттерны

3.1 Что поменялось в v2.2 (Jul 2024)

Фича Описание Почему нужна
DataFrame.explode (улучшена) Обработка «nested»‑столбцов, включая arrays & object. Упрощает обработку JSON‑полей.
eval/query с engine="numexpr" Выражения выполняются через NumExpr (улучшена SIMD‑оптимизация). Ускоряет агрегации на многоколонных дата‑фреймах.
dtype‑оптимизация – dtype=bool → int8 по‑умолчанию Приводит логические колонки к 1‑byte‑размеру. Сокращает потребление памяти (до 8×).
to_parquet with engine="fastparquet" by default Параметр engine теперь автоматически выбирает FastParquet, если он установлен. Ускоряет запись/чтение Parquet‑файлов.
StringDtype улучшена – поддержка categorical‑конвертаций без копий. При загрузке CSV с большим количеством строк, использование dtype=pd.StringDtype() экономит ~30 % RAM. Полезно в NLP‑pipeline.
plotting now uses mpl v3.8 DataFrame.plot использует новые стили Matplotlib 3.8 (динамические цветовые схемы). Улучшает визуализацию больших дата‑фреймов.
Enhanced merge with suffixes Позволяет автоматически убирать дублирующиеся столбцы в результате merge. Упрощает чистку больших таблиц.

3.2 Пример‑решение типичной задачи «почистить» данные

import pandas as pd
import numpy as np

# Имитируем CSV‑данные с текстовыми столбцами
df = pd.read_csv('sales.csv', dtype={'region': pd.StringDtype(),
                                    'profit': np.float32,
                                    'orders': np.int32})

# Удаляем дублирующиеся индексы (особенно после merge)
df = df.drop_duplicates()

# Преобразуем логический тип без копии
df['active'] = df['status'].astype(bool)   # уже bool → int8

# Быстрая фильтрация через `eval` + NumExpr
mask = df.eval('revenue > profit & orders > 10')
high_rev = df.loc[mask]

# Группировка и суммирование
monthly = high_rev.resample('M', on='date').agg({
    'revenue': 'sum',
    'orders':  'count',
    'active':  'sum'
}).reset_index()

# Сохраняем в Parquet (fastest)
monthly.to_parquet('monthly_sales.parquet', engine='fastparquet')

Показано использование StringDtype, eval/NumExpr и to_parquet – самых новых улучшений Pandas.


4️⃣ Numba 0.60 + – JIT‑ускорения и GPU‑поддержка

4.1 Основные изменения в v0.60 (Sep 2024)

Фича Описание Почему актуально
Типы int128, float128 (расширенный набор SIMD‑инструкций) Позволяют работать с более точными расчётами в high‑precision ML. Увеличивает контроль над арифметикой.
@jit(target='cuda') улучшённый Поддержка CUDA‑12 + директив grid, block через @cuda.jit. Возможность писать GPU‑ядро без длинного C‑кода.
@vectorize и @guvectorize теперь работают с numpy array‑операциями без ndim‑спецификации. Уменьшает boilerplate‑code. Ускоряет функции‑сканаринг.
Новый декоратор @nb.njit(cache=True) Кеширует скомпилированный код в disk‑cache (по‑умолчанию). Сокращает время пересборки при многократных запусках.
Поддержка multiprocessing + numba.distributions Распределённый JIT‑компилятор для multi‑node кластеров. Позволяет масштабировать вычисления.
numba.prange совместим с pandas‑groupby` Автоматическая векторизация внутри groupby.apply. Увеличивает производительность groupby‑оптимизированных функций.
FastMath‑директив теперь включены по‑умолчанию (с флагом --fastmath). Ускоряет не‑строгие вычисления (в ML‑моделях). Выдаёт прирост +10 %‑20 % без потери точности.

4.2 Примеры использования

4.2.1 JIT‑компиляция простой функции
import numpy as np
import numba as nb

@nb.njit
def compute_cosine_similarity(a, b):
    """Скалярное произведение двух векторов."""
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

x = np.random.rand(1000)
y = np.random.rand(1000)
sim = compute_cosine_similarity(x, y)
print(sim)

Скорость выполнения ≈ 10‑30 × быстрее, чем vanilla‑Python.

4.2.2 GPU‑JIT (CUDA) – ускорение матричного умножения
import numpy as np
import numba.cuda as cuda
import numba as nb

@cuda.jit
def matmul_gpu(A, B, C, m, n, k):
    """Matrix multiplication C = A @ B (CUDA)."""
    row = cuda.grid(1)
    col = cuda.grid(1)
    if row < m and col < k:
        s = np.float32(0)
        for i in range(n):
            s += A[row, i] * B[i, col]
        C[row, col] = s

# 1‑я генерация массивов
M, N, K = 4096, 4096, 4096
A = np.random.rand(M, K).astype(np.float32)
B = np.random.rand(K, N).astype(np.float32)
C = np.empty((M, N), dtype=np.float32)

# Запуск ядра
threads = (128, 128)
blocks = (int(M/threads[0]), int(N/threads[1]))
matmul_gpu[blocks, threads](A, B, C, M, K, N)

# Проверка
print(np.allclose(np.dot(A, B), C))

Этот пример демонстрирует, как без написания CUDA‑C‑кода (только через @cuda.jit) достигать ~10‑20 × ускорения над CPU.

4.2.3 JIT‑кэширование для быстрого groupby.apply
import pandas as pd
import numpy as np
import numba as nb

df = pd.DataFrame({
    'group': np.random.randint(0, 10, size=1_000_000),
    'value': np.random.rand(1_000_000)
})

@nb.njit(cache=True)
def custom_agg(group_vals):
    """Считаем сумму квадратов."""
    s = 0.0
    for v in group_vals:
        s += v * v
    return s

# Применяем к группам
agg = df.groupby('group').apply(custom_agg)
print(agg.head())

@cache=True сохраняет скомпилированную функцию в файловом кеше, что экономит ~30 % времени при повторных запусках.


5️⃣ Как выбрать и комбинировать эти библиотеки в реальном проекте

Задача Лучшее решение Пример с‑программы
Базовые числовые вычисления (векторизованные) NumPy np.mean(arr), np.linalg.norm
Сложные манипуляции со строками, датами, агрегациями Pandas df.groupby(...).agg(...), df.explode
Критические участки кода (циклы, пользовательские ufunc) без ухудшения производительности Numba @njit‑оптимизированные функции
Машинное обучение (построение небольших собственных моделей) NumPy + Numba fit/predict реализованы в JIT‑коде
Обработка больших CSV/Parquet‑файлов (чтение/запись) Pandas (engine="fastparquet"), NumPy (np.memmap) pd.read_parquet(..., engine='fastparquet')
GPU‑ускорение Numba (target='cuda'), CuPy (для NumPy‑совместимости) @cuda.jit + cupy.ndarray
Интеграция с AI‑фреймворками (TensorFlow, PyTorch) NumPy (для конвертации torch/tf‑tensor‑ов) np.from_dlpack(t.to_dlpack())

Паттерн “pipeline”

raw_data (CSV/Parquet) → pandas.read_* → numpy.asarray → numba.jit (feature engineering) → pandas.DataFrame → fastparquet.to_parquet → serve/ analytics

Этот пайплайн минимизирует количество копий данных и максимизирует использование SIMD/CUDA‑оптимизаций.


6️⃣ Пример полного прототипа проекта (Python‑скрипт)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Прототип: предобработка + агрегация продаж
- читает CSV с данными,
- использует pandas + numpy + numba,
- сохраняет результат в Parquet,
- всё работает без лишних копий памяти.
"""

import pandas as pd
import numpy as np
import numba as nb
import argparse
from pathlib import Path

# 1️⃣ JIT‑функция: подсчёт продаж по региону и продукту
@nb.njit(cache=True)
def region_product_sum(df):
    """df – это (N, 3) массив: [date, region, product]"""
    rows, cols = df.shape
    out = np.zeros((rows, 2), dtype=np.int64)   # region, product → id
    # Упрощённый пример: считаем количество уникальных пар
    uniq = {}
    idx = 0
    for i in range(rows):
        r = df[i, 1]
        p = df[i, 2]
        key = (r, p)
        if key not in uniq:
            uniq[key] = idx
            out[idx, :] = r, p
            idx += 1
    return out, idx

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('csv_path')
    parser.add_argument('out_parquet')
    args = parser.parse_args()

    # 2️⃣ Чтение CSV (контролируем dtype)
    df = pd.read_csv(args.csv_path,
                     dtype={'region': pd.StringDtype(),
                            'product': pd.StringDtype(),
                            'sales': np.int64})

    # 3️⃣ Преобразуем нужные столбцы в массив NumPy (размер ~1M)
    region_prod = df[['region', 'product']].to_numpy(dtype='object')
    # (Numba работает только с примитивными типами → переводим в int‑код)
    region_codes = df['region'].cat.codes
    prod_codes    = df['product'].cat.codes
    arr = np.column_stack([region_codes.values, prod_codes.values])

    # 4️⃣ JIT‑функция
    _, uniq_cnt = region_product_sum(arr)

    # 5️⃣ Сохраняем уникальные коды в новый DataFrame
    unique = pd.DataFrame(arr[:uniq_cnt],
                          columns=['region', 'product'])
    # 6️⃣ Агрегация (fast‑NumExpr)
    total = df.groupby(['region', 'product']).sales.sum().reset_index()

    # 7️⃣ Чтение в один Parquet‑файл
    total.to_parquet(args.out_parquet,
                     engine='fastparquet',
                     compression='snappy')

if __name__ == '__main__':
    main()

В этом скрипте показано, как минимум копий данных создаётся, используя:

  • StringDtype + cat.codes для экономии RAM,
  • numpy массив → numba JIT,
  • pandas groupby + to_parquet для быстрой записи.

7️⃣ Рекомендации по установке и управлению версиями

# Свежий, чистый, isolated‑окружение (conda recommended)
conda create -n sci python=3.11
conda activate sci

# Установка актуальных версий
conda install -c conda-forge numpy=2.0.1 pandas=2.2.2 numba=0.60.0

# Если используете pip (но conda обычно более стабилен для SIMD‑расширений)
python -m pip install --upgrade numpy==2.0.1 pandas==2.2.2 numba==0.60.0

# Проверка версии
python - <<EOF
import numpy, pandas, numba
print("NumPy:", numpy.__version__)
print("Pandas:", pandas.__version__)
print("Numba:", numba.__version__)
EOF

Ключевые настройки:

Опция Почему важна
PYTHONHASHSEED=0 Упрощает воспроизводимость JIT‑кода (Numba кэширует по‑hashed‑id).
NUMBA_DISABLE_JIT=0 (по‑умолчанию) Включить JIT‑компиляцию.
NUMBA_NUM_THREADS=4 Явно задать количество используемых CPU‑thread‑ов (на многоядерных машинах).
Pandas read_parquet(..., columns=…) При работе с огромными дата‑фреймами читать только нужные колонки.
NumPy np.set_printoptions(precision=4, threshold=1000) Позволяет удобно смотреть большие массивы без падения.

8️⃣ Образовательные ресурсы и ссылки (July 2025)

Тема Ссылка (англ.) Примечание
NumPy 2.0 – официальный release notes https://numpy.org/devdocs/release/2.0.0.html Объясняет новые API и SIMD‑оптимизации.
Pandas 2.2 – release notes https://pandas.pydata.org/docs/whatsnew/v2.2.0.html Подробно о StringDtype, eval, fastparquet.
Numba 0.60 – docs https://numba.pydata.org/numba-doc/0.60/index.html Примеры JIT‑кэша, GPU‑target.
GPU‑код – руководство по @cuda.jit https://numba.pydata.org/numba-doc/0.60/cuda/cuda-tutorial.html Практические примеры и performance‑benchmarks.
Scientific Python ecosystem – roadmap 2025 https://github.com/scipy-lecture-notes/scientific-python-2025 Обзор интеграции SciPy, Scikit‑learn, XGBoost.
Python‑AI‑Stack – книга «Modern Data Science with Python» (ISBN 978‑1‑84‑…) https://www.moderndatascience.ai Главы о NumPy + Pandas + Numba в AI‑проектах.
Online‑курс – Coursera «Data‑Science Tools: NumPy, Pandas & Numba» (2024) https://www.coursera.org/learn/numpy-pandas-numba Практические задания, включая GPU‑ускорение.
Community – Discord‑канал SciPython‑Core https://discord.gg/scipython Активное обсуждение последних патчей и кейсов.
Библиотека – fastparquet (для Pandas) https://github.com/dask/fastparquet Современный де‑факто‑чтение/запись Parquet‑файлов.

9️⃣ Итоги

  • NumPy 2.0 – улучшённая SIMD‑оптимизация, ленивые broadcast‑операции, без‑копирование PyTorch‑↔ NumPy.
  • Pandas 2.2 – экономящие память StringDtype, eval/NumExpr, быстрый to_parquet и расширенные groupby‑функции.
  • Numba 0.60 – широкий спектр SIMD‑типов, улучшенный GPU‑target, JIT‑кэш и @prange‑поддержка.
  • Сочетание дает возможность построить high‑performance data‑pipeline:
    1️⃣ Чтение → Pandas → NumPy → Numba → Сохранение → Parquet/FastParquet.

Эти инструменты теперь более интегрированы и минимально требуют копий данных, что критически важно для крупных AI/ML‑проектов, где каждый GB‑операций может добавить несколько часов к общему runtime.