Семестр 3 · Модуль 3 · Неделя 8

SciPy: численные расчёты и честная визуализация

quad · odeint/solve_ivp · minimize · curve_fit · interp1d · scipy.stats · принципы честной визуализации

2 ч теории. Данные — reaction.csv (25 точек «эксперимента»). После лекции — live-coding и задачи семинара.

Прежде чем начать: подумайте, как бы вы «проверили закон» по точкам с шумом — например, зависимость скорости реакции от температуры.

Подбор кривой — как физики проверяют законы

точки «эксперимента» reaction.csv и подобранная модель A·exp(-λt)+c (результат — см. ниже)
Что за «модель» вы бы выбрали для данных, которые сначала быстро падают, потом выходят на плато?

scipy.integrate.quad: определённый интеграл

from scipy.integrate import quad from scipy.stats import norm quad(lambda x: x**2, 0, 1) (0.33333333333333337, 3.7e-15) quad(norm.pdf, -np.inf, np.inf) (0.9999999999999998, 1.0e-08)
Как проверить, что quad посчитал правильно, если аналитического ответа нет?

odeint / solve_ivp: решение дифференциальных уравнений

from scipy.integrate import odeint def decay(y, t, k): return -k * y # y' = -k·y t = np.linspace(0, 10, 50) sol = odeint(decay, 5.0, t, args=(0.3,)) sol[[0, 25, -1], 0] [5.0 1.082 0.249]
Что изменится в решении, если в модели поставить +k·y? Когда это реалистично?

scipy.optimize.minimize: поиск минимума

from scipy.optimize import minimize, rosen minimize(rosen, x0=[0, 0], method='BFGS') fun: 2.84e-11 x: [1.0 1.0]
Что произойдёт, если стартовать minimize из точки (0, 0), а не из окрестности минимума?

curve_fit: подбор параметров модели к данным

from scipy.optimize import curve_fit def model(t, A, lam, c): return A * np.exp(-lam * t) + c popt, pcov = curve_fit(model, x, y, p0=[5.0, 0.3, 0.5]) perr = np.sqrt(np.diag(pcov)) resid = y - model(x, *popt) rmse = np.sqrt(np.mean(resid**2)) A = 4.94 ± 0.10 λ = 0.30 ± 0.02 c = 0.53 ± 0.09 RMSE = 0.121
данные + модель (слева), остатки (справа)
Зачем показывать остатки, если модель «красиво легла» на данные?

Разумный p0: почему без него curve_fit «застревает»

curve_fit(model, x, y, p0=[1.0, 1.0, 1.0]) A = 4.94, λ = 0.30, c = 0.53 RMSE = 0.121 (хорошо) curve_fit(model, x, y, p0=[0.0, 0.0, 0.0]) A = -19773, λ ≈ 0.0, c = 19777 RMSE = 0.503 (застрял)
Как оценить p0 для модели затухания, не зная истинных параметров? Что подсказывает форма данных?

interp1d: интерполяция по узлам

from scipy.interpolate import interp1d f_lin = interp1d(xn, yn, kind='linear') f_cub = interp1d(xn, yn, kind='cubic') f_lin(2.5); f_cub(2.5) 1.35 1.44
узлы, линейная и кубическая интерполяция; пунктир — опасная экстраполяция
Когда нужен interp1d, а когда curve_fit? Что «знает» интерполятор о физике вашей задачи?

Экстраполяция: правдоподобный «мусор»

f_lin(6.5) # вне диапазона узлов ValueError: A value (6.5) in x_new is above the interpolation range's maximum value (5). interp1d(xn, yn, kind='linear', fill_value='extrapolate')(6.5) -0.3 # отрицательное при положительных данных — «мусор»
Почему экстраполяция линейным интерполятором дала −0.3, хотя все данные положительные?

scipy.stats: описательная статистика и тесты

from scipy.stats import ttest_ind, pearsonr ttest_ind(ages_male, ages_female) TtestResult(statistic=-1.118, pvalue=0.271) pearsonr(df['Fare'], df['Survived']) (-0.218, 0.176)
Что значит «p = 0.27» простыми словами? Можно ли по этим данным утверждать, что пол влияет на возраст?

Честная визуализация: масштаб осей

одинаковые данные: слева «взрыв продаж», справа честный рост
  • Слева ось Y обрезана (118–128): рост ~2% выглядит как «рывок в 3 раза».
  • Справа ось с нуля: виден скромный, но честный рост.
  • Правила: бары с нуля; не обрезать ось без маркера разрыва (//); подписывать единицы; не прятать выбросы.
В каких отраслях обрезка оси Y — обычная практика «продажи продают»? Почему это этическая проблема для аналитика?

Ещё манипуляции: 3D-эффекты, размер маркера, базы

слева — плоская bar-диаграмма, справа — «объём» (та же высота, разная масса)
  • 3D/объём у bar-диаграмм искажает восприятие (верх столбца читается по-разному).
  • Размер маркера должен быть пропорционален данным, а не «ради красоты».
  • Честные проценты: всегда уточняйте базу («вырос на 50%» — от чего?).
  • Не скрывайте выбросы и «нулевые» значения.
Почему «объёмный» столбец в 8 раз «тяжелее» плоского, хотя высота та же? Как это обманывает глаз?

Типичные ошибки недели 8

  1. curve_fit без разумного p0 — застревание в локальном минимуме, параметры «мусорные» (RMSE большой).
  2. Экстраполяция interp1d за пределы данных — правдоподобные, но бессмысленные значения.
  3. Путаница «подобрать параметры» (curve_fit) и «интерполировать» (interp1d).
  4. Полином высокой степени «для красоты» — переобучение, кривая скачет.
  5. quad вызывают с векторной функцией без понимания, что подынтегральная функция должна быть скалярной.
  6. Остатки не показаны — нельзя оценить, «врёт» ли модель систематически.
  7. Вывод «корреляция → причинность» без оговорок.
Студент подобрал полином 5-й степени, и он «идеально» прошёл через все точки. Почему это плохая модель?

Вопросы для проверки понимания

  1. Что возвращает quad и как проверить точность?
  2. Как подобрать параметры экспоненциальной модели к данным с шумом?
  3. Чем интерполяция отличается от экстраполяции и почему последняя опасна?
  4. Какой график «вводит в заблуждение» масштабом и почему?
  5. Чем ttest_ind отличается от pearsonr?

Ответьте письменно за 3 минуты — это мини-самооценка перед семинаром.

Переход: на семинаре вы посчитаете интегралы, решите ОДУ, подберёте кривую к reaction.csv и «почините» манипулирующий график.

Что дальше

Готовы? Откройте семинар-8.md, возьмите reaction.csv и подберите первую модель.