Django-Admin.ru

SciPy: научные вычисления в Python

Библиотека SciPy в Python: установка, связь с NumPy, оптимизация, интегралы, интерполяция, статистика, линейная алгебра, сигналы и разреженные матрицы.

Редакция Django-Admin.ru
· 20 мин чтения

Содержание · 17 разделов
  1. Что такое SciPy и как она связана с NumPy
  2. Установка SciPy
  3. Модули SciPy
  4. Оптимизация: scipy.optimize
  5. Минимум функции: minimize
  6. Глобальный минимум
  7. Корни уравнений
  8. Подбор параметров по данным: curve_fit
  9. Линейное программирование: linprog
  10. Интегралы и дифференциальные уравнения: scipy.integrate
  11. Определённый интеграл
  12. Обыкновенные дифференциальные уравнения: solve_ivp
  13. Интерполяция: scipy.interpolate
  14. Статистика: scipy.stats
  15. Распределения
  16. Сравнение двух выборок
  17. Корреляция и простая регрессия
  18. Линейная алгебра: scipy.linalg
  19. Сигналы и спектр: scipy.signal и scipy.fft
  20. Разреженные матрицы: scipy.sparse
  21. Пространственные данные: scipy.spatial
  22. Другие полезные модули
  23. Когда SciPy, а когда другая библиотека
  24. Ограничения SciPy
  25. Частые ошибки
  26. Частые вопросы
  27. Нужно ли устанавливать NumPy отдельно?
  28. Чем scipy.linalg отличается от numpy.linalg?
  29. Почему shapiro отвергает нормальность на большой выборке?
  30. Как прочитать файл .mat из MATLAB?
  31. Можно ли решить уравнение или взять интеграл в общем виде?
  32. С чего начать изучение SciPy?
  33. Итог

SciPy — библиотека Python с готовыми численными алгоритмами: поиск минимума и корней уравнений, интегралы и дифференциальные уравнения, интерполяция, статистические тесты, линейная алгебра, фильтрация сигналов, разреженные матрицы. Она работает поверх массивов NumPy: данные хранит NumPy, а SciPy их обрабатывает. Установка — pip install scipy, первый пример:

import numpy as np
from scipy import optimize, integrate

res = optimize.minimize_scalar(lambda x: (x - 3) ** 2 + 1)
print(round(res.x, 4), round(res.fun, 4))  # → 3.0 1.0 — минимум функции в точке x = 3

value, error = integrate.quad(np.sin, 0, np.pi)
print(value)  # → 2.0 — интеграл sin(x) от 0 до π

Ниже — установка, обзор модулей с рабочими примерами, выбор между SciPy и соседними библиотеками и ограничения. Примеры проверены на SciPy 1.18.1, NumPy 2.5 и Python 3.12.

Что такое SciPy и как она связана с NumPy

SciPy (Scientific Python) — открытая библиотека под лицензией BSD. Вычислительные части написаны на C, C++ и Cython и опираются на библиотеки линейной алгебры BLAS и LAPACK, а Python служит интерфейсом. Тяжёлые расчёты выполняются в скомпилированном коде, а не в интерпретаторе, поэтому SciPy не медленнее NumPy.

Разделение ролей простое:

  • NumPy даёт массив ndarray и базовые операции: арифметику, срезы, sum, mean, простую линейную алгебру и FFT.
  • SciPy принимает эти массивы и решает прикладные задачи: найти параметры модели, проверить гипотезу, решить систему уравнений, отфильтровать шум.

Почти каждая функция SciPy принимает и возвращает массивы NumPy, поэтому библиотеки используют вместе. Результаты удобно рисовать в Matplotlib, а таблицы с исходными данными готовить в pandas. Место SciPy среди других пакетов показано в обзоре библиотек Python.

Установка SciPy

Ставьте SciPy в виртуальное окружение проекта:

# Windows
py -m pip install scipy

# macOS / Linux
python3 -m pip install scipy

NumPy pip поставит автоматически как зависимость. В Anaconda и Miniconda SciPy уже есть или ставится командой conda install scipy.

Проверка:

import scipy

print(scipy.__version__)  # → 1.18.1 (у вас может быть другая версия)

SciPy 1.18 требует Python 3.12–3.14 и NumPy 2.0 или новее. На старом Python pip молча поставит последнюю совместимую версию SciPy, поэтому часть новых функций может отсутствовать. Если импорт падает с ModuleNotFoundError: No module named 'scipy', пакет установлен в другой интерпретатор — разбор причин в статье про ModuleNotFoundError.

Модули принято импортировать по отдельности:

from scipy import optimize, stats       # модули целиком
from scipy.stats import ttest_ind        # отдельная функция

Запись import scipy с последующим scipy.optimize.minimize(...) тоже работает: подмодули загружаются при первом обращении.

Модули SciPy

МодульДля чего
scipy.optimizeминимум функции, корни уравнений, подбор параметров, линейное программирование
scipy.integrateопределённые интегралы, обыкновенные дифференциальные уравнения
scipy.interpolateинтерполяция и сплайны
scipy.statsраспределения, описательная статистика, статистические тесты
scipy.linalgлинейная алгебра: системы, разложения, собственные значения
scipy.signalцифровые фильтры, поиск пиков, спектральный анализ
scipy.fftбыстрое преобразование Фурье
scipy.sparseразреженные матрицы и графы (scipy.sparse.csgraph)
scipy.spatialKD-деревья, расстояния, выпуклые оболочки, триангуляция
scipy.specialспециальные функции: гамма, бесселевы, сочетания
scipy.constantsфизические и математические константы
scipy.ndimageфильтры и морфология для многомерных изображений
scipy.ioфайлы MATLAB (.mat), WAV, Matrix Market
scipy.clusterk-средних и иерархическая кластеризация
scipy.differentiateчисленные производные

Полный список функций — в справочнике SciPy. Дальше разобраны модули, которые нужны чаще всего.

Оптимизация: scipy.optimize

Минимум функции: minimize

minimize() ищет минимум функции нескольких переменных. Функция должна принимать один аргумент — массив переменных, а x0 задаёт стартовую точку:

from scipy import optimize

def f(p):
    x, y = p
    return (x - 1) ** 2 + (y + 2) ** 2 + x * y

res = optimize.minimize(f, x0=[0, 0])
print(res.success, res.x.round(4), round(res.fun, 4))
# → True [ 2.6667 -3.3333] -4.3333

Результат — объект OptimizeResult: x — найденная точка, fun — значение функции, success и message — сошёлся ли алгоритм. Проверяйте success: при False найденная точка ненадёжна.

Ограничения на переменные задаёт bounds — пары «минимум, максимум», None означает отсутствие границы:

res = optimize.minimize(f, x0=[0, 0], bounds=[(0, None), (-1, 1)])
print(res.x.round(4), round(res.fun, 4))  # → [ 1.5 -1. ] -0.25

Для максимума минимизируйте функцию со знаком минус. Если известен градиент, передайте его в jac: алгоритм вызовет функцию в разы реже. Для тестовой функции Розенброка число вызовов падает с 231 до 54:

res = optimize.minimize(optimize.rosen, x0=[3, 0], jac=optimize.rosen_der)
print(res.x.round(4), res.nfev)  # → [1. 1.] 54

Метод выбирается параметром method. По умолчанию SciPy берёт BFGS для задачи без ограничений, L-BFGS-B — при bounds, SLSQP — при ограничениях-уравнениях и неравенствах (constraints). Для негладких функций подходит Nelder-Mead.

Глобальный минимум

minimize() находит локальный минимум — ближайший к стартовой точке. У функции с несколькими «ямами» ответ зависит от x0. Для глобального поиска есть differential_evolution, dual_annealing, shgo и basinhopping:

import numpy as np

def bumpy(x):
    return x[0] ** 2 + 10 * np.sin(3 * x[0])

local = optimize.minimize(bumpy, x0=[2])
best = optimize.differential_evolution(bumpy, bounds=[(-5, 5)], seed=1)
print(local.x.round(3), round(local.fun, 3))  # → [1.537] -7.586 — застрял в соседней яме
print(best.x.round(3), round(best.fun, 3))    # → [-0.512] -9.732 — глобальный минимум

Глобальным методам нужны конечные границы для каждой переменной, и работают они медленнее.

Корни уравнений

Корень одного уравнения на отрезке, где функция меняет знак, найдёт root_scalar. Систему нелинейных уравнений решает root (или старая fsolve):

sol = optimize.root_scalar(lambda x: x**3 - 2*x - 5, bracket=[2, 3])
print(sol.root)  # → 2.094551481542327

def equations(v):
    x, y = v
    return [x**2 + y**2 - 4, x - y - 1]   # окружность и прямая

sol = optimize.root(equations, x0=[1, 1])
print(sol.success, sol.x.round(4))  # → True [1.8229 0.8229]

У системы может быть несколько решений; какое из них вернётся, зависит от x0.

Подбор параметров по данным: curve_fit

curve_fit() подбирает параметры формулы под экспериментальные точки методом наименьших квадратов. Это ответ на частый вопрос «как аппроксимировать график в Python». Первый аргумент модели — независимая переменная, остальные — искомые параметры:

import numpy as np
from scipy import optimize

def model(t, a, k):
    return a * np.exp(-k * t)

t = np.array([0, 1, 2, 3, 4, 5])
y = np.array([10.1, 6.0, 3.7, 2.2, 1.4, 0.8])

params, cov = optimize.curve_fit(model, t, y, p0=[10, 0.5])
a, k = params
errors = np.sqrt(np.diag(cov))  # стандартные ошибки параметров
print(f"a = {a:.3f} ± {errors[0]:.3f}, k = {k:.3f} ± {errors[1]:.3f}")
# → a = 10.070 ± 0.052, k = 0.505 ± 0.005
print(model(6, a, k).round(2))  # → 0.49 — прогноз для t = 6

Для нелинейных моделей задавайте начальное приближение p0: со значениями по умолчанию (все единицы) алгоритм может не сойтись и выдать RuntimeError: Optimal parameters not found. Ограничить параметры можно через bounds=([нижние], [верхние]). Для обычного полинома проще np.polyfit из NumPy.

Линейное программирование: linprog

linprog() решает задачи вида «максимизировать прибыль при ограниченных ресурсах». Функция всегда минимизирует, поэтому коэффициенты цели для максимума берут со знаком минус. Ограничения записываются как A_ub @ x <= b_ub.

Пример: стол приносит 40 у. е., стул — 30. На стол уходит 2 часа сборки и 1 час покраски, на стул — по часу. Доступно 100 часов сборки и 80 часов покраски:

res = optimize.linprog(
    c=[-40, -30],                # максимизируем 40x + 30y
    A_ub=[[2, 1], [1, 1]],       # сборка, покраска
    b_ub=[100, 80],
    bounds=[(0, None), (0, None)],
)
print(res.x, -res.fun)  # → [20. 60.] 2600.0

Оптимум — 20 столов и 60 стульев. Если переменные должны быть целыми, используйте optimize.milp с параметром integrality.

Интегралы и дифференциальные уравнения: scipy.integrate

Определённый интеграл

quad() численно считает интеграл функции одной переменной. Пределы могут быть бесконечными (np.inf). Возвращается пара: значение и оценка абсолютной погрешности:

import numpy as np
from scipy import integrate

value, err = integrate.quad(lambda x: np.exp(-x**2), -np.inf, np.inf)
print(value)          # → 1.7724538509055159
print(np.sqrt(np.pi)) # → 1.7724538509055159 — точный ответ √π

value, err = integrate.quad(lambda x, a: a * x**2, 0, 3, args=(2,))
print(value)  # → 18.000000000000004

Дополнительные параметры функции передаются через args. Двойные и тройные интегралы считают dblquad и tplquad. Обратите внимание на порядок аргументов: внутренняя переменная идёт первой:

# ∫ от x=0 до 1 ∫ от y=0 до 2 x·y dy dx
value, err = integrate.dblquad(lambda y, x: x * y, 0, 1, 0, 2)
print(value)  # → 0.9999999999999999

Если функция задана не формулой, а таблицей измерений, интегрируйте по точкам: integrate.trapezoid (метод трапеций) или integrate.simpson (формула Симпсона):

x = np.linspace(0, np.pi, 101)
print(integrate.simpson(np.sin(x), x=x))  # → 2.0000000108245044

Обыкновенные дифференциальные уравнения: solve_ivp

solve_ivp() решает задачу Коши — систему dy/dt = f(t, y) с начальным условием. Для примера возьмём распад вещества: dy/dt = -k·y, в начале 100 единиц, k = 0.3:

def decay(t, y, k):
    return -k * y

sol = integrate.solve_ivp(
    decay, t_span=(0, 10), y0=[100], args=(0.3,),
    t_eval=[0, 2, 5, 10], rtol=1e-8,
)
print(sol.y[0].round(2))  # → [100.    54.88  22.31   4.98]

t_eval — моменты, в которые нужен ответ; sol.y — двумерный массив, по строке на каждую переменную. Без rtol=1e-8 решатель использует допуск 1e-3, и ответ расходится с точным во втором знаке (54.87 вместо 54.88). Для жёстких систем (химическая кинетика, электрические цепи) укажите method="Radau" или "BDF". Старая функция odeint работает, но для нового кода рекомендована solve_ivp.

Интерполяция: scipy.interpolate

Интерполяция восстанавливает значения между известными точками. Для линейной интерполяции хватает np.interp из NumPy, SciPy нужна для гладких кривых:

import numpy as np
from scipy import interpolate

hours = np.array([0, 3, 6, 9, 12])
temp = np.array([12.0, 10.5, 14.0, 19.5, 22.0])

print(np.interp(7.5, hours, temp))  # → 16.75 — линейно

spline = interpolate.CubicSpline(hours, temp)
print(spline([1, 7.5, 10]).round(2))  # → [10.79 16.78 20.94]
print(spline(7.5, 1).round(3))        # → 1.91 — первая производная в точке

pchip = interpolate.PchipInterpolator(hours, temp)
print(pchip([1, 7.5, 10]).round(2))   # → [11.02 16.86 20.58]

CubicSpline даёт гладкую кривую, но между точками может «перелетать» за пределы данных. PchipInterpolator сохраняет монотонность и не создаёт ложных пиков — удобно для физических величин, которые не могут быть отрицательными.

Важно: за пределами данных сплайн продолжает многочлен и быстро теряет смысл: spline(15) даёт 16.0, хотя температура росла. Чтобы получать nan вместо фантазий, передайте extrapolate=False.

В старых примерах встречаются interp1d и interp2d. interp1d помечена как устаревшая (legacy), а interp2d удалена в SciPy 1.14 — вызов выдаёт NotImplementedError. Для двумерных данных на сетке используйте RegularGridInterpolator или RectBivariateSpline, для разрозненных точек — griddata.

Статистика: scipy.stats

Распределения

Каждое распределение — объект с одинаковым набором методов: pdf (плотность), cdf (функция распределения), ppf (квантиль), sf (1 - cdf), rvs (случайная выборка). Параметры задаются через loc и scale:

from scipy import stats

print(stats.norm.ppf(0.975).round(4))  # → 1.96 — квантиль стандартного нормального

iq = stats.norm(loc=100, scale=15)     # IQ: среднее 100, стандартное отклонение 15
print(iq.sf(130).round(4))             # → 0.0228 — доля людей с IQ выше 130
print(iq.rvs(size=5, random_state=42).round(1))  # → [107.5  97.9 109.7 122.8  96.5]

print(stats.binom.pmf(3, n=10, p=0.5).round(4))  # → 0.1172 — ровно 3 орла из 10 бросков

Кроме norm и binom есть t, chi2, expon, poisson, uniform, lognorm и ещё около сотни распределений.

Сравнение двух выборок

Типичная задача — проверить, отличаются ли средние двух групп. Двухвыборочный t-тест Стьюдента:

import numpy as np
from scipy import stats

a = np.array([12.1, 11.8, 12.6, 13.0, 12.4, 11.9, 12.8, 12.2])  # группа А
b = np.array([12.9, 13.4, 12.7, 13.8, 13.1, 13.6, 12.8, 13.3])  # группа Б

res = stats.ttest_ind(a, b, equal_var=False)  # тест Уэлча — не требует равных дисперсий
print(round(res.statistic, 3), round(res.pvalue, 5))  # → -4.141 0.00101
print(res.confidence_interval(0.95))
# → ConfidenceInterval(low=np.float64(-1.2905942181867507), high=np.float64(-0.40940578181324855))

p-value — вероятность получить такую или большую разницу, если на самом деле группы не различаются. Значение 0,001 меньше привычного порога 0,05, поэтому различие считают статистически значимым. Доверительный интервал показывает, что среднее группы А ниже на 0,41–1,29.

Выбор теста зависит от данных:

СитуацияФункция
Две независимые группы, примерно нормальные данныеttest_ind(a, b, equal_var=False)
Одни и те же объекты до и послеttest_rel(before, after)
Распределение далеко от нормального, выбросыmannwhitneyu(a, b)
Три и больше группf_oneway(a, b, c), kruskal(a, b, c)
Таблица сопряжённости (категории)chi2_contingency(table)
Проверка нормальностиshapiro(x), normaltest(x)
print(stats.shapiro(a).pvalue.round(3))  # → 0.828 — нет оснований отвергать нормальность

Корреляция и простая регрессия

x = np.array([1, 2, 3, 4, 5, 6, 7, 8])
y = np.array([2.1, 3.9, 6.2, 7.8, 10.1, 12.2, 13.8, 16.1])

r = stats.pearsonr(x, y)
print(round(r.statistic, 4))  # → 0.9994 — коэффициент Пирсона

lr = stats.linregress(x, y)
print(round(lr.slope, 3), round(lr.intercept, 3))  # → 1.998 0.036 — y ≈ 1.998x + 0.036

Для ранговой корреляции есть spearmanr и kendalltau, для описательной статистики одной строкой — stats.describe(x) (размер, минимум и максимум, среднее, дисперсия, асимметрия, эксцесс).

Линейная алгебра: scipy.linalg

Система линейных уравнений A·x = b решается через solve. Обращать матрицу через inv для этого не нужно: так медленнее и менее точно.

import numpy as np
from scipy import linalg

A = np.array([[3, 2, -1], [2, -2, 4], [-1, 0.5, -1]])
b = np.array([1, -2, 0])

x = linalg.solve(A, b)
print(x)                      # → [ 1. -2. -2.]
print(np.allclose(A @ x, b))  # → True — проверка
print(linalg.det(A).round(4)) # → -3.0

Для вырожденной матрицы solve выбрасывает LinAlgError. Другие частые функции: eig и eigh (собственные значения; eigh — для симметричных матриц), lu, qr, cholesky, svd (разложения), expm (матричная экспонента).

В NumPy тоже есть модуль numpy.linalg. Простые операции дают одинаковый результат, но scipy.linalg шире: больше разложений, матричные функции (expm, sqrtm, logm) и подсказки о структуре матрицы. Например, linalg.solve(A, b, assume_a="pos") для симметричной положительно определённой матрицы работает быстрее общего случая. Если одну матрицу нужно решать с разными правыми частями, разложите её один раз через lu_factor и вызывайте lu_solve.

Сигналы и спектр: scipy.signal и scipy.fft

Пример: синусоида 5 Гц, на которую наложены помеха 50 Гц (сетевая наводка) и случайный шум. Найдём частоты в спектре и уберём помеху фильтром нижних частот Баттерворта:

import numpy as np
from scipy import signal, fft

fs = 500                     # частота дискретизации, Гц
t = np.arange(0, 1, 1 / fs)  # 1 секунда
rng = np.random.default_rng(0)
clean = np.sin(2 * np.pi * 5 * t)
noisy = clean + 0.5 * np.sin(2 * np.pi * 50 * t) + 0.2 * rng.standard_normal(t.size)

# Какие частоты есть в сигнале
spectrum = np.abs(fft.rfft(noisy))
freqs = fft.rfftfreq(noisy.size, d=1 / fs)
print(sorted(freqs[np.argsort(spectrum)[-2:]].tolist()))  # → [5.0, 50.0]

# Фильтр нижних частот 4-го порядка со срезом 15 Гц
sos = signal.butter(4, 15, btype="low", fs=fs, output="sos")
filtered = signal.sosfiltfilt(sos, noisy)
print(np.abs(noisy - clean).mean().round(3))     # → 0.35  — ошибка до фильтра
print(np.abs(filtered - clean).mean().round(3))  # → 0.037 — после

peaks, _ = signal.find_peaks(filtered, height=0.5)
print((peaks / fs).round(3))  # → [0.054 0.25  0.446 0.65  0.854] — вершины синусоиды, с

sosfiltfilt проходит сигнал вперёд и назад, поэтому не сдвигает его по времени. Формат output="sos" численно устойчивее пары коэффициентов b, a, особенно для фильтров высокого порядка. В scipy.signal также есть spectrogram, welch (спектральная плотность), savgol_filter (сглаживание), resample и convolve.

Для преобразования Фурье используйте scipy.fft. Модуль scipy.fftpack из старых статей считается устаревшим и обновлений не получает.

Разреженные матрицы: scipy.sparse

Разреженная матрица хранит только ненулевые элементы. Это нужно, когда матрица огромная, а почти все её элементы — нули: графы, тексты в формате «мешка слов», сетки в методе конечных элементов.

import numpy as np
from scipy import sparse

dense = np.array([[0, 0, 3], [4, 0, 0], [0, 5, 0]])
m = sparse.csr_array(dense)
print(m.nnz)                     # → 3 — хранится 3 числа вместо 9
print(m @ np.array([1, 2, 3]))   # → [ 9  4 10]
print(m.toarray())               # обратно в обычный массив

Разница в памяти на больших размерах:

eye = sparse.eye_array(10_000, format="csr")
print(eye.data.nbytes + eye.indices.nbytes + eye.indptr.nbytes)  # → 160004 байт, около 156 КБ
# та же матрица в NumPy (float64): 10 000 × 10 000 × 8 байт ≈ 763 МБ

Системы с разреженной матрицей решает scipy.sparse.linalg.spsolve, а алгоритмы на графах (кратчайшие пути, компоненты связности) — scipy.sparse.csgraph:

from scipy.sparse import linalg as splinalg

n = 100_000
main = np.full(n, 4.0)
off = np.full(n - 1, -1.0)
A = sparse.diags_array([off, main, off], offsets=[-1, 0, 1], format="csc")
x = splinalg.spsolve(A, np.ones(n))
print(x[:3].round(4))  # → [0.366  0.4641 0.4904]

Формат CSR быстр для умножения и выборки строк, CSC — для столбцов и решателей, COO и LIL — для пошагового построения матрицы. Для нового кода выбирайте классы *_array (csr_array, coo_array): старые *_matrix работают, но ведут себя как устаревший np.matrix, где * означает матричное умножение.

Пространственные данные: scipy.spatial

KDTree быстро ищет ближайших соседей среди тысяч и миллионов точек:

import numpy as np
from scipy.spatial import KDTree, distance, ConvexHull

shops = np.array([[0, 0], [5, 1], [2, 7], [8, 8], [6, 4]])
tree = KDTree(shops)

dist, idx = tree.query([5, 5])     # ближайший магазин к точке (5, 5)
print(round(dist, 3), shops[idx])  # → 1.414 [6 4]

print(tree.query_ball_point([5, 5], r=3))  # → [4] — все в радиусе 3

print(distance.cdist([[0, 0]], shops).round(2))  # матрица расстояний
# → [[ 0.    5.1   7.28 11.31  7.21]]

hull = ConvexHull(shops)
print(hull.vertices, hull.volume)  # → [0 1 3 2] 36.0

В distance есть метрики euclidean, cityblock (манхэттенская), cosine и другие. У двумерной ConvexHull атрибут volume означает площадь, а area — периметр. Также в модуле есть триангуляция Делоне (Delaunay), диаграммы Вороного (Voronoi) и повороты в 3D (spatial.transform.Rotation). Широта и долгота заданы в градусах, поэтому евклидово расстояние между ними не равно расстоянию в километрах: для городов нужна формула гаверсинуса или библиотека геодезии.

Другие полезные модули

  • scipy.special — специальные функции: special.comb(10, 3, exact=True) → 120 (сочетания), gamma, beta, бесселевы функции, expit (сигмоида), logsumexp (устойчивая сумма экспонент).
  • scipy.constants — физические константы в СИ: constants.c → 299792458.0, constants.g → 9.80665; convert_temperature(100, "Celsius", "Kelvin") → 373.15.
  • scipy.ndimage — фильтры, морфология, поворот и масштабирование массивов изображений любой размерности.
  • scipy.io — loadmat и savemat для файлов MATLAB, wavfile.read и wavfile.write для WAV.
  • scipy.cluster — kmeans и иерархическая кластеризация (cluster.hierarchy).

Когда SciPy, а когда другая библиотека

SciPy закрывает численные методы, но не всё, что связано с данными и математикой:

ЗадачаИнструмент
Массивы, арифметика, средние, простая линейная алгебра и FFTNumPy
Таблицы с подписями, чтение CSV и Excel, группировкиpandas
Оптимизация, интегралы, ОДУ, интерполяция, фильтры, тестыSciPy
Регрессия с полной таблицей коэффициентов, ANOVA, временные ряды (ARIMA)statsmodels
Классификация, кластеризация, модели с обучением и валидациейscikit-learn
Символьная математика: точные интегралы, производные, упрощение формулSymPy
Нейросети, расчёты на GPUPyTorch, JAX

Эти инструменты дополняют друг друга. pandas вызывает SciPy для интерполяции сплайнами, scikit-learn использует SciPy как обязательную зависимость. Обычный порядок: данные читают и чистят в pandas, считают в NumPy и SciPy, модели обучают в scikit-learn — подробнее в статье про машинное обучение на Python.

Ограничения SciPy

  • Только численные ответы. SciPy вернёт 2.0 для интеграла sin(x) от 0 до π, но не формулу -cos(x). Аналитические решения — в SymPy.
  • Локальные методы. minimize, root, curve_fit находят ближайшее к стартовой точке решение. Результат зависит от x0 и p0, а success=False нужно проверять.
  • Функции-обратные вызовы на Python. Функцию, которую вы передаёте в quad, minimize или solve_ivp, SciPy вызывает сотни и тысячи раз из Python. Медленная функция с циклами замедлит весь расчёт; помогает векторизация на NumPy, передача градиента jac или компиляция через Numba.
  • Скупой отчёт статистики. Тесты возвращают статистику и p-value, без сводных таблиц. Для отчёта по регрессии с коэффициентами, ошибками и R² удобнее statsmodels.
  • Расчёты на CPU. Поддержка массивов CuPy, PyTorch и JAX появляется постепенно и пока охватывает лишь часть функций; это экспериментальный режим.
  • Устаревшие и удалённые API. В старых примерах встречаются scipy.misc.imread (удалена, используйте Pillow или imageio), integrate.simps (заменена на simpson), interp2d (удалена), scipy.fftpack (legacy). Модуль scipy.odr объявлен устаревшим в 1.17 и будет удалён в 1.19, замена — пакет odrpack.

Частые ошибки

  • ImportError: cannot import name 'simps' from 'scipy.integrate' — функцию переименовали, используйте integrate.simpson. Аналогично trapz → trapezoid, scipy.misc.comb → scipy.special.comb.
  • RuntimeError: Optimal parameters not found в curve_fit — задайте p0, близкий к правде, ограничьте параметры через bounds или увеличьте maxfev.
  • OptimizeWarning: Covariance of the parameters could not be estimated — параметров больше, чем позволяют данные, или они взаимозависимы; ошибкам параметров из cov верить нельзя.
  • Функция для minimize объявлена как f(x, y) — нужно f(p) с одним аргументом-массивом. Дополнительные постоянные параметры передавайте через args.
  • LinAlgError в solve — матрица вырождена: уравнения системы линейно зависимы.

Частые вопросы

Нужно ли устанавливать NumPy отдельно?

Нет. NumPy — обязательная зависимость SciPy, pip поставит её автоматически. Импортировать NumPy в коде всё равно придётся, если вы создаёте массивы сами.

Чем scipy.linalg отличается от numpy.linalg?

Базовые функции (solve, det, inv, eig) совпадают по смыслу. В scipy.linalg больше разложений и матричных функций, есть параметры для учёта структуры матрицы. Если SciPy уже в проекте, для линейной алгебры обычно берут её.

Почему shapiro отвергает нормальность на большой выборке?

На тысячах наблюдений тест замечает даже ничтожные отклонения от нормального распределения, которые не мешают t-тесту. При N больше 5000 SciPy ещё и предупреждает, что p-value может быть неточным. Для больших выборок смотрите на гистограмму и график квантилей (Q-Q plot), а не только на p-value.

Как прочитать файл .mat из MATLAB?

scipy.io.loadmat("file.mat") возвращает словарь: ключи — имена переменных, значения — массивы NumPy. Числа приходят двумерными массивами, например [[500]]; одно значение извлекают через .item(), вектор — через .ravel(). Файлы формата MATLAB 7.3 — это HDF5, их читают через h5py.

Можно ли решить уравнение или взять интеграл в общем виде?

SciPy работает с числами: она найдёт корень 2.0946 или значение интеграла, но не формулу. Для символьных решений — разложения, производных, первообразных — используйте SymPy, а затем при необходимости переведите формулу в численную функцию для SciPy.

С чего начать изучение SciPy?

Освойте массивы NumPy, затем модуль под вашу задачу: stats для анализа данных, optimize для подбора параметров, integrate для моделирования, signal для датчиков и звука. Изучать все модули подряд не нужно.

Итог

  • SciPy — набор численных алгоритмов поверх массивов NumPy; ставится через pip install scipy, NumPy подтягивается сам.
  • Главные модули: optimize (минимум, корни, curve_fit, linprog), integrate (quad, solve_ivp), interpolate (CubicSpline, PchipInterpolator), stats (распределения и тесты), linalg, signal и fft, sparse, spatial.
  • Проверяйте success у оптимизаторов, задавайте x0 и p0 осмысленно, для глобального минимума берите differential_evolution.
  • Для таблиц нужна pandas, для отчётов по регрессии — statsmodels, для машинного обучения — scikit-learn, для формул — SymPy.
  • Старые примеры с fftpack, interp2d, simps и scipy.misc переписывайте на актуальные функции.
  • #Python
  • #SciPy
  • #NumPy
  • #Научные вычисления
  • #Анализ данных
↑ ↓ выборEnter открытьEsc закрыть