Добрый день уважаемые читатели. В сегодняшней посте я продолжу свой цикл статей посвященный анализу данных на python c помощью модуля Pandas
и расскажу один из вариантов использования данного модуля в связке с модулем для машинного обучения scikit-learn
. Работа данной связки будет показана на примере задачи
про спасенных с «Титаника". Данное задание имеет большую популярность среди людей, только начинающих заниматься анализом данных и машинным обучением
.
- Постановка задачи
- Анализ входных данных
- Предварительная обработка входных данных
- Построение моделей классификации и их анализ
- Заключение
- Постановка задачи
- Анализ входных данных
- Предварительная обработка входных данных
- Построение моделей классификации и их анализ
- Заключение
- Что такое анализ данных?
- Что такое Pandas?
- Структуры данных в Pandas
- Анализ данных в Pandas
- Отсутствующие данные
- Среда разработки
Постановка задачи
Итак суть задачи состоит в том, чтобы с помощью методов машинного обучения построить модель, которая прогнозировала бы спасется человек или нет. К задаче прилагаются 2 файла:
- train.csv
— набор данных на основании которого будет строиться модель ( обучающая выборка
) - test.csv
— набор данных для проверки модели
Как было написано выше, для анализ понадобятся модули Pandas и scikit-learn. С помощью Pandas
мы проведем начальный анализ данных, а sklearn
поможет в вычислении прогнозной модели. Итак, для начала загрузим нужные модули:
Кроме того даются пояснения по некоторым полям:
Анализ входных данных
>Итак, задача сформирована и можно приступить к ее решению.
Для начала загрузим тестовую выборку и посмотрим как она выглядит::
from pandas import read_csv, DataFrame, Series
data = read_csv('Kaggle_Titanic/Data/train.csv')
Можно предположить, что чем выше социальный статус, тем больше вероятность спасения. Давайте проверим это взглянув на количество спасшихся и утонувших в зависимости в разрезе классов. Для этого нужно построить следующую сводную:
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()]
Итак у нас нашлось 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)
Предварительная обработка входных данных
Предварительный анализ данных завершен, и по его результатам у нас получилась некая выборка, в которой содержатся несколько полей и вроде бы можно преступить к построению модели, если бы не одно «но»: наши данные содержат не только числовые, но и текстовые данные.
Поэтому переде тем, как строить модель, нужно закодировать все наши текстовые значения.
Можно это сделать в ручную, а можно с помощью модуля 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)
В итоге наши исходные данные будут выглядеть так:
Теперь нам надо написать код для приведения проверочного файла в нужный нам вид. Для этого можно просто скопировать куски кода которые были выше(или просто написать функцию для обработки входного файла):
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
, если оно вдруг не заполнено.
Построение моделей классификации и их анализ
Ну что же, данные обработаны и можно приступить к построению модели, но для начала нужно определиться с тем, как мы будем проверять точность полученной модели. Для данной проверки мы будем использовать скользящий контроль
и 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 = {} #список для записи результатов кросс валидации разных алгоритмов
Теперь наша обучающая выборка выглядит так:
Теперь разобьем показатели полученные ранее на 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 массива:
- Новый обучающий массив параметров
- тестовый массив параметров
- Новый массив показателей
- тестовый массив показателей
Далее представлены перечисленные методы с наилучшими параметрами подобранные опытным путем:
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)
Заключение
В данной статье я постарался показать, как можно использовать пакет pandas
в связке с пакетом для машинного обучения sklearn
. Полученная модель при сабмите на Kaggle показала точность 0.77033. В статье я больше хотел показать именно работу с инструментарием и ход выполнения исследования, а не построение подробного алгоритма, как например в этой
серии статей.
Добрый день уважаемые читатели. В сегодняшней посте я продолжу свой цикл статей посвященный анализу данных на python c помощью модуля Pandas
и расскажу один из вариантов использования данного модуля в связке с модулем для машинного обучения scikit-learn
. Работа данной связки будет показана на примере задачи
про спасенных с «Титаника". Данное задание имеет большую популярность среди людей, только начинающих заниматься анализом данных и машинным обучением
.
Постановка задачи
Итак суть задачи состоит в том, чтобы с помощью методов машинного обучения построить модель, которая прогнозировала бы спасется человек или нет. К задаче прилагаются 2 файла:
- train.csv
— набор данных на основании которого будет строиться модель ( обучающая выборка
) - test.csv
— набор данных для проверки модели
Как было написано выше, для анализ понадобятся модули Pandas и scikit-learn. С помощью Pandas
мы проведем начальный анализ данных, а sklearn
поможет в вычислении прогнозной модели. Итак, для начала загрузим нужные модули:
Кроме того даются пояснения по некоторым полям:
Анализ входных данных
>Итак, задача сформирована и можно приступить к ее решению.
Для начала загрузим тестовую выборку и посмотрим как она выглядит::
from pandas import read_csv, DataFrame, Series
data = read_csv('Kaggle_Titanic/Data/train.csv')
Можно предположить, что чем выше социальный статус, тем больше вероятность спасения. Давайте проверим это взглянув на количество спасшихся и утонувших в зависимости в разрезе классов. Для этого нужно построить следующую сводную:
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()]
Итак у нас нашлось 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)
Предварительная обработка входных данных
Предварительный анализ данных завершен, и по его результатам у нас получилась некая выборка, в которой содержатся несколько полей и вроде бы можно преступить к построению модели, если бы не одно «но»: наши данные содержат не только числовые, но и текстовые данные.
Поэтому переде тем, как строить модель, нужно закодировать все наши текстовые значения.
Можно это сделать в ручную, а можно с помощью модуля 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)
В итоге наши исходные данные будут выглядеть так:
Теперь нам надо написать код для приведения проверочного файла в нужный нам вид. Для этого можно просто скопировать куски кода которые были выше(или просто написать функцию для обработки входного файла):
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
, если оно вдруг не заполнено.
Построение моделей классификации и их анализ
Ну что же, данные обработаны и можно приступить к построению модели, но для начала нужно определиться с тем, как мы будем проверять точность полученной модели. Для данной проверки мы будем использовать скользящий контроль
и 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 = {} #список для записи результатов кросс валидации разных алгоритмов
Теперь наша обучающая выборка выглядит так:
Теперь разобьем показатели полученные ранее на 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 массива:
- Новый обучающий массив параметров
- тестовый массив параметров
- Новый массив показателей
- тестовый массив показателей
Далее представлены перечисленные методы с наилучшими параметрами подобранные опытным путем:
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)
Заключение
В данной статье я постарался показать, как можно использовать пакет pandas
в связке с пакетом для машинного обучения sklearn
. Полученная модель при сабмите на Kaggle показала точность 0.77033. В статье я больше хотел показать именно работу с инструментарием и ход выполнения исследования, а не построение подробного алгоритма, как например в этой
серии статей.
Наука данных является обширной областью исследования с большим количеством областей, из которых анализ данных является неоспоримо один из наиболее важных из всех этих областей, и независимо от своего уровня мастерства в науке данных, она становится все более важной для понимания.
Если вы новичок в Python, советуем прочитать книги по языку программирования Python
Что такое анализ данных?
Анализ данных — это обработка и преобразование большого количества неструктурированных или неорганизованных данных с целью генерирования ключевой информации об этих данных, которые могли бы помочь в принятии обоснованных решений.
Существуют различные инструменты, используемые для анализа данных, Python, Microsoft Excel, Tableau, SaS и т. Д., Но в этой статье мы сосредоточимся на том, как анализ данных выполняется в python. Более конкретно, как это делается с библиотекой Python под названием Pandas
.
Что такое Pandas?
Pandas
— это библиотека Python с открытым исходным кодом, используемая для манипулирования данными. Это быстрая и высокоэффективная библиотека с инструментами для загрузки нескольких видов данных в память. Его можно использовать для изменения формы, маркировки среза, индексации или даже группировки нескольких форм данных.
Структуры данных в Pandas
В Pandas есть 3 структуры данных, а именно:
- Series
- DataFrame
- Panel
Лучший способ различить три из них — это видеть, что один содержит несколько стеков другого. Итак, DataFrame — это стек Series, а Panel — это стек DataFrame.
Series — это одномерный массив.
Стек из нескольких Series составляет двухмерный DataFrame
Стек из нескольких DataFrames образует трехмерный Panel
Структура данных, с которой мы будем работать больше всего, — это двухмерный DataFrame, который также может быть средством представления по умолчанию для некоторых наборов данных, с которыми мы можем столкнуться.
Анализ данных в Pandas
Для этой статьи какие-либо установки не требуются. Мы будем использовать инструмент под названием colaboratory
, созданный Google. Это онлайн среда Python для анализа данных, машинного обучения и искусственного интеллекта. Это просто облачный Jupyter Notebook, который поставляется с предустановленным почти каждым пакетом Python, который вам понадобится как специалист по данным.
Теперь перейдите на сайт https://colab.research.google.com/notebooks/intro.ipynb
. Вы должны увидеть картинку ниже.

В левом верхнем углу, выберите опцию «File» и нажмите «New notebook». Вы увидите новую страницу записной книжки Jupyter
, загруженную в ваш браузер. Первое, что нам нужно сделать, это импортировать Pandas
в нашу рабочую среду. Мы можем сделать это, с помощью строки:
import pandas as pd
Для этой статьи мы будем использовать набор данных о ценах на жилье для нашего анализа данных. Набор данных, который мы будем использовать, можно найти здесь
. Первое, что мы хотели бы сделать, это загрузить этот набор данных в нашу среду.
Мы можем сделать это с помощью следующего кода в новой ячейке;
df = pd.read_csv('https://firebasestorage.googleapis.com/v0/b/ai6-portfolio-abeokuta.appspot.com/o/kc_house_data.csv?alt=media &token=6a5ab32c-3cac-42b3-b534-4dbd0e4bdbc0 ', sep=',')
read_csv
Используется, чтобы прочитать файл CSV и мы прошли SEP свойство, чтобы показать, что файл CSV разделяются запятыми.
Также следует отметить, что наш загруженный CSV-файл хранится в переменной df
.
Нам не нужно использовать функцию print()
в Jupyter Notebook. Мы можем просто ввести имя переменной в нашей ячейке, и Jupyter Notebook распечатает его для нас.
Мы можем попробовать это, набрав df
новую ячейку и запустив ее, она распечатает все данные в нашем наборе данных в виде DataFrame
для нас.
Но мы не всегда хотим видеть все данные, иногда просто хотим видеть первые несколько данных и имена их столбцов. Мы можем использовать df.head()
функцию, чтобы напечатать первые пять столбцов и df.tail()
распечатать последние пять. Вывод любого из двух будет выглядеть как таковой;

Если мы хотим проверить наличие связей между этими несколькими строками и столбцами данных, функция describe()
поможет нам в этом.
Запуск df.describe()
дает следующий вывод;

Сразу видим, что функция describe()
дает среднее, стандартное отклонение, минимальное и максимальное значения.
Также можем проверить форму нашего 2D DataFrame, чтобы узнать, сколько у него строк и столбцов. Можем сделать это, используя df.shape()
который возвращает кортеж в формате (строки, столбцы).
Мы также можем проверить имена всех столбцов в нашем DataFrame, используя df.columns()
.
Что если мы хотим выбрать только один столбец и вернуть все данные в нем? Это сделано способом, похожим на прорезание словаря. Введите следующий код в новую ячейку и запустите его
df['price ']
Приведенный выше код возвращает столбец цены, мы можем пойти дальше, сохранив его в новой переменной
price = df['price']
Теперь мы можем выполнить любое другое действие, которое может быть выполнено в DataFrame с нашей ценовой переменной, поскольку оно является лишь подмножеством фактического DataFrame. Мы можем использовать такие функции, как df.head()
df.shape()
т.д.
Также можем выбрать несколько столбцов, передав список имен столбцов в df
как таковой
data = df[['price ', 'bedrooms']]
Приведенный выше выбор столбцов с именами «цена» и «спальни», если мы введем в data.head()
новую ячейку, у нас будет следующее

Вышеупомянутый способ нарезки столбцов возвращает все элементы строк в этом столбце, что если мы хотим вернуть подмножество строк и подмножество столбцов из нашего набора данных? Это можно сделать с помощью iloc
индексации и аналогично спискам Python. Таким образом, мы можем сделать что-то вроде
df.iloc[50: , 3]
Который возвращает 3-й столбец от 50-го ряда до конца. Это довольно аккуратно и точно так же, как нарезка списков в Python.
Теперь давайте сделаем несколько действительно интересных вещей: в нашем наборе данных о ценах на жилье есть столбец, в котором указывается цена дома, а в другом столбце — количество спален в конкретном доме. Цена на жилье является постоянной величиной, поэтому возможно, что у нас нет двух домов с одинаковой ценой. Но количество спален несколько дискретно, поэтому у нас может быть несколько домов с двумя, тремя, четырьмя спальнями и т.д.
Что если мы хотим получить все дома с одинаковым количеством спален и определить среднюю цену каждой отдельной спальни? Это относительно легко сделать в Pandas:
df.groupby('bedrooms ')['price '].mean()
Вышеупомянутые сначала группируют DataFrame по наборам данных с идентичным номером спальни, используя df.groupby()
функцию, затем мы говорим, что мы даем нам только столбец спальни и используем mean()
функцию, чтобы найти среднее значение каждого дома в наборе данных.
Что если мы хотим визуализировать вышесказанное? Мы хотели бы иметь возможность проверить, как меняется средняя цена каждого отдельного номера спальни? Нам просто нужно связать предыдущий код с plot()
функцией:
df.groupby('bedrooms ')['price '].mean().plot()
У нас будет вывод, который выглядит таковым;

График показывает нам некоторые тенденции в данных. На горизонтальной оси у нас есть различное количество спален (обратите внимание, что более чем один дом может иметь Х количество спален). На вертикальной оси мы имеем среднее значение цен в отношении соответствующего количества спален на горизонтальной ось. Теперь мы можем сразу заметить, что дома с 5-10 спальнями стоят намного дороже, чем дома с 3 спальнями. Также станет очевидным, что дома с 7 или 8 спальнями стоят намного больше, чем дома с 15, 20 или даже 30 комнатами.
Информация, подобная вышеприведенной, объясняет, почему анализ данных очень важен, мы можем извлечь полезную информацию из данных, которые не сразу или совсем невозможно заметить без анализа.
Отсутствующие данные
Давайте предположим, что я провожу опрос, который состоит из серии вопросов. Я делюсь ссылкой на опрос с тысячами людей, чтобы они могли высказать свое мнение. Моя конечная цель — провести анализ данных на этих данных, чтобы я мог получить некоторые ключевые выводы из этих данных.
Теперь многое может пойти не так, некоторые геодезисты могут чувствовать себя неловко, отвечая на некоторые мои вопросы, и оставить это поле пустым. Многие люди могут сделать то же самое для нескольких частей моего опроса. Это не может считаться проблемой, но представьте, что если бы я собирал числовые данные в своем опросе, а часть анализа требовала, чтобы я получил либо сумму, среднее значение, либо какую-то другую арифметическую операцию. Несколько пропущенных значений приведут к большому количеству неточностей в моем анализе, я должен найти способ найти и заменить эти пропущенные значения некоторыми значениями, которые могут быть их близкой заменой.
Pandas предоставляют нам функцию для поиска пропущенных значений в вызываемом DataFrame isnull()
.
df.isnull()
Оно возвращает DataFrame с логическими значениями, который сообщает нам, действительно ли изначально присутствующие данные отсутствовали. Вывод будет выглядеть таким:

Нам нужен способ заменить все эти пропущенные значения, чаще всего выбор пропущенных значений можно принять за ноль. Иногда это может быть принято как среднее значение всех других данных или, возможно, среднее значение данных вокруг него, в зависимости от варианта использования анализируемых данных.
Чтобы заполнить все пропущенные значения в DataFrame, мы используем функцию fillna()
:
df.fillna(0)
Выше мы заполняем все пустые данные значением ноль. Это может быть любой другой номер, который мы указали.
Важность анализа не может быть переоценена, он помогает нам получить ответы прямо из наших данных! Многие утверждают, что анализ данных — это новая нефть для цифровой экономики.
Все примеры в этой статье можно найти здесь
.

Язык программирования Python
в последнее время все чаще используется для анализа данных, как в науке, так и коммерческой сфере. Этому способствует простота языка, а также большое разнообразие открытых библиотек.
И так, таблица с наблюдениями имеет следующие столбцы:
- datetime — дата появления объекта
- city — город в котором появился объект
- state — штат
- country — страна
- duration (seconds) — время на которое появился объект в секундах
- duration (hours/min) — время на которое появился объект в часах/минутах
- shape — форма объекта
- comments — коментарий
- date posted — дата публикации
- latitude — широта
- longitude — долгота
Для тех, кто хочет пробовать нуля, подготовим рабочее место. У меня на домашнем ПК стоит Ubuntu, поэтому покажу для нее. Для начала нужно установить сам интерпретатор Python3
с библиотеками. В убунту подобном дистрибутиве это будет:
sudo apt-get install python3
sudo apt-get install python3-pip
pip
— это система управления пакетами, которая используется для установки и управления программными пакетами, написанными на Python. С её помощью устанавливаем библиотеки, которые будем использовать:
sklearn
— библиотека, алгоритмов машинного обучения, она понадобится нам в дальнейшем для классификации исследуемых данных,matplotlib
— библиотека для построения графиков,pandas
— библиотека для обработки и анализа данных. Будем использовать для первичной обработки данных,numpy
— математическая библиотека с поддержкой многомерных массивов,yandex-translate
— библиотека для перевода текста, через yandex API (для использования нужно получить API ключ в яндексе),pycountry
— библиотека, которую будем использовать для преобразования кода страны в полное название страны,
Используя pip
пакеты ставятся просто:
pip3 install sklearn
pip3 install matplotlib
pip3 install pandas
pip3 install numpy
pip3 install yandex-translate
pip3 install pycountry
Файл DataSet — scrubbed.csv
должен лежать в рабочей директории, где создается файл программы.
Итак приступим. Подключаем модули, которые используются нашей программой. Модуль подключается с помощью инструкции:
import <название модуля>
Если название модуля слишком длинное, и/или не нравится по соображениям удобства или политическим убеждениямм, то с помощью ключевого слова as
для него можно создать псевдоним:
import <название модуля> as <псевдоним>
Тогда, чтобы обратиться к определенному атрибуту, который определен в модуле
<название модуля>.<Атрибут>
<псевдоним>.<Атрибут>
Для подключения определенных атрибутов модуля используется инструкция from
. Для удобства, чтобы не писать названия модуля, при обращении к атрибуту, можно подключить нужный атрибут отдельно.
from <Название модуля> import <Атрибут>
Подключение нужных нам модулей:
import pandas as pd
import numpy as np
import pycountry
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
from yandex_translate import YandexTranslate # Используем класс YandexTranslate из модуля yandex_translate
from yandex_translate import YandexTranslateException # Используем класс YandexTranslateException из модуля yandex_translate
Для того, что бы улучшить наглядность графиков, напишем вспомогательную функцию для генерации цветовой схемы. На входе функция принимает количество цветов, которое необходимо сгенерировать. Функция возвращает связный список с цветами.
# Размер надписей на графиках
PLOT_LABEL_FONT_SIZE = 14
# Генерация цветовой схемы
# Возвращает список цветов
def getColors(n):
COLORS = []
cm = plt.cm.get_cmap('hsv', n)
for i in np.arange(n):
COLORS.append(cm(i))
return COLORS
Для перевода некоторых названий с англиского на русский язык создадим функцию translate
. И да, нам понадобится интернет, чтобы воспользоваться API переводчика от Яндекс
.
Функция принимает на вход аргументы:
- string
— строка, которую нужно перевести, - translator_obj
— объект в котором реализован переводчик, если равен None
, то строка не переводится.
и возвращает переведенную на русский язык строку.
def translate(string, translator_obj=None):
if translator_class == None:
return string
t = translator_class.translate(string, 'en-ru')
return t['text'][0]
Инициализация объекта переводчика должна быть в начале кода.
YANDEX_API_KEY = 'Здесь должен быть определен API ключ !!!!!'
try:
translate_obj = YandexTranslate(YANDEX_API_KEY)
except YandexTranslateException:
translate_obj = None
YANDEX_API_KEY
— это ключ доступа к API Yandex, его следует получить в Яндексе. Если он пустой, то объект translate_obj
инициализируется значением None
и перевод будет игнорироваться.
Напишем еще одну вспомогательную функцию для сортировки объектов dict
.
dict
— представляет собой встроенный тип Python, где данные хранятся в виде пары ключ-значения. Функция сортирует словарь по значениям в убывающем порядке и возвращает отсортированные список ключей и соответсвуюущий ему по порядку следования элементов список значений. Эта функция будет полезна при построении гистограмм.
def dict_sort(my_dict):
keys = []
values = []
my_dict = sorted(my_dict.items(), key=lambda x:x[1], reverse=True)
for k, v in my_dict:
keys.append(k)
values.append(v)
return (keys,values)
Мы добрались до непосредственно данных. Для чтения файла с таблицей используем метод read_csv
модуля pd
. На вход функции подаем имя csv
файла, и чтобы подавить предупреждения при чтении файла, задаем параметры escapechar
и low_memory
.
- escapechar
— символы, которые следует игнорировать - low_memory
— настройка обработки файла. Задаем False
для считывание файла целиком, а не частями.
df = pd.read_csv('./scrubbed.csv', escapechar='`', low_memory=False)
В некоторых полях таблицы есть поля со значением None
. Этот встроенный тип, обозначающий неопределенность, поэтому некоторые алгоритмы анализа могут работать некорректно с этим значением, поэтому произведем замену None
на строку ‘unknown’
в полях таблицы. Эта процедура называется импутацией
.
import <название модуля> as <псевдоним>Поменяем коды стран на названия на русском языке с помощью библиотеки
Переведем все названия видов объектов на небе на русский язык.
pip3 install sklearn
pip3 install matplotlib
pip3 install pandas
pip3 install numpy
pip3 install yandex-translate
pip3 install pycountry
Первичную обработку данных на этом завершаем.
import <название модуля>
Больше всего наблюдений естественно в США. Тут ведь оно как, все гики, которые следят за НЛО живут в США (о версии, что таблица составлялась гражданами США, лукаво умолчим). Судя по количеству американских фильмов скорее всего второе. От Кэпа: если инопланетяне действительно посещали землю в открытую, то вряд ли бы их заинтересовала одна страна, сообщение об НЛО появлялись бы из разных стран.
Интересно еще посмотреть в какое время года наблюдали больше всего объектов. Есть резонное предположение, что больше всего наблюдений в весеннее время.
Посмотрим какие формы объектов на небе видели и сколько раз.
Из графика мы видим, что больше всего на небе видели просто свет, который в принципе необязательно является НЛО. Этому явлению существует внятное объяснение, например, ночное небо отражает свет от прожекторов, как в фильмах про Бэтмена. Также это вполне может быть северным сиянием, которое появляется не только в полярной зоне, но и в средних широтах, а изредка даже и в близи эвкватора. Вообще атмосфера земли пронизана множеством излучений различной природы, электрическими и магнитными полями.
Вообще, атмосфера Земли пронизана множеством излучений различной природы, электрическими и магнитными полями.
Подробнее см.: https://www.nkj.ru/archive/articles/19196/ (Наука и жизнь, Что светится на небе?)
Интересно еще посмотреть среднее время, на которое в небе появлялся каждый из объектов.
shapes_durations_dict = {}
for i in shapes_type_count_keys:
dfs = df[['duration (seconds)', 'shape']].loc[df['shape'] == i]
shapes_durations_dict[i] = dfs['duration (seconds)'].mean(axis=0)/60.0/60.0
shapes_durations_dict_keys = []
shapes_durations_dict_values = []
for k in shapes_type_count_keys:
shapes_durations_dict_keys.append(k)
shapes_durations_dict_values.append(shapes_durations_dict[k])
plt.title('Среднее время появление каждого объекта', fontsize=12)
plt.bar(np.arange(OBJECT_COUNT), shapes_durations_dict_values, color=getColors(OBJECT_COUNT))
plt.xticks(np.arange(OBJECT_COUNT), shapes_durations_dict_keys, rotation=90, fontsize=16)
plt.ylabel('Среднее время появления в часах', fontsize=12)
plt.show()

Из диаграммы видими, что больше всего в небе в среднем висел конус (более 20 часов). Если покопаться в интернетах, то ясно, что конусы в небе, это тоже свечение, только в виде конуса (неожиданно, да?). Вероятнее всего это свет от падающих комет. Среднее время больше 20 часов — это какая-то нереальная величина. В исследуемых данных большой разброс, и вполне могла вкраться ошибка. Несколько очень больших, неверных значений времени появления могут существенно исказить расчет среднего значения. Поэтому при больших отклонениях, считают не среднее значение, а медиану
.
Медиана
— это некоторое число, характеризующее выборку, одна половина в выборке меньше этого числа, другая больше. Для расчета медианы используем функцию median
.
Заменим в коде выше:
shapes_durations_dict[i] = dfs['duration (seconds)'].mean(axis=0)/60.0/60.0
shapes_durations_dict[i] = dfs['duration (seconds)'].median(axis=0)/60.0/60.0

Полумесяц видели в небе чуть больше 5-ти часов. Другие объекты не надолго промелькнули в небе. Это уже наиболее достоверно.
Для первого знакомства с методологией обработки данных на Python, думаю, достаточно. В следующих публикациях займемся статистическим анализом, и постараемся выбрать другой не менее актуальный пример.
Ссылка на архив с данными и блоткнотом Jupyter
Библиотека для предварительной обработки данных
Библиотека алгоритмов машинного обучения
Библиотека для визуализации данных
Туториал на русском языке по работе с Jupyter
Pandas — главная Python-библиотека для анализа данных. Она быстрая и мощная: в ней можно работать с таблицами, в которых миллионы строк. Вместе с Марией Жаровой, ментором проекта на курсе по Data Science
, рассказываем про команды, которые позволят начать работать с реальными данными.
Среда разработки
Pandas работает как в IDE (средах разработки), так и в облачных блокнотах для программирования. Как установить библиотеку в конкретную IDE, читайте тут
. Мы для примера будем работать в облачной среде Google Colab. Она удобна тем, что не нужно ничего устанавливать на компьютер: файлы можно загружать и работать с ними онлайн, к тому же есть совместный режим для работы с коллегами. Про Colab мы писали в этом обзоре
.
Пройдите тест и узнайте, какой вы аналитик данных и какие перспективы вас ждут. Ссылка в конце статьи.