<jupyter_start><jupyter_text>Линейная регрессия и основные библиотеки Python для анализа данных и научных вычислений Это задание посвящено линейной регрессии. На примере прогнозирования роста человека по его весу Вы увидите, какая математика за этим стоит, а заодно познакомитесь с основными библиотеками Python, необходимыми для дальнейшего прохождения курса. **Материалы**- Лекции данного курса по линейным моделям и градиентному спуску- [Документация](http://docs.scipy.org/doc/) по библиотекам NumPy и SciPy- [Документация](http://matplotlib.org/) по библиотеке Matplotlib - [Документация](http://pandas.pydata.org/pandas-docs/stable/tutorials.html) по библиотеке Pandas- [Pandas Cheat Sheet](http://www.analyticsvidhya.com/blog/2015/07/11-steps-perform-data-analysis-pandas-python/)- [Документация](http://stanford.edu/~mwaskom/software/seaborn/) по библиотеке Seaborn   Задание 1. Первичный анализ данных c Pandas В этом заданиии мы будем использовать данные [SOCR](http://wiki.stat.ucla.edu/socr/index.php/SOCR_Data_Dinov_020108_HeightsWeights) по росту и весу 25 тысяч подростков. **[1].** Если у Вас не установлена библиотека Seaborn - выполните в терминале команду *conda install seaborn*. (Seaborn не входит в сборку Anaconda, но эта библиотека предоставляет удобную высокоуровневую функциональность для визуализации данных).<jupyter_code>import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Считаем данные по росту и весу (*weights_heights.csv*, приложенный в задании) в объект Pandas DataFrame:<jupyter_code>data = pd.read_csv('weights_heights.csv', index_col='Index')<jupyter_output><empty_output><jupyter_text>Чаще всего первое, что надо надо сделать после считывания данных - это посмотреть на первые несколько записей. Так можно отловить ошибки чтения данных (например, если вместо 10 столбцов получился один, в названии которого 9 точек с запятой). Также это позволяет познакомиться с данными, как минимум, посмотреть на признаки и их природу (количественный, категориальный и т.д.). После этого стоит построить гистограммы распределения признаков - это опять-таки позволяет понять природу признака (степенное у него распределение, или нормальное, или какое-то еще). Также благодаря гистограмме можно найти какие-то значения, сильно не похожие на другие - "выбросы" в данных. Гистограммы удобно строить методом *plot* Pandas DataFrame с аргументом *kind='hist'*.**Пример.** Построим гистограмму распределения роста подростков из выборки *data*. Используем метод *plot* для DataFrame *data* c аргументами *y='Height'* (это тот признак, распределение которого мы строим)<jupyter_code>data.plot(y='Height', kind='hist', 
           color='red',  title='Height (inch.) distribution')<jupyter_output><empty_output><jupyter_text>Аргументы:- *y='Height'* - тот признак, распределение которого мы строим- *kind='hist'* - означает, что строится гистограмма- *color='red'* - цвет **[2]**. Посмотрите на первые 5 записей с помощью метода *head* Pandas DataFrame. Нарисуйте гистограмму распределения веса с помощью метода *plot* Pandas DataFrame. Сделайте гистограмму зеленой, подпишите картинку.<jupyter_code>data.head(5)
data.plot(y='Weight', kind='hist', color='green',  title='Weight (inch.) distribution')<jupyter_output><empty_output><jupyter_text>Один из эффективных методов первичного анализа данных - отображение попарных зависимостей признаков. Создается $m \times m$ графиков (*m* - число признаков), где по диагонали рисуются гистограммы распределения признаков, а вне диагонали - scatter plots зависимости двух признаков. Это можно делать с помощью метода $scatter\_matrix$ Pandas Data Frame или *pairplot* библиотеки Seaborn. Чтобы проиллюстрировать этот метод, интересней добавить третий признак. Создадим признак *Индекс массы тела* ([BMI](https://en.wikipedia.org/wiki/Body_mass_index)). Для этого воспользуемся удобной связкой метода *apply* Pandas DataFrame и lambda-функций Python.<jupyter_code>def make_bmi(height_inch, weight_pound):
    METER_TO_INCH, KILO_TO_POUND = 39.37, 2.20462
    return (weight_pound / KILO_TO_POUND) / \
           (height_inch / METER_TO_INCH) ** 2
data['BMI'] = data.apply(lambda row: make_bmi(row['Height'], 
                                              row['Weight']), axis=1)<jupyter_output><empty_output><jupyter_text>**[3].** Постройте картинку, на которой будут отображены попарные зависимости признаков , 'Height', 'Weight' и 'BMI' друг от друга. Используйте метод *pairplot* библиотеки Seaborn.<jupyter_code>sns.pairplot(data);<jupyter_output><empty_output><jupyter_text>Часто при первичном анализе данных надо исследовать зависимость какого-то количественного признака от категориального (скажем, зарплаты от пола сотрудника). В этом помогут "ящики с усами" - boxplots библиотеки Seaborn. Box plot - это компактный способ показать статистики вещественного признака (среднее и квартили) по разным значениям категориального признака. Также помогает отслеживать "выбросы" - наблюдения, в которых значение данного вещественного признака сильно отличается от других.  **[4]**. Создайте в DataFrame *data* новый признак *weight_category*, который будет иметь 3 значения: 1 – если вес меньше 120 фунтов. (~ 54 кг.), 3 - если вес  больше или равен 150 фунтов (~68 кг.), 2 – в остальных случаях. Постройте «ящик с усами» (boxplot), демонстрирующий зависимость роста от весовой категории. Используйте метод *boxplot* библиотеки Seaborn и метод *apply* Pandas DataFrame. Подпишите ось *y* меткой «Рост», ось *x* – меткой «Весовая категория».<jupyter_code>def weight_category(weight):
    if weight<120:
        return 1
    elif weight>=150:
        return 3
    else:
        return 2

data['weight_cat'] = data['Weight'].apply(weight_category)
sns.boxplot(x=data['weight_cat'],y=data['Height']);
ax.set_xlabel(u"Весовая категория", fontsize="x-large")
ax.set_ylabel(u"Рост", fontsize="x-large")<jupyter_output><empty_output><jupyter_text>**[5].** Постройте scatter plot зависимости роста от веса, используя метод *plot* для Pandas DataFrame с аргументом *kind='scatter'*. Подпишите картинку.<jupyter_code>data.plot(x='Weight', y = 'Height', kind='scatter', title='Correlation between weight and height')<jupyter_output><empty_output><jupyter_text>Задание 2. Минимизация квадратичной ошибки В простейшей постановке задача прогноза значения вещественного признака по прочим признакам (задача восстановления регрессии) решается минимизацией квадратичной функции ошибки. **[6].** Напишите функцию, которая по двум параметрам $w_0$ и $w_1$ вычисляет квадратичную ошибку приближения зависимости роста $y$ от веса $x$ прямой линией $y = w_0 + w_1 * x$:$$error(w_0, w_1) = \sum_{i=1}^n {(y_i - (w_0 + w_1 * x_i))}^2 $$Здесь $n$ – число наблюдений в наборе данных, $y_i$ и $x_i$ – рост и вес $i$-ого человека в наборе данных.<jupyter_code>def error(w1, w0, data):
    sum_error = 0.0
    for index, row in data.iterrows():
        sum_error +=(row['Height'] - (w0 + w1 * row['Weight'])) ** 2
    return sum_error<jupyter_output><empty_output><jupyter_text>Итак, мы решаем задачу: как через облако точек, соответсвующих наблюдениям в нашем наборе данных, в пространстве признаков "Рост" и "Вес" провести прямую линию так, чтобы минимизировать функционал из п. 6. Для начала давайте отобразим хоть какие-то прямые и убедимся, что они плохо передают зависимость роста от веса.**[7].** Проведите на графике из п. 5 Задания 1 две прямые, соответствующие значениям параметров ($w_0, w_1) = (60, 0.05)$ и ($w_0, w_1) = (50, 0.16)$. Используйте метод *plot* из *matplotlib.pyplot*, а также метод *linspace* библиотеки NumPy. Подпишите оси и график.<jupyter_code>x = np.linspace(60, 170)
data.plot(x='Weight', y='Height', kind='scatter', title='Correlation between weight and height')
plt.plot(x, 60 + 0.05 * x, color='yellow')
plt.plot(x, 50 + 0.16 * x, color='green')
plt.legend( ('line (60, 0.05)', 'line (50, 0.16)') );<jupyter_output><empty_output><jupyter_text>Минимизация квадратичной функции ошибки - относительная простая задача, поскольку функция выпуклая. Для такой задачи существует много методов оптимизации. Посмотрим, как функция ошибки зависит от одного параметра (наклон прямой), если второй параметр (свободный член) зафиксировать.**[8].** Постройте график зависимости функции ошибки, посчитанной в п. 6, от параметра $w_1$ при $w_0$ = 50. Подпишите оси и график.<jupyter_code>w0 = 50.0
error_fun = []
for w1 in np.arange(-5.0, 5.0, 0.25):
    error_funh.append(error(w1, w0, data))
plt.title('the dependence of the error function on the parameter w1')
plt.ylabel('error function')
plt.xlabel('w1')
plt.plot(error_fun)<jupyter_output><empty_output><jupyter_text>Теперь методом оптимизации найдем "оптимальный" наклон прямой, приближающей зависимость роста от веса, при фиксированном коэффициенте $w_0 = 50$.**[9].** С помощью метода *minimize_scalar* из *scipy.optimize* найдите минимум функции, определенной в п. 6, для значений параметра  $w_1$ в диапазоне [-5,5]. Проведите на графике из п. 5 Задания 1 прямую, соответствующую значениям параметров ($w_0$, $w_1$) = (50, $w_1\_opt$), где $w_1\_opt$ – найденное в п. 8 оптимальное значение параметра $w_1$.<jupyter_code>import scipy.optimize
from scipy.optimize import minimize_scalar

w0 = 50
w1_opt = minimize_scalar(error, bounds = (-5,5), args=(w0,data))
print (w1_opt)
w0 = 50    
x = np.linspace(70, 180)
data.plot(x='Weight', y='Height', kind='scatter', title='Minimize_scalar method')
plt.plot(x, w0 + w1_opt.x * x, color='red');<jupyter_output><empty_output><jupyter_text>При анализе многомерных данных человек часто хочет получить интуитивное представление о природе данных с помощью визуализации. Увы, при числе признаков больше 3 такие картинки нарисовать невозможно. На практике для визуализации данных в 2D и 3D в данных выделаяют 2 или, соответственно, 3 главные компоненты (как именно это делается - мы увидим далее в курсе) и отображают данные на плоскости или в объеме. Посмотрим, как в Python рисовать 3D картинки, на примере отображения функции $z(x,y) = sin(\sqrt{x^2+y^2})$ для значений $x$ и $y$ из интервала [-5,5] c шагом 0.25.<jupyter_code>from mpl_toolkits.mplot3d import Axes3D<jupyter_output><empty_output><jupyter_text>Создаем объекты типа matplotlib.figure.Figure (рисунок) и  matplotlib.axes._subplots.Axes3DSubplot (ось).<jupyter_code>fig = plt.figure()
ax = fig.gca(projection='3d') # get current axis

# Создаем массивы NumPy с координатами точек по осям X и У. 
# Используем метод meshgrid, при котором по векторам координат 
# создается матрица координат. Задаем нужную функцию Z(x, y).
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
Z = np.sin(np.sqrt(X**2 + Y**2))

# Наконец, используем метод *plot_surface* объекта 
# типа Axes3DSubplot. Также подписываем оси.
surf = ax.plot_surface(X, Y, Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()<jupyter_output><empty_output><jupyter_text>**[10].** Постройте 3D-график зависимости функции ошибки, посчитанной в п.6 от параметров $w_0$ и $w_1$. Подпишите ось $x$ меткой «Intercept», ось $y$ – меткой «Slope», a ось $z$ – меткой «Error».<jupyter_code>fig = plt.figure()
ax = fig.gca(projection='3d')

W0 = np.arange(-5, 5, 0.25)
W1 = np.arange(-5, 5, 0.25)
W0, W1 = np.meshgrid(W0, W1)
ERR = error(W1, W0, data)

surf = ax.plot_surface(W0, W1, ERR)
ax.set_xlabel('Intercept')
ax.set_ylabel('Slope')
ax.set_zlabel('Error')
plt.show()<jupyter_output><empty_output><jupyter_text>**[11].** С помощью метода *minimize* из scipy.optimize найдите минимум функции, определенной в п. 6, для значений параметра $w_0$ в диапазоне [-100,100] и $w_1$ - в диапазоне [-5, 5]. Начальная точка – ($w_0$, $w_1$) = (0, 0). Используйте метод оптимизации L-BFGS-B (аргумент method метода minimize). Проведите на графике из п. 5 Задания 1 прямую, соответствующую найденным оптимальным значениям параметров $w_0$ и $w_1$. Подпишите оси и график.<jupyter_code>from scipy.optimize import minimize
def error_vec(w):
    return error(w[1], w[0], data)
w_opt = minimize(error_vec, [0.0, 0.0], method='L-BFGS-B', bounds=([-100, 100], [-5, 5]))
print (w_opt)
x = np.linspace(70, 180)
data.plot(x = 'Weight', y = 'Height', kind = 'scatter', title = 'L-BFGS-B minimize method')
plt.plot(x, w_opt.x[0] + w_opt.x[1] * x, color = 'red');<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11504281790273065, 'fraction_numerical': 0.022701567296816934, 'mean_word_length': 5.6091831286705816, 'pattern_counts': {'":': 0, '<': 66, '<?xml version=': 0, '>': 66, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '66805', 'license': 'MIT', 'n_tokens_mistral': 5199, 'n_tokens_neox': 5706, 'n_words': 1324, 'path': 'supervised_training/1_week/peer_works/.ipynb_checkpoints/3-checkpoint.ipynb', 'repo_name': 'rvg77/ML_Coursera', 'size': '12378'}