Методичкаx

advertisement
Лабораторная работа 7
Основы анализа данных на языке python.
Цель: получить практические навыки обработки и анализа больших
объемов данных на языке Python.
Рассмотрим пример анализа данных на python c помощью
модуля Pandas. Работа будет показана на примере задачи про спасенных с
"Титаника". Данное задание имеет большую популярность среди людей,
только начинающих заниматься анализом данных и машинным обучением.
Постановка задачи
Итак суть задачи состоит в том, чтобы с помощью методов машинного
обучения построить модель, которая прогнозировала бы спасется человек
или нет. К задаче прилагаются 2 файла:
train.csv — набор данных на основании которого будет строиться модель
(обучающая выборка)
test.csv — набор данных для проверки модели
Для анализ понадобятся модули Pandas и scikit-learn. С
помощью Pandas мы проведем начальный анализ данных, а sklearn поможет в
вычислении прогнозной модели. Итак, для начала загрузим нужные модули:
Кроме того даются пояснения по некоторым полям:
PassengerId — идентификатор пассажира
Survival — поле в котором указано спасся человек (1) или нет
(0)Pclass — содержит социально-экономический статус:
высокий
1.
средний
2.
низкий
Name — имя пассажира
Sex — пол пассажира
Age — возраст
SibSp — содержит информацию о количестве родственников 2-го
порядка (муж, жена, братья, сетры)
Parch — содержит информацию о количестве родственников на
борту 1-го порядка (мать, отец, дети)
Ticket — номер билета
Fare — цена билета
Cabin — каюта
Embarked — порт посадки

C — Cherbourg

Q — Queenstown

S — Southampton
Анализ входных данных
Итак, задача сформирована и можно приступить к ее решению.
Для начала загрузим тестовую выборку и посмотрим как она выглядит::
from pandas import read_csv, DataFrame, Series
data = read_csv('Kaggle_Titanic/Data/train.csv')
PassengerId Survived Pclass
1
2
3
4
5
0
1
1
1
0
Name
Sex
Age SibSp Parch
Ticket
re
3
Braund,
Mr. Owen
Harris
male
22
1
0
A/5 21171 2500
1
Cumings,
Mrs. John
Bradley
(Florence
Briggs
Th...
female 38
1
0
PC 17599 .2833
3
Heikkinen,
Miss.
Laina
female 26
0
0
STON/O2.
3101282 9250
1
Futrelle,
Mrs.
Jacques
Heath
(Lily May
Peel)
female 35
1
0
113803
.1000
3
Allen, Mr.
William
Henry
male
0
0
373450
0500
35
Можно предположить, что чем выше социальный статус, тем больше
вероятность спасения. Давайте проверим это взглянув на количество
спасшихся и утонувших в зависимости в разрезе классов. Для этого нужно
построить следующую сводную:
data.pivot_table('PassengerId', 'Pclass', 'Survived', 'count').plot(kind='bar',
stacked=True)
Наше вышеописанное предположение про то, что чем выше у пассажиров их
социальное положение, тем выше их вероятность спасения. Теперь давайте
взглянем, как количество родственников влияет на факт спасения:
fig, axes = plt.subplots(ncols=2)
data.pivot_table('PassengerId', ['SibSp'], 'Survived', 'count').plot(ax=axes[0],
title='SibSp')
data.pivot_table('PassengerId', ['Parch'], 'Survived', 'count').plot(ax=axes[1],
title='Parch')
Как видно из графиков наше предположение снова подтвердилось, и из
людей имеющих больше 1 родственников спаслись не многие.
Сейчас порассуждаем на предмет данных, которые находятся номера кают.
Теоретически данных о каютах пользователей может не быть, так что давайте
посмотрим на столько это поле заполнено:
data.PassengerId[data.Cabin.notnull()].count()
В итоге заполнено всего 204 записи и 890, на основании этого можно сделать
вывод, что данное поле при анализе можно опустить.
Следующее поле, которое мы разберем будет поле с возрастом (Age).
Посмотрим на сколько оно заполнено:
data.PassengerId[data.Age.notnull()].count()
Данное поле практически все заполнено (714 непустых записей), но есть
пустые значения, которые не определены. Давайте зададим ему значение
равное медиане по возрасту из всей выборки. Данный шаг нужен для более
точного построения модели:
data.Age = data.Age.median()
У нас осталось разобраться с полями Ticket, Embarked, Fare, Name.
Давайте посмотрим на поле Embarked, в котором находится порт посадки и
проверим есть ли такие пассажиры у которых порт не указан:
data[data.Embarked.isnull()]
PassengerId Survived Pclass Name
62
830
1
1
Sex
Age SibSp Parch Ticket Fare Cabi
1
Icard,
Miss.
Amelie female 28
0
0
113572 80
B28
1
Stone,
Mrs.
George
Nelson
(Martha
Evelyn) female 28
0
0
113572 80
B28
Итак у нас нашлось 2 таких пассажира. Давайте присвоим эти пассажирам
порт в котором село больше всего людей:
MaxPassEmbarked = data.groupby('Embarked').count()['PassengerId']
data.Embarked[data.Embarked.isnull()] =
MaxPassEmbarked[MaxPassEmbarked == MaxPassEmbarked.max()].index[0]
Ну что же разобрались еще с одним полем и теперь у нас остались поля с имя
пассажира, номером билета и ценой билета.
По сути нам из этих трех полей нам нужна только цена(Fare), т.к. она в
какой-то мере определяем ранжирование внутри классов поля Pclass. Т. е.
например люди внутри среднего класса могут быть разделены на тех, кто
ближе к первому(высшему) классу, а кто к третьему(низший). Проверим это
поле на пустые значения и если таковые имеются заменим цену медианой по
цене из все выборки:
data.PassengerId[data.Fare.isnull()]
В нашем случае пустых записей нет.
В свою очередь номер билета и имя пассажира нам никак не помогут, т. к.
это просто справочная информация. Единственное для чего они могут
пригодиться — это определение кто из пассажиров потенциально являются
родственниками, но так как люди у которых есть родственники практически
не спаслись (это было показано выше) можно пренебречь этими данными.
Теперь, после удаления всех ненужных полей, наш набор выглядит так:
data = data.drop(['PassengerId','Name','Ticket','Cabin'],axis=1)
Survived
Pclass
Sex
Age
SibSp
Parch
Fare
Embarked
0
3
male
28
1
0
7.2500
S
1
1
female
28
1
0
71.2833
C
1
3
female
28
0
0
7.9250
S
1
1
female
28
1
0
53.1000
S
8.0500
S
0
3
male
28
0
0
Предварительная обработка входных данных
Предварительный анализ данных завершен, и по его результатам у нас
получилась некая выборка, в которой содержатся несколько полей и вроде
бы можно преступить к построению модели, если бы не одно «но»: наши
данные содержат не только числовые, но и текстовые данные.
Поэтому переде тем, как строить модель, нужно закодировать все наши
текстовые значения.
Можно это сделать в ручную, а можно с помощью
модуля sklearn.preprocessing. Давайте воспользуемся вторым вариантом.
Закодировать список с фиксированными значениями можно с помощью
объекта LabelEncoder(). Суть данной функции заключается в том, что на вход
ей подается список значений, который надо закодировать, на выходе
получается список классов индексы которого и являются кодами элементов
поданного на вход списка.
from sklearn.preprocessing import LabelEncoder
label = LabelEncoder()
dicts = {}
label.fit(data.Sex.drop_duplicates()) #задаем список значений для
кодирования
dicts['Sex'] = list(label.classes_)
data.Sex = label.transform(data.Sex) #заменяем значения из списка кодами
закодированных элементов
label.fit(data.Embarked.drop_duplicates())
dicts['Embarked'] = list(label.classes_)
data.Embarked = label.transform(data.Embarked)
В итоге наши исходные данные будут выглядеть так:
Survived
Pclass
Sex Age
SibSp
Parch
Fare
Embarked
0
3
1
28
1
0
7.2500
2
1
1
0
28
1
0
71.2833 0
1
3
0
28
0
0
7.9250
1
1
0
28
1
0
53.1000 2
0
3
1
28
0
0
8.0500
2
2
Теперь нам надо написать код для приведения проверочного файла в нужный
нам вид. Для этого можно просто скопировать куски кода которые были
выше(или просто написать функцию для обработки входного файла):
test = read_csv('Kaggle_Titanic/Data/test.csv')
test.Age[test.Age.isnull()] = test.Age.mean()
test.Fare[test.Fare.isnull()] = test.Fare.median() #заполняем пустые
значения средней ценой билета
MaxPassEmbarked = test.groupby('Embarked').count()['PassengerId']
test.Embarked[test.Embarked.isnull()] =
MaxPassEmbarked[MaxPassEmbarked == MaxPassEmbarked.max()].index[0]
result = DataFrame(test.PassengerId)
test = test.drop(['Name','Ticket','Cabin','PassengerId'],axis=1)
label.fit(dicts['Sex'])
test.Sex = label.transform(test.Sex)
label.fit(dicts['Embarked'])
test.Embarked = label.transform(test.Embarked)
Код описанный выше выполняет практически те же операции, что мы
проделали с обучающей выборкой. Отличие в том, что добавилась строка для
обработки поля Fare, если оно вдруг не заполнено.
Pclass
Sex
Age
SibSp
Parch
Fare
Embarked
3
1
34.5
0
0
7.8292
1
3
0
47.0
1
0
7.0000
2
2
1
62.0
0
0
9.6875
1
3
1
27.0
0
0
8.6625
2
3
0
22.0
1
1
12.2875
2
Построение моделей классификации и их анализ
Ну что же, данные обработаны и можно приступить к построению модели, но
для начала нужно определиться с тем, как мы будем проверять точность
полученной модели. Для данной проверки мы будем
использовать скользящий контроль и ROC-кривые. Проверку будем
выполнять на обучающей выборке, после чего применим ее на тестовую.
Итак рассмотрим несколько алгоритмов машинного обучения:




Метод опорных векторов
Метод ближайших соседей
Random forest
Логистическая регрессия
Загрузим нужные нам библиотеки:
from sklearn import cross_validation, svm
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_curve, auc
import pylab as pl
Для начала, надо разделить нашу обучающую выборку на показатель,
который мы исследуем, и признаки его определяющие:
target = data.Survived
train = data.drop(['Survived'], axis=1) #из исходных данных убираем Id
пассажира и флаг спасся он или нет
kfold = 5 #количество подвыборок для валидации
itog_val = {} #список для записи результатов кросс валидации разных
алгоритмов
Теперь наша обучающая выборка выглядит так:
Pclass
Sex
Age
SibSp
Parch
Fare
Embarked
3
1
28
1
0
7.2500
2
1
0
28
1
0
71.2833
0
3
0
28
0
0
7.9250
2
1
0
28
1
0
53.1000
2
3
1
28
0
0
8.0500
2
Теперь разобьем показатели полученные ранее на 2 подвыборки(обучающую
и тестовую) для расчет ROC кривых (для скользящего контроля этого делать
не надо, т.к. функция проверки это делает сама. В этом нам поможет
функция train_test_split модуляcross_validation:
ROCtrainTRN, ROCtestTRN, ROCtrainTRG, ROCtestTRG =
cross_validation.train_test_split(train, target, test_size=0.25)
В качестве параметров ей передается:
Массив параметров

Массив значений показателей

Соотношение в котором будет разбита обучающая выборка (в
нашем случае для тестового набора будет выделена 1/4 часть данных
исходной обучающей выборки)

На выходе функция выдает 4 массива:
1.
2.
3.
4.
Новый обучающий массив параметров
тестовый массив параметров
Новый массив показателей
тестовый массив показателей
Далее представлены перечисленные методы с наилучшими параметрами
подобранные опытным путем:
model_rfc = RandomForestClassifier(n_estimators = 70) #в параметре
передаем кол-во деревьев
model_knc = KNeighborsClassifier(n_neighbors = 18) #в параметре
передаем кол-во соседей
model_lr = LogisticRegression(penalty='l1', tol=0.01)
model_svc = svm.SVC() #по умолчанию kernek='rbf'
Теперь проверим полученные модели с помощью скользящего контроля. Для
этого нам необходимо воcпользоваться функциейcross_val_score
scores = cross_validation.cross_val_score(model_rfc, train, target, cv = kfold)
itog_val['RandomForestClassifier'] = scores.mean()
scores = cross_validation.cross_val_score(model_knc, train, target, cv =
kfold)
itog_val['KNeighborsClassifier'] = scores.mean()
scores = cross_validation.cross_val_score(model_lr, train, target, cv = kfold)
itog_val['LogisticRegression'] = scores.mean()
scores = cross_validation.cross_val_score(model_svc, train, target, cv =
kfold)
itog_val['SVC'] = scores.mean()
Давайте посмотрим на графике средний показатель тестов перекрестной
проверки каждой модели:
DataFrame.from_dict(data = itog_val, orient='index').plot(kind='bar',
legend=False)
Как можно увидеть из графика лучше всего себя показал алгоритм
RandomForest. Теперь же давайте взглянем на графики ROC-кривых, для
оценки точности работы классификатора. Графики будем рисовать с
помощью библиотеки matplotlib:
pl.clf()
plt.figure(figsize=(8,6))
#SVC
model_svc.probability = True
probas = model_svc.fit(ROCtrainTRN,
ROCtrainTRG).predict_proba(ROCtestTRN)
fpr, tpr, thresholds = roc_curve(ROCtestTRG, probas[:, 1])
roc_auc = auc(fpr, tpr)
pl.plot(fpr, tpr, label='%s ROC (area = %0.2f)' % ('SVC', roc_auc))
#RandomForestClassifier
probas = model_rfc.fit(ROCtrainTRN,
ROCtrainTRG).predict_proba(ROCtestTRN)
fpr, tpr, thresholds = roc_curve(ROCtestTRG, probas[:, 1])
roc_auc = auc(fpr, tpr)
pl.plot(fpr, tpr, label='%s ROC (area = %0.2f)' % ('RandonForest',roc_auc))
#KNeighborsClassifier
probas = model_knc.fit(ROCtrainTRN,
ROCtrainTRG).predict_proba(ROCtestTRN)
fpr, tpr, thresholds = roc_curve(ROCtestTRG, probas[:, 1])
roc_auc = auc(fpr, tpr)
pl.plot(fpr, tpr, label='%s ROC (area = %0.2f)' %
('KNeighborsClassifier',roc_auc))
#LogisticRegression
probas = model_lr.fit(ROCtrainTRN,
ROCtrainTRG).predict_proba(ROCtestTRN)
fpr, tpr, thresholds = roc_curve(ROCtestTRG, probas[:, 1])
roc_auc = auc(fpr, tpr)
pl.plot(fpr, tpr, label='%s ROC (area = %0.2f)' %
('LogisticRegression',roc_auc))
pl.plot([0, 1], [0, 1], 'k--')
pl.xlim([0.0, 1.0])
pl.ylim([0.0, 1.0])
pl.xlabel('False Positive Rate')
pl.ylabel('True Positive Rate')
pl.legend(loc=0, fontsize='small')
pl.show()
Как видно по результатам ROC-анализа лучший результат опять показал
RandomForest. Теперь осталось только применить нашу модель к тестовой
выборке:
model_rfc.fit(train, target)
result.insert(1,'Survived', model_rfc.predict(test))
result.to_csv('Kaggle_Titanic/Result/test.csv', index=False)
Варианты:
1. Определить вероятность спасения пассажира мужского пола из
первого класса.
2. Определить вероятность спасения детей из второго класса
3. Определить вероятность спасения одиноких пассажиров (без
родственников)
4. Определить вероятность спасения пассажиров, севших в порту
Queenstown с дорогими билетами.
5. Определить средний возраст спасшихся и погибших пассажиров.
6. Определить вероятность спасения одиноких пожилых людей.
7. Вывести статистику о спасшихся детях.
8. Вывести среднюю цену билета для каждого порта.
9. Вывести среднюю цену билета для каждого социального класса.
10. Определить, реально ли существовал пассажир "Титаника",
рассказавшая историю в документальной части фильма Джеймса Кэмерона
"Титаник". Вывести ее данные.
11. Сопоставить официальную статистику погибших с данными,
полученными в результате анализа.
Download