matplotlib

Основным материалом по matplotlib будет следующий ресурс: http://nbviewer.jupyter.org/gi[..]_python/tree/master/


Задача 1

Глобальная задача: увидеть, что в питоне для умножения больших чисел используется алгоритм Карацубы, а также получить практически его асимптотику.
Должен получиться график в духе такого:


py vs gmp 2


В данной задаче нужно:

  1. Научиться генерить случайные целые числа длиной k бит;
  2. Научиться измерять время нескольких операций, используя таймер time.process_time();
  3. Отобрать набор битовых длин для изучения времени: геометрическую прогрессию с началом в 30 и окончанием в 300000 бит длиной 150-300 челов;
  4. Вычислить среднее время умножения нескольких пар чисел;
  5. Построить график времени в log-log шкале (пусть битовые длины k[i], а времёна t[i], нужно построить зависимость log(t[i]) от log(k[i]);
  6. Найти такую прямую l(x) = ax+b, что сумма квадратов разностей (log(t[i]) – l(log(k[i]))) для битовых длин k[i] > 2000 минимальна;
  7. Построить график этой прямой в log-log шкале, а также вычислить параметры в обычной шкале (log(y) = a log(x) + b => y = e^(b)*x^(a))
  8. Подписать эту прямую на графике

Задача 2

Ирисы Фишера


В данной задаче нужно:

  1. Прочитать про датасет по ссылке выше в википедии;
  2. Научиться загружать эти данные и изучить их;
  3. Построить графики как в примере ниже;
  4. Научиться решать следующую задачу: даны два множества точек на плоскости таких, что они разделимы прямой, то есть существует прямая такая, что все точки одного множества лежать по одну сторону от прямой, а точки второго
    по другую. Необходимо найти хотя бы одну такую прямую;
  5. Улучшение 4: нужно найти такую прямую, что минимальное расстояние до любой из точек максимально.
  6. (Подсказка к п. 5: оказывается такая прямая всегда является либо серединным перпендикуляром к отрезку, соединяющему точки разных классов, либо параллельна прямой, проходящей через две точки одного класса (докажите это). Поэтому нужную прямую можно найти за квадратичное время.
  7. Для простоты будем классифицировать только ирисы видов setosa и virginica, поэтому удалите из выборки по versicolor. Выберите пару параметров, лучше всех разделяющих разные классы Ирисов. Для классификации нужно построить разделяющую прямую в плоскости этих двух параметров.
  8. После того, как всё это получилось, займёмся валидацией и причёсыванием кода. Для начала создадим класс нашего классификатора со следующей структурой (см. код класса ниже). Добейтесь того, чтобы он на глазок работал.
  9. После того, как мы создали класс классификатора и научились классифицировать ирисы, нужно проверить качество наших предсказаний. Обычно это делается при помощи кросс-валидации. Идея кросс-валидации в следующем: разобьём нашу обучающую выборку на n частей. Для каждой из этих частей выполним обучение на оставшихся (n-1) части, выполним предсказание на данной части и сравним с ответом. Посчитаем долю правильных ответов. На выходе мы получим строку из n чисел — качеств предсказаний. Посчитаем среднее значение — это и будет качество предсказания на кросс-валидации. Этот пункт обзорный, подробности будут дальше.
  10. Реализуйте функцию accuracy_score(clf, X, y), принимающую на вход уже обученный классификатор, данные для предсказания и вектор ответов. Функция должна возвращать долю правильных ответов. Проверьте, что ваша функция работает на вашем классификаторе из п.7.
  11. Следующий шаг — разбивка всего множества на n частей. Здесь нужно реализовать класс KFold с двумя методами: init(self, n_splits=5, random_state=None) и split(self, X, y=None). Конструктор должен только лишь сохранять параметры в своих атрибутах. Метод split должен возвращать список из n_splits элементов. Каждый элемент списка — это пара массивов train_index, test_index. Объединение этих массивов — набор индексов от 0 до X.shape[0]-1. В массиве test_index должно быть примерно (X.shape[0]/n_splits) элементов. Массив индексов от 0 до X.shape[0]-1 перемешивается и делится на n_splits примерно равных частей. Соответственно каждый набор test_index — это набор индексов из этих n_splits частей, а train_index — оставшиеся (n_splits-1) частей. Параметр random_state используется следующим образом: если он передан, то необходимо вызвать np.random.seed(random_state) для того, чтобы при заполненном параметре разбиение каждый раз получалось одинаковым. Параметр y здесь не используется, он потребуется в будущем.
  12. Финальный шаг — функция cross_val_score(estimator, X, y, scoring, cv). Она принимает на вход классификатор clf (необученный), множество для обучения X, вектор ответов y, функцию для вычисления качества scoring, а также элемент класса KFold для разбивки тестового множества на n_splits классов. Функция должна n_splits раз обучить классификатор clf на данных X[train_index], вычислить качество предсказания при помощи функции scoring (вектор ответов — это y[train_index]). После чего вернуть массив длины n_splits — качество предсказания на каждом из шагов.
  13. Теперь можно пытаться разделять виды versicolor и virginica. Здесь множества не разделяются, поэтому нужно придумать какой-нибудь штраф за нахождение не в той полуплоскости и каким-нибудь образом находить прямую с минимальным штрафов. Проверьте качество вашей классификации при помощи уже реализованной кросс-валидации.
  14. Весь набор тестер-фолдер-валидатор очень удобно использовать для проверки качества реализованного классификатора. Он пригодится и в следующей задаче, поэтому после того, как всё заработает, причешите код. Сделайте его красивым и удобным.


from sklearn import datasets
# После этой команды будет загружен словарь (выполнить один раз)
iris = datasets.load_iris()

# Сохраняем данные на компьютер (выполнить один раз)
# data, target, feature_names, target_names = iris['data'], iris['target'], iris['feature_names'], iris['target_names']
# with open(r'M:\np_basic\iris.dump', 'wb') as f:
# pickle.dump((data, target, feature_names, target_names), f)

# Загрузка данных из сохранённого дампа
with open(r'M:\np_basic\iris.dump', 'rb') as f:

(data, target, feature_names, target_names) = pickle.load(f)


import numpy as np


class SimpleSplitLineClassifier():

_estimator_type = "classifier"


def init(self): # У нашего классификатора пока нет параметров
pass


def fit(self, X, y): # Команда для обучения классификатора
# Делаем проверки
if len(X.shape) != 2:
raise ValueError("Массив X должен быть двумерным")

if len(y.shape) != 1:

raise ValueError("Массив y должен быть одномерным")

self.classes_ = sorted(set(y)) # Упорядочиваем классы для ответов
if len(self.classes_) != 2:

raise ValueError("Массив y должен состоять ровно из двух различных значений")

if X.shape[0] != y.shape[0]:

raise ValueError("В массиве X должно быть столько строк, сколько в массиве y элементов")
# Сохраняем данные, на которых мы обучались в своих атрибутах
self.X_ = X
self.y_ = y
# Находим разделяющую прямую ax+by+c=0.
# Для классификации мы будем подставлять точку (x,y) в уравнение этой прямой,
# если полученное значение больше или равно нулю, то классифицируем в self.classes_[0], иначе – в self.classes_[1]
self.a, self.b, self.c = 0, 0, 0 # Вы ведь уже научились находить эту прямую?
return self


def predict(self, X):
# Проверяем, что форма массива X для предсказания правильная
if len(X.shape) != 2:
raise ValueError("Массив X должен быть двумерным")

if X.shape[1] != self.X_.shape[1]:

raise ValueError("В массиве X должно быть столько же столбцов, сколько в массиве, на котором производилось обучение")
# Классифицируем
predictions = np.zeros(len(X)) # Вы ведь уже научились определять ирисы?
return predictions


# Две вспомогательные функции на будущее
def get_params(self, deep=True): # Получить текущие параметры классификатора (у нас их нет)
return dict()


def set_params(self, **parameters): # Установить параметры классификатора
for parameter, value in parameters.items():
self.setattr(parameter, value)
return self

# Создаём классификатор
clf = SimpleSplitLineClassifier()
# Обучаем его
clf.fit(np.array( [ [1, 0], [1, 1], [-1, 0], [-1, 1] ] ), np.array([0, 0, 1, 1]))
# Предсказываем
print(clf.predict(np.array( [ [1, 0], [2, 0] ] )))


class KFold:

def init(self, n_splits=5, random_state=None):
self.n_splits = n_splits
self.random_state = random_state


def split(self, X, y=None):
if self.random_state is not None:
np.random.seed(self.random_state)
...
return ...

def accuracy_score(clf, X, y):

prediction = clf.predict(X)
accuracy = ...
return accuracy

def cross_val_score(estimator, X, y, scoring, cv):

...
return ...

cv_folder = KFold(n_splits=3, random_state=179)
print(cross_val_score(estimator=clf, X=x_train, y=y_train, scoring=accuracy_score, cv=cv_folder))


Задача 3

Титаник


Данные:
file:/titanic_train.csv


import numpy as np
import pandas as pd
titanic_df = pd.read_csv("titanic_train.csv")
# Вектор ответов
y_train = titanic_df['Survived'].as_matrix()
# Удаляем ненужные столбцы
titanic_df.drop(['PassengerId','Name','Ticket','Survived'], axis=1, inplace=True)
# Данные
columns = titanic_df.columns
x_train = titanic_df.as_matrix()


Описание столбцов:


  • PassengerId — идентификатор пассажира
  • Survival — поле в котором указано спасся человек (1) или нет (0)
  • Pclass — содержит социально-экономический статус:
    • высокий
    • средний
    • низкий
  • Name — имя пассажира
  • Sex — пол пассажира
  • Age — возраст
  • SibSp — содержит информацию о количестве родственников 2-го порядка (муж, жена, братья, сетры)
  • Parch — содержит информацию о количестве родственников на борту 1-го порядка (мать, отец, дети)
  • Ticket — номер билета
  • Fare — цена билета
  • Cabin — каюта
  • Embarked — порт посадки
    • C — Cherbourg
    • Q — Queenstown
    • S — Southampton




Для kaggle:
file:/titanic_test.csv


test_df = pd.read_csv("train.csv")
# Сохраняем ID пассажиров
PassengerIds = test_df['PassengerId'].as_matrix()
# Удаляем ненужные столбцы
test_df.drop(['PassengerId','Name','Ticket','Survived'], axis=1, inplace=True)
# Делаем предсказание
y_pred = np.zeros_like(PassengerIds)
# Генерим ответ
submission = pd.DataFrame({"PassengerId": PassengerIds, "Survived": y_pred})
submission.to_csv('titanic_submission.csv', index=False)