Раньше мы уже искали необычные модели Playboy с помощью библиотеки Python Scikit-learn. Теперь мы продемонстрируем некоторые возможности библиотек SymPy, SciPy и Matplotlib на живом примере из разряда занимательных школьных задач по математике.
Стоит девушка с изящными, натренированными, а главное, оголенными ножками. Скучает. Перед тем как демонстрировать свой (n+1)-ый уровень владения техникой пикапа, хочется получше рассмотреть ножки девушки - а стоит ли овчинка выделки? Получше рассмотреть - это под наибольшим углом. Можно незаметно подходить к девушке (типа вдаль смотришь), но приседать нельзя - надо же как-то и приличия соблюдать. С какого расстояния ножки видны под наибольшим углом? Допустим, Ваш рост таков, что глаза находятся на высоте $m$ над поверхностью земли. Ноги девушки оголены до высоты $f$.
Картинка и перефразированная задача из "Кристоф Дрессер: Обольстить математикой. Числовые игры на все случаи жизни. Бином. Лаборатория знаний, 2015"
Поясним проблему. Издалека плохо рассматривать ноги - они видны под слишком малым углом. Но и если подойти слишком близко, ноги тоже будут видны под малым углом. Где-то должно быть оптимальное расстояние.
Пусть $x$ - расстояние до девушки, $f$ - длина оголенной части ног девушки, $\alpha$ - угол, под которым ноги видны (надо максимизировать).
Угол $\alpha$ проще всего найти, вычитая из прямого угла углы $\beta$ и $\gamma$. Если школьная тригонометрия еще как-то жива в закоулках мозга, легко получим, что
Задача сводится к максимизации $\alpha$ по переменной $x$. Ну это тоже просто, скажем мы: зануляем производную - и вперед!
Для начала построим график функции $\alpha(x)$. Для определенности возьмем значения переметров $m=1.7\ м$ и $f=0.7\ м$ (хотелось бы 1 м, но все же предполагается, что имеется некая юбка.)
# отключим лишние предупреждения Anaconda
import warnings
warnings.filterwarnings('ignore')
%pylab inline
import numpy as np
from math import pi, atan
def alpha(x, m, f):
return pi/2 - atan(x/m) - atan((m-f)/x)
x = np.arange(0,6,0.05)
plot(x, [alpha(i, 1.7, 0.7) for i in x])
Итак, действительно, где-то в 1-1.5 м от девушки ее ноги видны под наибольшим углом. Ну... это уже сложно без палева. Давайте теперь найдем точное значение оптимального расстояния до девушки.
Решение достаточно простое, всего-то надо вспомнить, что производная арктангенса
Зафиксируем $m=1.7\ м$ и $f=0.7\ м$. Тогда $\alpha$ = $\frac{\pi}{2} - arctg(\frac{x}{1.7}) - arctg(\frac{1}{x})$
После несложных преобразований получается $\alpha'(x) = \frac{(x^2 + 1.7^2) - 1.7(x^2+1)}{(x^2+1)(x^2 + 1.7^2)}$
И тогда максимум $\alpha(x)$ достигается, когда
SymPy - это библиотека символьных вычислений на языке Python. Мы рассмотрим, как с ее помощью вычислять производные (метод diff) и находить корни уравнений.
import sympy as sym
Заведем символьную переменную $x$ и функцию $\alpha(x)$. Для этого $\pi$ и $arctg$ тоже надо взять из SymPy.
x = sym.Symbol('x')
alpha = sym.pi/2 - sym.atan(x/1.7) - sym.atan(1/x)
alpha
Посчитаем производную $\alpha'(x)$. Методу diff надо указать функцию, переменную, по которой происходит дифференцирование, и порядок производной, в данном случае 1.
alpha_deriv = sym.diff(alpha, x, 1)
alpha_deriv
Как видно, к общему знаменателю SymPy просто так выражения не приводит. Для этого есть метод simplify.
sym.simplify(alpha_deriv)
Теперь найдем нули производной с помощью метода solve.
sym.solve(alpha_deriv, x)
Опять получили ответ, что лучше всего рассматривать девушку $\approx$ с 1.3 м.
Метода maximize как такового нет, поэтому задача максимизации будет эмулироваться минимизацией функции, домноженной на (-1). Рассмотрим самый простой случай - минимация скалярной функции одной переменной. Реализованы методы оптимизации 'brent', 'bounded' и 'golden', но отличия почему-то толком не документированы.
from scipy.optimize import minimize_scalar
alpha = lambda x: -(pi/2 - atan(x/1.7) - atan(1/x))
result = minimize_scalar(alpha, bounds=[0., 100.], method = 'bounded')
result
Ответ прежний, как и ожидалось.
result.x
Теперь выберем девушку, на чьи ноги будем любоваться. Вернемся к знакомому набору данных girls.csv по моделям месяца по Playboy.
Найти среди моделей Playboy девушку с самым высоким ростом при "нормальном" индексе массы тела - от 18 до 18.5.
import pandas as pd
girls = pd.read_csv('data/girls.csv')
girls.head()
Создадим новый признак BMI - индекс массы тела, равный весу, деленному на рост в метрах в квадрате
girls['BMI'] = 100 ** 2 * girls['Weight'] / (girls['Height'] ** 2)
girls['BMI'].head()
Построим гистограмму распределения BMI
girls['BMI'].hist()
Википедия говорит, что нормальный индекс BMI - 18,5—24,99. Видим, что средний индекс у моделей Playboy примерно на нижней границе нормы. Отберем девушек с BMI от 18 до 18.5.
selected_girls = girls[(girls['BMI'] >= 18) &
(girls['BMI'] <= 18.5)]
selected_girls.sort(columns=['Height', 'Bust'],
ascending=[False, False]).head(1)
Это Miss July 1994 Traci Adell. Дальше поисковик в помощь. Такой выбор вряд ли разочарует. Вот самое "приличное", что можно найти.
Мы посмотрели самые основы использования библиотек Python SymPy, SciPy и Pandas. Обилие примеров уже реального использования этих библиотек можно найти в репозиториях GitHub. Один из обзоров таких репозиториев тут.