Задание выполнил(а): Подчезерцев Алексей
Дата выдачи: 03.02.2019
Дедлайн: 23:59 12.02.2019
В данном домашнем задании вы попрактикуетесь в работе с библиотеками numpy, pandas и matplotlib.
Каждая задача $p_i$ оценивается в 1 балл; Оценка за ДЗ = $\frac{10}{24}\sum_{i}p_i $
За сдачу задания позже срока на итоговую оценку за задание накладывается штраф в размере 1 балл в день, но получить отрицательную оценку нельзя.
Внимание! Домашнее задание выполняется самостоятельно. «Похожие» решения считаются плагиатом и все задействованные студенты (в том числе те, у кого списали) не могут получить за него больше 0 баллов.
Загрузка файлов с решениями происходит в системе Anytask.
Формат названия файла: homework_01_Фамилия_Имя.ipynb
import numpy as np
Во всех задачах необходимо написать код решения внутри функции и проверить его работу, вызвав функцию для данных из условия.
При решении задач запрещается использовать циклы (for, while) и оператор if.
Везде, где встречаются массивы или матрицы, подразумевается, что это numpy.array.
Напишите функцию, возвращающую округленную взвешенную сумму оценок по данным оценкам и весам. Можете посчитать свою оценку за курс :) В нашем случае вес экзамена равен 0.3, вес домашек - 0.42, вес контрольной - 0.14, вес самостоятельных - 0.14. Например, если за экзамен у вас 7, за домашки 10, за контрольную 8, а за самостоятельные 6, то вы получите отличную оценку 8!
def result_mark(weights, marks):
return (weights * marks).sum().round().astype(int)
weights = np.array([0.3, 0.42, 0.14, 0.14])
marks = np.array([7, 10, 8, 6])
result_mark(weights, marks)
Напишите функцию, меняющую все каждое третье (начиная с 0) значение массива целых чисел на заданное число. Например, если на вход поступает массив array([3, 5, 1, 0, -3, 22, 213436]) и число -111, то на выходе должен получиться массив array([-111, 5, 1, -111, -3, 22, -111]).
def change_array(array, number):
array[::3] = number
return array
array = np.array([3, 5, 1, 0, -3, 22, 213436])
number = -111
change_array(array, number)
Напишите функцию, выдающую индексы «близких» элементов заданных массивов, а именно тех пар элементов, чей модуль разницы не превосходит заданного значения. Например, если на вход поступают массив array([1.5, 0.5, 2, -4.1, -3, 6, -1]), массив array([1.2, 0.5, 1, -4, 3, 0, -1.2]) и число 0.5, то на выходе должен получиться массив array([0, 1, 3, 6]) (важно: не tuple, а одномерный массив типа numpy.ndarray (то есть .ndim от него равно 1)!).
def find_close(array1, array2, precision):
return np.arange(array1.size)[abs(array1 - array2) <= precision]
array1 = np.array([1.5, 0.5, 2, -4.1, -3, 6, -1])
array2 = np.array([1.2, 0.5, 1, -4, 3, 0, -1.2])
precision = 0.5
find_close(array1, array2, precision)
Напишите функцию, которая составляет блочную матрицу из четырех блоков, где каждый блок - это заданная матрица. Например, если на вход поступает матрица $$ \begin{pmatrix} 0 & 1 & 2\\ 3 & 4 & 5\\ \end{pmatrix}, $$ то ответом будет матрица $$ \begin{pmatrix} 0 & 1 & 2 & 0 & 1 & 2\\ 3 & 4 & 5 & 3 & 4 & 5\\ 0 & 1 & 2 & 0 & 1 & 2\\ 3 & 4 & 5 & 3 & 4 & 5\\ \end{pmatrix} $$
def block_matrix(block):
block = np.concatenate([block, block])
return np.hstack([block, block])
block = np.array([[0, 1, 2], [3, 4, 5]])
block_matrix(block)
Напишите функцию, вычисляющую произведение всех ненулевых диагональных элементов на диагонали данной квадратной матрицы. Например, если на вход поступает матрица $$ \begin{pmatrix} 0 & 1 & 2\\ 3 & 4 & 5\\ 6 & 7 & 8\\ \end{pmatrix}, $$ то ответом будет 32.
def diag_prod(matrix):
diag = matrix.diagonal()
return diag[diag != 0].prod()
matrix = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
diag_prod(matrix)
Для улучшения качества работы некоторых алгоритмов машинного обучения может быть полезно использовать нормализацию данных, чтобы привести признаки в выборке к одному масштабу — а именно, из каждого столбца вычесть среднее его значений и поделить на их стандартное отклонение. Напишите функцию, нормализующую входящую матрицу (по столбцам). Например, если на вход подается матрица $$ \begin{pmatrix} 1 & 4 & 4200\\ 0 & 10 & 5000\\ 1 & 2 & 1000\\ \end{pmatrix}, $$ то результатом с точностью до сотых будет матрица $$ \begin{pmatrix} 0.71 & -0.39 & 0.46\\ -1.41 & 1.37 & 0.93\\ 0.71 & -0.98 & -1.39\\ \end{pmatrix} $$ Учтите, что в вашей матрице не должно получаться никаких nan. Подумайте, в каком случае они могут возникнуть и как обойти эту проблему.
Подсказка. Казалось бы, при чем тут деление на ноль.
def normalize(matrix):
dividend = matrix - np.mean(matrix,axis=0)
divider = np.std(matrix, axis = 0)
return np.divide(dividend, divider, out=np.zeros(matrix.shape), where=divider!=0)
matrix = np.array([[1, 4, 4200], [0, 10, 5000], [1, 2, 1000]])
normalize(matrix)
Напишите функцию, вычисляющую какую-нибудь первообразную данного полинома (в качестве константы возьмите ваше любимое число). Например, если на вход поступает массив коэффициентов array([4, 6, 0, 1]), что соответствует полиному $4x^3 + 6x^2 + 1$, на выходе получается массив коэффициентов array([1, 2, 0, 1, -2]), соответствующий полиному $x^4 + 2x^3 + x - 2$.
def antiderivative(coefs):
return np.polyint(coefs)
coefs = np.array([4, 6, 0, 1])
antiderivative(coefs)
Напишите функцию, делающую данную треугольную матрицу симметричной. Например, если на вход поступает матрица $$ \begin{pmatrix} 1 & 2 & 3 & 4\\ 0 & 5 & 6 & 7\\ 0 & 0 & 8 & 9\\ 0 & 0 & 0 & 10\\ \end{pmatrix}, $$ то на выходе должна быть матрица $$ \begin{pmatrix} 1 & 2 & 3 & 4\\ 2 & 5 & 6 & 7\\ 3 & 6 & 8 & 9\\ 4 & 7 & 9 & 10\\ \end{pmatrix}. $$
def make_symmetric(matrix):
sub = matrix.transpose()
return np.tril(sub, -1) + matrix
matrix = np.array([[1, 2, 3, 4], [0, 5, 6, 7], [0, 0, 8, 9], [0, 0, 0, 10]])
make_symmetric(matrix)
Напишите функцию, создающую прямоугольную матрицу из m одинаковых строк, заполненных последовательными натуральными числами от a до b включительно в возрастающем порядке. Например, если m = 5, a = 3, b = 10, то на выходе будет матрица $$ \begin{pmatrix} 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10\\ 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10\\ 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10\\ 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10\\ 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10\\ \end{pmatrix} $$
def construct_matrix(m, a, b):
sub = np.zeros((m, b - a + 1), np.int32)
return np.arange(a, b + 1) + sub
m = 5
a = 3
b = 10
construct_matrix(m, a, b)
Напишите функцию, вычисляющую косинусную близость двух векторов. Например, если на вход поступают вектора array([-2, 1, 0, -5, 4, 3, -3]) и array([0, 2, -2, 10, 6, 0, 0]), ответом будет -0.25.
def cosine_similarity(vec1, vec2):
return np.dot(vec1, vec2)/(np.linalg.norm(vec2)*np.linalg.norm(vec1))
vec1 = np.array([-2, 1, 0, -5, 4, 3, -3])
vec2 = np.array([0, 2, -2, 10, 6, 0, 0])
cosine_similarity(vec1, vec2)
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
В этой части домашнего задания вы поработаете с данными о пассажирах с затонувшего «Титаника». Описание данных:
https://www.kaggle.com/c/titanic/data
Каждый построенный график необходимо красиво оформить - для этого требуется как минимум указать его название, а также отметить, что изображено по каждой из осей.
Функции, которые могут пригодиться при оформлении графиков: .show(), .title(), .xlabel(), .ylabel()
Откройте в ноутбуке файл с данными ("train.csv"). Какой размер имеет таблица? Выведите первые 5 и последние 5 строк.
data = pd.read_csv('data.csv', delimiter=',')
data.shape
891 записей и 12 признаков
pd.concat([data.head(5), data.tail(5)])
Для каждого из признаков укажите его тип (вещественный, категориальный, порядковый, другое). Придумайте три задачи, которые можно было бы решать для данного датасета — задачу регрессии, задачу классификации и задачу кластеризации.
Задача регрессии - определение стоимости тарифа (Fare)
Задача классификации - определение, выжил пассажир или нет по входным параметрам
Задача кластеризации - группировка пассажиров по возрастным категориям в зависимости от статуса выжившего, тарифа и др. параметров
Какова доля выживших после крушения пассажиров? Какова доля мужчин и женщин среди выживших?
data["Survived"].mean()
survived = data[data["Survived"] == 1]
sex_filter = survived["Sex"] == "male"
survived[sex_filter].shape[0] / survived.shape[0], survived[~sex_filter].shape[0] / survived.shape[0]
Сколько пассажиров ехало в каждом классе? Кого было больше в самом многолюдном классе — мужчин или женщин?
data_pass_per_class = data.groupby("Pclass")["Pclass"].describe()["count"]
data_pass_per_class
data[data["Pclass"] == data_pass_per_class[data_pass_per_class == data_pass_per_class.max()].index[0]]["Sex"].max()
Выведите минимальную, среднюю и максимальную сумму, которую заплатили пассажиры за проезд. Проделайте то же самое только для тех пассажиров, которые сели на корабль в Саутгемптоне. Выведите гистограммы, показывающие распределения стоимостей билетов в зависимости от места посадки.
data["Fare"].describe()[["min", "mean", "max"]]
data[data["Embarked"] == "S"]["Fare"].describe()[["min", "mean", "max"]]
bins = np.linspace(0, data["Fare"].max(), 50)
alpha=0.6
plt.figure(figsize=(20,10))
plt.grid(True)
plt.title('Распределения стоимостей билетов в зависимости от направления')
plt.hist(data[data["Embarked"] == "C"]["Fare"], bins, alpha=alpha)
plt.hist(data[data["Embarked"] == "Q"]["Fare"], bins, alpha=alpha)
plt.hist(data[data["Embarked"] == "S"]["Fare"], bins, alpha=alpha)
plt.xlabel('Стоимость')
plt.ylabel('Количество')
plt.legend(['Cherbourg', 'Queenstown', 'Southampton'])
plt.show()
Все ли признаки несут в себе полезную информацию? Почему? Избавьтесь от ненужных столбцов.
Номер билета не несут полезную информацию. Pandas сам нумерует записи, идентификатор в данном случае так же не нужен
data = data.drop(columns=['Ticket', 'PassengerId'])
data.head()
Есть ли в данных пропуски? Если да, то в каких столбцах? Сколько пропусков в каждом из них?
data.isnull().sum(axis=0)
Подумайте, как стоит заполнить пропуски в каждом столбце в зависимости от его типа, и сделайте это. Проверьте, что пропусков нигде не осталось.
data["Age"].fillna(data["Age"].mean(), inplace=True)
data["Cabin"].fillna('Unknown', inplace=True)
data["Embarked"].fillna('S', inplace=True)
data.isnull().sum(axis=0)
Посчитайте, насколько сильно коррелируют друг с другом цена за билет и возраст пассажиров. Также проверьте наличие этой зависимости визуально (в этом вам поможет построение диаграммы рассеяния).
data["Age"].corr(data["Fare"])
plt.figure(figsize=(10,6))
plt.grid(True)
plt.title('Зависимость цены билета от возраста пассажира')
plt.xlabel('Возраст')
plt.ylabel('Цена')
plt.scatter(data["Age"], data["Fare"])
plt.show()
Правда ли, что чаще выживали пассажиры с более дорогими билетами? А есть ли зависимость выживаемости от класса?
data["Survived"].corr(data["Fare"])
data[data["Survived"] == 1]["Fare"].mean()
data[data["Survived"] == 0]["Fare"].mean()
Чаще выживали пассажиры с дорогими билетами
data["Survived"].corr(data["Pclass"])
data.groupby("Pclass")["Survived"].mean()
data[data["Survived"] == 1].groupby("Pclass").count()
data[data["Survived"] == 0].groupby("Pclass").count()
Зависимость выживаемости от класса так же присутствует. Больше людей, в процентном и количественном отношении, выжило в 1 классе
Создание новых признаков (feature engineering) является одним из основных средств улучшения качества работы алгоритмов машинного обучения на этапе обработки данных. Добавьте в таблицу столбец, который будет показывать, сколько родных плыло вместе с пассажиром на корабле, включая его самого. Затем придумайте свой признак, который может быть полезен для алгоритма (несет в себе какую-то информацию), и добавьте его в таблицу.
data['Relatives'] = 1 + data['SibSp'] + data['Parch']
# Стоимость километра пути. Дистанция из Википедии
data.loc[data["Embarked"] == "S", "PricePerKm"] = data["Fare"] / 5867
data.loc[data["Embarked"] == "C", "PricePerKm"] = data["Fare"] / 5720
data.loc[data["Embarked"] == "Q", "PricePerKm"] = data["Fare"] / 5147
data.head()
Придумайте какое-нибудь интересное задание на анализ предлагаемых данных и решите его. Например, какая фамилия была самой популярной на корабле?
data["Name"].str.split(",").str.get(0).describe()["top"]
Постройте гистограммы распределения вещественных признаков. Есть ли в них разница? Чем это может быть плохо с точки зрения машинного обучения? Отнормируйте вещественные признаки и посмотрите, как изменилась ситуация.
def custom_hist(data, title, xlabel, ylabel='Количество', bins=None):
figsize = (20,6)
plt.figure(figsize=figsize)
plt.grid(True)
plt.title(title)
plt.hist(data, data.max().astype(int) if bins is None else bins)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.show()
custom_hist(data["Age"], 'Распределения пассажиров по возрасту', 'Возраст')
custom_hist(data["SibSp"], 'Распределения пассажиров по количеству братьев/сестер или супругов', 'Число братьев/сестер или супругов')
custom_hist(data["Parch"], 'Распределения пассажиров по количеству родителей или детей', 'Число родителей или детей')
custom_hist(data["Fare"], 'Распределения пассажиров по стоимости билета', 'Стоимость билета', bins=20)
custom_hist(data["Relatives"], 'Распределения пассажиров по числу родственников', 'Число родственников')
custom_hist(data["PricePerKm"], 'Распределения пассажиров стоимости км пути', 'Стоимость км пути', bins=20)
data["Age_norm"] = (data["Age"]-data["Age"].mean())/data["Age"].std()
data["SibSp_norm"] = (data["SibSp"]-data["SibSp"].mean())/data["SibSp"].std()
data["Parch_norm"] = (data["Parch"]-data["Parch"].mean())/data["Parch"].std()
data["Fare_norm"] = (data["Fare"]-data["Fare"].mean())/data["Fare"].std()
data["Relatives_norm"] = (data["Relatives"]-data["Relatives"].mean())/data["Relatives"].std()
data["PricePerKm_norm"] = (data["PricePerKm"]-data["PricePerKm"].mean())/data["PricePerKm"].std()
data.head()
custom_hist(data["Age_norm"], 'Распределения пассажиров по возрасту', 'Возраст', bins=80)
custom_hist(data["SibSp_norm"], 'Распределения пассажиров по количеству братьев/сестер или супругов', 'Число братьев/сестер или супругов', bins=7)
custom_hist(data["Parch_norm"], 'Распределения пассажиров по количеству родителей или детей', 'Число родителей или детей', bins=5)
custom_hist(data["Fare_norm"], 'Распределения пассажиров по стоимости билета', 'Стоимость билета', bins=20)
custom_hist(data["Relatives_norm"], 'Распределения пассажиров по числу родственников', 'Число родственников', bins=12)
custom_hist(data["PricePerKm_norm"], 'Распределения пассажиров стоимости км пути', 'Стоимость км пути', bins=20)
Есть ли в данных категориальные признаки, которые принимают значения строк, а не чисел? Если да, то для многих алгоритмов машинного обучения это может стать серьезным препятствием, поэтому закодируйте категориальные признаки так, чтобы к данным можно было применить любые алгоритмы машинного обучения.
data.loc[data["Sex"] == "male", "Sex"] = 0
data.loc[data["Sex"] == "female", "Sex"] = 1
data.loc[data["Embarked"] == "S", "Embarked"] = 0
data.loc[data["Embarked"] == "C", "Embarked"] = 1
data.loc[data["Embarked"] == "Q", "Embarked"] = 2
data['Cabin']=pd.factorize(data['Cabin'])[0]
data.head()
data.info()