#!/usr/bin/env python
# coding: utf-8
# In[1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')
plt.style.use('ggplot')
plt.rcParams['figure.figsize'] = (12,5)
# Для кириллицы на графиках
font = {'family': 'Verdana',
'weight': 'normal'}
plt.rc('font', **font)
# # Майнор по Анализу Данных, Группа ИАД-2
# ## 10/04/2017 Отбор признаков и понижение размерности
# # Проклятье размености
#
#
# ## Способы понижения размерности
#
# Избавляться от размерности можно методами **отбора признаков (Feature Selection)** и методами **уменьшения размерности (Feature Reduction)**
# ### Feature Selection
# Методы деляться на три группы:
# * Filter methods
# * Признаки рассматриваются независимо друг от друга
# * Изучается индивидуальный "вклад" призника в предсказываемую переменную
# * Быстрое вычисление
# * Wrapper methods
# * Идет отбор группы признаков
# * Может быть оооочень медленным, но качество, обычно, лучше чем у Filter Methods
# * Embedded methods
# * Отбор признаков "зашит" в модель
# * *Пример?*
# ### Filter method - Mutual Information
# $$MI(y,x) = \sum_{x,y} p(x,y) \ln\left[\frac{p(x,y)}{p(x)p(y)}\right]$$
# Сколько информации $x$ сообщает об $y$.
# $$NormalizedMI(y,x) = \frac{MI(y,x)}{H(y)}$$
# Загрузим довольно известный набор данных о выживаемости после катастрофы титаника.
# In[2]:
df_titanic = pd.read_csv('titanic.csv')
df_titanic.head()
# In[44]:
pd.crosstab(df_titanic.Survived, df_titanic.Sex, normalize=True, )
# Найдем MI между выживаемостью и остальными признаками
# In[41]:
def calc_mutual_information(y, x):
P = pd.crosstab(x, y, normalize=True).values
logP = np.log(((P/P.sum(axis=0)).T/P.sum(axis=1)).T)
return (P*logP).sum()
# In[47]:
calc_mutual_information(df_titanic.Survived, df_titanic.Sex)
# In[26]:
from sklearn.metrics import mutual_info_score
from sklearn.metrics import normalized_mutual_info_score
# In[48]:
mutual_info_score(df_titanic.Survived, df_titanic.Sex)
# ### Wrapper Methods - Recursive Feature Elimination
#
# При данном подходе из (линейной) модели последовательно удаляются признаки с наименьшим коэффициентом
#
# Используйте реализацию RFE в sklean c кросс-валидацией.
#
# * Обучите модель
# * Выведите на графике размер признакового пространства и полученное качество
# * Выведите веса признаков в выбранном признаковом пространстве
# In[49]:
from sklearn.linear_model import LogisticRegression
from sklearn.feature_selection import RFECV
from sklearn.model_selection import StratifiedKFold
# In[50]:
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=123)
# In[51]:
def titanic_preproc(df_input):
df = df_input.copy()
# Удаляем пропуски
df = df.dropna()
# Создаем такой признак
df.loc[:, 'has_cabin'] = df.loc[:, 'Cabin'].isnull().astype(int)
# Удаляем колонки
cols2drop = ['PassengerId', 'Name', 'Ticket', 'Cabin']
df = df.drop(cols2drop, axis=1)
# Нормализуем Age Fare и SibSp (Так делать не оч хорошо)
df.loc[:, 'Age'] = (df.loc[:, 'Age'] - df.loc[:, 'Age'].mean())/df.loc[:, 'Age'].std()
df.loc[:, 'Fare'] = (df.loc[:, 'Fare'] - df.loc[:, 'Fare'].mean())/df.loc[:, 'Fare'].std()
df.loc[:, 'SibSp'] = (df.loc[:, 'SibSp'] - df.loc[:, 'SibSp'].mean())/df.loc[:, 'SibSp'].std()
# Закодируем поле Sex
df.loc[:, 'Sex'] = df.loc[:, 'Sex'].replace({'male': 0, 'female':1})
# Pclass и Embarked можно рассматривать как категориальный признак
df = pd.get_dummies(df, prefix_sep='=', columns=['Pclass', 'Embarked'], drop_first=True)
return df
# In[61]:
df_prep.head()
# In[52]:
df_prep = df_titanic.pipe(titanic_preproc)
X, y = df_prep.iloc[:, 1:].values, df_prep.iloc[:, 0].values
# In[53]:
model = LogisticRegression(random_state=123)
rfe = RFECV(model, step=1, cv=cv, scoring='roc_auc', verbose=1, n_jobs=-1)
# In[54]:
rfe.fit(X, y)
# In[55]:
rfe.grid_scores_
# In[59]:
d = rfe.grid_scores_.shape[0]
plt.plot(range(1, d+1), rfe.grid_scores_)
plt.xlabel('number of features')
plt.ylabel('ROC-AUC cv')
# In[62]:
print 'Выбранные признаки'
fnames = df_prep.columns[1:].values
fnames[rfe.support_]
# In[66]:
pd.Series(index=fnames[rfe.support_], data=rfe.estimator_.coef_[0])
# ### Embedded methods
# * Обучите случайный лес на данных
# * Выведите важность признаков и сравните с выдачей по Filter и Wrapper подходам
# In[ ]:
## Your code here
# # Principal Component Analysis
# ## Метод Главных Компонент
# # PCA
#
# * Позволяет уменьшить число переменных, выбрав самые изменчивые из них
# * Новые переменные являются линейной комбинацией старых переменных
# * Переход к ортономированному базису
#
#
# ## FYI (Посмотрите дома, если интересно)
# ### Построение PCA
# * Пусть $x \in \mathbb{R}^d$ - вектор признаков для какого-то объекта. Будем считать, что $x$ - центрировано и отшкалировано. $E[x_i] = 0, V[x_i] = 1, \quad i=1 \dots d$
# * Требуется найти линейное преобразование, которое задается ортогональной матрицей $A$:
# $$ pc = A^\top x $$
#
# * $pc_i = a_i^\top x = x^\top a_i$
# * $cov[x] = E[(x - E[x])(x - E[x])^\top] = Exx^\top = \Sigma$ - ковариационная матрица
# * $E[pc_i] = E[a_i^\top x] = a_i^\top E[x]$
# * $cov[pc_i, pc_j] = E[pc_i \cdot pc_j^\top] = a_i^\top \Sigma a_j $
# * $\Sigma$ - симметричная и положительно определенная матрица.
# * Собственные числа $\lambda_i \in \mathbb{R}, \lambda_i \geq 0$ (Будем считать, что $\lambda_1 > \lambda_2 > \dots > \lambda_d $
# * Собственные вектора при $\lambda_i \neq \lambda_j $ ортогональны: $v_i^\top v_j = 0$
# * У каждого $\lambda_i$ есть единственный $v_i$
# ### Первая компонента
# $$ pc_1 = a_1 ^\top x $$
#
# \begin{equation}
# \begin{cases}
# V[pc_1] = a_1^\top \Sigma a_1 \rightarrow \max_a \\
# a_1^\top a_1 = 1
# \end{cases}
# \end{equation}
# * Строим функцию лагранжа
# $$ \mathcal{L}(a_1, \nu) = a_1^\top \Sigma a_1 - \nu (a_1^\top a_1 - 1) \rightarrow max_{a_1, \nu}$$
# * Считаем производую по $a_1$
# $$ \frac{\partial\mathcal{L}}{\partial a_1} = 2\Sigma a_1 - 2\nu a_1 = 0 $$
# * Получается, что $a_1$ один из собственных векторов матрицы $\Sigma$, причем при $\lambda_1$
# $$ V[pc_1] = a_1^\top \Sigma a_1 = \lambda_i a_1^\top a_1 = \lambda_i $$
# ### Вторая компонента
# $$ pc_2 = a_2 ^\top x $$
#
# \begin{equation}
# \begin{cases}
# V[pc_1] = a_2^\top \Sigma a_2 \rightarrow \max_a \\
# a_2^\top a_2 = 1 \\
# cov[pc_1, pc_2] = a_2^\top \Sigma a_1 = \lambda_1 a_2^\top a_1 = 0
# \end{cases}
# \end{equation}
# * Строим функцию лагранжа
# $$ \mathcal{L}(a_2, \nu, \tau) = a_2^\top \Sigma a_2 - \nu (a_2^\top a_2 - 1) - \tau a_2^\top a_1 \rightarrow max_{a_1, \nu}$$
#
# Аналогичными выкладками приходим к тому, что $a_2$ - собственный вектор $\Sigma$ при $\lambda_2$
# ## Singular Value Decomposition
#
# Для любой матрицы $X$ размера $n \times m$ можно найти разложение вида:
# $$ X = U S V^\top ,$$
# где
# * $U$ - унитарная матрица, состоящая из собственных векторов $XX^\top$
# * $V$ - унитарная матрица, состоящая из собственных векторов $X^\top X$
# * $S$ - диагональная матрица с сингулярными числами $s_i = \sqrt{\lambda_i}$
# Матрицы $U$ и $V$ ортогональны и могут быть использованы для перехода к ортогональному базису:
# $$ XV = US$$
#
# Сокращение размерности заключается в том, что вместо того, чтобы умножать $X$ на всю матрицу $V$, а лишь на первые $k
# ## MNIST PCA
#
#
# ## Игрушечный пример
# In[ ]:
from sklearn.decomposition import PCA
from numpy.linalg import svd
from sklearn.datasets import load_digits
# In[ ]:
C = np.array([[0., -0.7], [1.5, 0.7]])
X = np.dot(np.random.randn(200, 2) + np.array([4, 2]), C)
# In[ ]:
plt.scatter(X[:, 0], X[:, 1])
plt.axis('equal')
# In[ ]:
pca = PCA(n_components=2)
# In[ ]:
PC = pca.fit_transform(X)
# In[ ]:
coef = pca.components_
coef
# In[ ]:
m = np.mean(X,axis=0)
fig, ax = plt.subplots(1,2)
ax[0].plot([0, coef[0,0]*2]+m[0], [0, coef[0,1]*2]+m[1],'--k')
ax[0].plot([0, coef[1,0]*2]+m[0], [0, coef[1,1]*2]+m[1],'--k')
ax[0].scatter(X[:,0], X[:,1])
ax[0].set_xlabel('$x_1$')
ax[0].set_ylabel('$x_2$')
ax[1].scatter(PC[:,0], PC[:,1])
ax[1].set_xlabel('$pc_1$')
ax[1].set_ylabel('$pc_2$')
ax[0].axis('equal')
ax[1].axis('equal')
# #### Сделаем все тоже самое через SVD
# In[ ]:
## Your Code Here
# ## Чиселки
# In[ ]:
digits = load_digits()
X = digits.images
y = digits.target
# In[ ]:
plt.imshow(X[2,:], cmap='Greys', interpolation='none')
# #### Задание
# * Переведите изображения к формату "матрица объект-признак" (reshape)
# * Выполните PCA c двумя компонентами и изобратите полученные точки на плоскости, раскаживая каждую точку в отдельный цвет в соответствии с `y`
# * Отнормируйте данные, запустите SVD, домножте `X` на нужную матрицу и убедитесь, что у вас получается тот же результат
# In[ ]:
### Your Code Here
# ## Пищевая ценность продуктов
# * Загрузите набор данных о пищевом рационе в разных странах мира `diet.csv`
# * Примените на данных PCA с 2 компонентами
# * Изобразите объекты в сжатом пространстве
# In[ ]:
df = pd.read_csv('diet.csv', sep=';')
# In[ ]:
df = df.dropna(axis=1)
df = df.drop('Energy (kcal/day)', axis=1)
df = df.set_index('Countries')
# In[ ]:
df.head()
# In[ ]:
X = df.values
X = (X - X.mean(axis=0))/X.std(axis=0)
# In[ ]:
## Your Code Here
# * Скорее всего вы обнаружите некоторые выбросы, с этим ничего не поделать - PCA чувствителен к выбросам
# * Удалите объекты-выборосы и повторите процедуру
# * Постарайтесь проинтерпретировать главные компоненты
# In[ ]:
## Your Code Here
# # Bonus: T-distributed stochastic neighbor embedding
# * [Вывод](http://jmlr.csail.mit.edu/papers/volume9/vandermaaten08a/vandermaaten08a.pdf)
# * [Примеры](http://lvdmaaten.github.io/tsne/)
# * [Демо](http://distill.pub/2016/misread-tsne/)