В этом посте мы будем использовать библиотеку scikit-learn для машинного обучения, которое поможет нам решить довольно интересную задачу. Если вы хотите узнать больше об этой библиотеке, пожалуйста, ознакомьтесь с этим постом. Все знают о затонувшем где-то в двадцатом веке корабле Титаник, теперь можно с приличной точностью предсказать, выжил кто-то или нет. Особенность этой проблемы заключается в том, что в Титанике разумно предположить, что разные социальные группы имели разные шансы на выживание.

Решение этой проблемы с помощью методов машинного обучения займет два поста. В первом мы будем иметь дело с пониманием и предварительной обработкой данных, а второй пост будет посвящен фактическому созданию моделей машинного обучения для решения задачи.

Знакомство с нашими данными

Набор данных, используемый для этой задачи, можно найти здесь. После того, как вы загрузили данные, должен появиться файл с именем train.csv, который мы будем использовать для разработки наших моделей.

Библиотека Pandas — один из лучших вариантов для обработки данных в Python. Давайте используем его для чтения набора данных внутри нашего файла. Pandas загрузит данные в объект DataFrame, который является основным типом объекта внутри Pandas.

Следующий код загружает данные и показывает нам некоторую информацию о них:

import pandas as pd
titanic = pd.read_csv("train.csv")
titanic.info()

Результат выполнения этой команды:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB

Здесь мы можем видеть все поля, которые находятся внутри набора данных. В следующих таблицах объясняется, что означает каждое поле.

Чтобы получить более конкретное представление о данных, мы можем использовать метод описания внутри Pandas, который работает следующим образом применительно к этому набору данных.

Предварительная обработка данных

Первое, что мы должны сделать, это разделить эти данные на обучающий, проверочный и тестовый наборы.

tit_train, tit_test = train_test_split(titanic, test_size = .2, random_state=42)

Обратите внимание, что мы назначаем фиксированное случайное состояние, чтобы иметь возможность воссоздать те же условия.

Ну, во-первых, нам нужно заметить, что данные не полные, это означает, что некоторые поля могут иметь значение NaN для некоторых пассажиров. Есть два варианта решения этой проблемы: один — стереть всех пассажиров с полем, имеющим значение NaN, а другой — заменить это значение NaN допустимым значением по нашему выбору. Есть поля, которые достаточно важны, чтобы мы могли решить, что если у пассажира нет действительного значения, его не следует рассматривать.

Используя библиотеку Scikit-learn, мы можем использовать некоторые методы, которые пригодятся, чтобы подготовить данные для алгоритмов машинного обучения.

Структура следующего кода сделана так, чтобы оставаться общей и применимой к любому набору данных, а не только к «Титанику».

import numpy as np
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.preprocessing import Imputer,LabelBinarizer, StandardScaler, LabelEncoder
from sklearn.base import BaseEstimator, TransformerMixin

# Ignore the columns that are not relevant
num_fields = ['Pclass','Age','SibSp','Parch','Fare']
cat_fields = ['Sex']
class Validator(BaseEstimator, TransformerMixin):
    """
    This transformer erases the instances that contain NaN in the given
    fields.
    Receives a DataFrame object.
    """
    def __init__(self, relevant_attributes):
        self.relevant_attributes = relevant_attributes
    
    def fit(self, X):
        return self
    
    def transform(self, X):
        nan_mat = X.isna()[self.relevant_attributes]
        nan_idx = nan_mat[np.max(nan_mat, axis=1)==True].index        
        return X.drop(nan_idx)
    
class DataFrameSelector(BaseEstimator, TransformerMixin): 
    """
    This transformer handles a DataFrame and returns the selected attributes
    as a numpy array.
    """
    def __init__(self, attribute_names, categorical = False):
        self.attribute_names = attribute_names
        self.categorical = categorical
    
    def fit(self, X, y=None):
        return self
    
    def transform(self, X):
        if not self.categorical:            
            return X[self.attribute_names].values
        cat_d = X[self.attribute_names].values
        l = LabelBinarizer()
        cat_d = l.fit_transform(cat_d)
        return cat_d

Класс Validator — это тот, который занимается удалением неполных строк из DataFrame, а DataFrameSelector позволяет нам получить пустой массив, который в конечном итоге будет использоваться для обучения моделей.

В библиотеке scikit-learn есть концепция конвейера, которая очень полезна в этой ситуации и может применяться следующим образом:

# Define the numeric pipeline
num_pipeline = Pipeline([
        ('valid', Validator(cat_fields)),
        ('selector', DataFrameSelector(num_fields)), 
        ('imputer',Imputer(strategy="median")),
        ('stdscaler', StandardScaler()),
    ])

cat_pipeline = Pipeline([ 
        ('valid', Validator(cat_fields)),
        ('data_binarize', DataFrameSelector(cat_fields, categorical = True)),
])

join_pipeline = FeatureUnion(transformer_list =[
        ("num_pipeline", num_pipeline),
        ("cat_pipeline", cat_pipeline),
    ])
trainX = join_pipeline.fit_transform(tit_train)
trainY = tit_train['Survived'].values

Как вы можете заметить, в предыдущем коде мы получаем две переменные, а именно trainX и trainY, которые содержат значения, на которых мы будем обучать наши модели в следующем посте. Увидимся там!