SciPy: научные вычисления в Python
Библиотека SciPy в Python: установка, связь с NumPy, оптимизация, интегралы, интерполяция, статистика, линейная алгебра, сигналы и разреженные матрицы.
Содержание · 17 разделов
- Что такое SciPy и как она связана с NumPy
- Установка SciPy
- Модули SciPy
- Оптимизация: scipy.optimize
- Минимум функции: minimize
- Глобальный минимум
- Корни уравнений
- Подбор параметров по данным: curve_fit
- Линейное программирование: linprog
- Интегралы и дифференциальные уравнения: scipy.integrate
- Определённый интеграл
- Обыкновенные дифференциальные уравнения: solve_ivp
- Интерполяция: scipy.interpolate
- Статистика: scipy.stats
- Распределения
- Сравнение двух выборок
- Корреляция и простая регрессия
- Линейная алгебра: scipy.linalg
- Сигналы и спектр: scipy.signal и scipy.fft
- Разреженные матрицы: scipy.sparse
- Пространственные данные: scipy.spatial
- Другие полезные модули
- Когда SciPy, а когда другая библиотека
- Ограничения SciPy
- Частые ошибки
- Частые вопросы
- Нужно ли устанавливать NumPy отдельно?
- Чем scipy.linalg отличается от numpy.linalg?
- Почему shapiro отвергает нормальность на большой выборке?
- Как прочитать файл .mat из MATLAB?
- Можно ли решить уравнение или взять интеграл в общем виде?
- С чего начать изучение SciPy?
- Итог
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.spatial | KD-деревья, расстояния, выпуклые оболочки, триангуляция |
scipy.special | специальные функции: гамма, бесселевы, сочетания |
scipy.constants | физические и математические константы |
scipy.ndimage | фильтры и морфология для многомерных изображений |
scipy.io | файлы MATLAB (.mat), WAV, Matrix Market |
scipy.cluster | k-средних и иерархическая кластеризация |
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 закрывает численные методы, но не всё, что связано с данными и математикой:
| Задача | Инструмент |
|---|---|
| Массивы, арифметика, средние, простая линейная алгебра и FFT | NumPy |
| Таблицы с подписями, чтение CSV и Excel, группировки | pandas |
| Оптимизация, интегралы, ОДУ, интерполяция, фильтры, тесты | SciPy |
| Регрессия с полной таблицей коэффициентов, ANOVA, временные ряды (ARIMA) | statsmodels |
| Классификация, кластеризация, модели с обучением и валидацией | scikit-learn |
| Символьная математика: точные интегралы, производные, упрощение формул | SymPy |
| Нейросети, расчёты на GPU | PyTorch, 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
- #Научные вычисления
- #Анализ данных