Космический корабль «Титаник» — межзвездный пассажирский лайнер, запущенный месяц назад. С почти 13 000 пассажиров на борту судно отправилось в свой первый рейс, доставив эмигрантов из нашей Солнечной системы к трем новым обитаемым экзопланетам, вращающимся вокруг ближайших звезд.
Обогнув Альфу Центавра по пути к своему первому пункту назначения — жаркому 55 Cancri E — неосторожный космический корабль «Титаник» столкнулся с пространственно-временной аномалией, скрытой в пылевом облаке. К сожалению, его постигла та же участь, что и его тезку 1000 лет назад. Хотя корабль остался цел, почти половина пассажиров была перенесена в другое измерение!
Цель
Чтобы помочь спасательным экипажам и найти потерянных пассажиров, мы должны предсказать, какие пассажиры были перевезены аномалией, используя записи, восстановленные из поврежденной компьютерной системы космического корабля.
Загрузка данных
Импорт библиотек
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
import os
import math
import plotly
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split, RandomizedSearchCV
!pip install -q lazypredict
from lazypredict.Supervised import LazyClassifier
!pip install -q kaggle
Используйте Kaggle API для получения данных
from google.colab import files # Upload kaggle API file files.upload() # Choose the kaggle.json file obtained from kaggle API ! mkdir ~/.kaggle ! cp kaggle.json ~/.kaggle/ # Make directory named kaggle and copy kaggle.json file there ! chmod 600 ~/.kaggle/kaggle.json
Saving kaggle.json to kaggle.json mkdir: cannot create directory ‘/root/.kaggle’: File exists
Загрузить данные
# Get the data from the Kaggle Spaceship Titanic competition !kaggle competitions download -c spaceship-titanic
Downloading spaceship-titanic.zip to /content 0% 0.00/299k [00:00<?, ?B/s] 100% 299k/299k [00:00<00:00, 86.8MB/s]
# Unzip the data ! unzip spaceship-titanic.zip -d spaceship-titanic
Archive: spaceship-titanic.zip inflating: spaceship-titanic/sample_submission.csv inflating: spaceship-titanic/test.csv inflating: spaceship-titanic/train.csv
# Load the train and test data from csv files into dataframes train = pd.read_csv('/content/spaceship-titanic/train.csv') train2 = train test = pd.read_csv('/content/spaceship-titanic/test.csv') # Make a dataframe for the end result submission = test[['PassengerId']] train.head()
Проблеск данных о поезде.
# Look into the train dataframe's shape print('The shape of train data:', train.shape) print('The shape of test data:', test.shape)
The shape of train data: (8693, 14) The shape of test data: (4277, 13)
Данные поезда содержат 14 функций и 8693 записи.
# Get more info about the data train.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 8693 entries, 0 to 8692 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 PassengerId 8693 non-null object 1 HomePlanet 8492 non-null object 2 CryoSleep 8476 non-null object 3 Cabin 8494 non-null object 4 Destination 8511 non-null object 5 Age 8514 non-null float64 6 VIP 8490 non-null object 7 RoomService 8512 non-null float64 8 FoodCourt 8510 non-null float64 9 ShoppingMall 8485 non-null float64 10 Spa 8510 non-null float64 11 VRDeck 8505 non-null float64 12 Name 8493 non-null object 13 Transported 8693 non-null bool dtypes: bool(1), float64(6), object(7) memory usage: 891.5+ KB
Данные поезда имеют 14 функций (столбцов), 1 типа boolean, 6 типа float и 7 типа object. Только столбцы «PassangerId» и «Transported» содержат ненулевые записи. В других столбцах не все записи не равны нулю.
Разделение функций
# Make function for feature splitting
def split(df):
df['PassengerId_Group'] = df['PassengerId'].str.split("_", expand = True)[0].astype(np.int)
df['PassengerId_Number'] = df['PassengerId'].str.split("_", expand=True)[1].astype(np.int)
df['Cabin_Deck'] = df['Cabin'].str.split("/", expand = True)[0].astype("string")
df['Cabin_Num'] = df['Cabin'].str.split("/", expand = True)[1].astype("string")
df['Cabin_Side'] = df['Cabin'].str.split("/", expand = True)[2].astype("string")
return df
Функция «PassangerId» состоит из 2 частей, а именно «Группы» и «Номера», разделенных подчеркиванием. Функция «Кабина» состоит из 3 частей, а именно «Палуба», «Номер» и «Сторона», разделенных косой чертой. Эта функция разбивает эти части на отдельные столбцы.
# Apply split function
train=split(train)
Разделить некоторые функции на данные о поездах
Отсутствующие значения
# Count the number of missing values on each features train.isnull().sum()
PassengerId 0 HomePlanet 201 CryoSleep 217 Cabin 199 Destination 182 Age 179 VIP 203 RoomService 181 FoodCourt 183 ShoppingMall 208 Spa 183 VRDeck 188 Name 200 Transported 0 PassengerId_Group 0 PassengerId_Number 0 Cabin_Deck 199 Cabin_Num 199 Cabin_Side 199 dtype: int64
Наибольшее количество нулевых значений в признаке — 217 из 8693 записей (2,49%), это функция CryoSleep. Это совсем небольшая сумма, так что это не будет проблемой.
# Handle missing values
def imputeNull(df): # Imputes null values
# Categorical columns
df['HomePlanet'].fillna(df['HomePlanet'].mode(), inplace = True)
df['CryoSleep'].fillna(df['CryoSleep'].mode(), inplace = True)
df['Destination'].fillna(df['Destination'].mode(), inplace = True)
df['VIP'].fillna(df['VIP'].mode(), inplace=True)
df['Cabin_Deck'].fillna(df['Cabin_Deck'].mode(), inplace = True)
df['Cabin_Side'].fillna(df['Cabin_Side'].mode(), inplace = True)
# Numerical columns
df[df['CryoSleep'] == True][['RoomService', 'FoodCourt', 'ShoppingMall',
'Spa', 'VRDeck']].fillna(0, inplace = True)
# If passanger asleep, any expenses will be 0
df['Age'].fillna(df['Age'].mean(), inplace = True)
df['RoomService'].fillna(df['RoomService'].median(), inplace = True)
df['FoodCourt'].fillna(df['FoodCourt'].median(), inplace = True)
df['ShoppingMall'].fillna(df['ShoppingMall'].median(), inplace = True)
df['Spa'].fillna(df['Spa'].median(), inplace = True)
df['VRDeck'].fillna(df['VRDeck'].median(), inplace = True)
#df['Cabin_Num'].fillna(df['Cabin_Num'].median(), inplace=True)
return df
# Some columns will be deleted before this function is used so no null values handling is made on those columns
Нулевые значения заменены из категориальных столбцов режимом записей и медианой записей для числовых столбцов, за исключением столбцов «RoomService», «FoodCourt», «ShoppingMall», «Spa», «VRDeck», для которых нулевые записи заменяются на 0, если статус «CyroSleep» имеет значение «Истина», потому что сумма не взимается, если кто-то находится в режиме cyro sleep (не пользуется какими-либо удобствами).
Исследовательский анализ данных
Уникальные значения
# Number of unique values for numerical features print('Unique values for numerical features\n',train.select_dtypes(include = "number").nunique().sort_values(),"\n") # Number of unique values for categorical features print('Unique values for categorical features\n',train.select_dtypes(exclude = "number").nunique().sort_values())
Unique values for numerical features PassengerId_Number 8 Age 80 ShoppingMall 1115 RoomService 1273 VRDeck 1306 Spa 1327 FoodCourt 1507 PassengerId_Group 6217 dtype: int64 Unique values for categorical features CryoSleep 2 VIP 2 Transported 2 Cabin_Side 2 HomePlanet 3 Destination 3 Cabin_Deck 8 Cabin_Num 1817 Cabin 6560 Name 8473 PassengerId 8693 dtype: int64
Группа пассажиров 6217 человек. Столбцы «RoomService», «FoodCourt», «ShoppingMall», «Spa», «VRDeck» имеют много уникальных значений, поскольку они представляют сумму денег, выставленную в счете.
Имеется 1817 номеров кают. «Кабина», «Имя», «PassengerId», «Cabin_Num» представляют собой идентификатор, поэтому они имеют много уникальных значений.
# Function that plots count of the entries of every unique values in categorical column that has relatively few unique values def view_categorical(df, categorical_threshold): # List every non-numerical column that has number of unique values below certain threshold categorical_features = df.select_dtypes(exclude = "number").nunique()[df.select_dtypes( exclude = "number").nunique() <= categorical_threshold].index.tolist() # Create subplots rows = math.ceil(len(categorical_features) / 4) plt.subplots(rows,4,figsize = (13,5)) # Make bar plot for i in range(len(categorical_features)): plt.subplot(rows,4,i + 1) df[categorical_features[i]].value_counts().sort_values().plot.bar() value_counts = df[categorical_features[i]].value_counts(normalize=True).sort_values() ax = value_counts.plot.bar() # print() # for j, v in enumerate(value_counts): # ax.containers: print(j) # height = j.get_height() # ax.bar_label(j, label=f"{v*100/value_counts.sum():.2f}%", font_size=8) plt.xticks(rotation = 45) plt.title(categorical_features[i]) plt.tight_layout() plt.show()
# Plot categorical columns in train data categorical_threshold = 16 view_categorical(train, categorical_threshold)
Графики выше показывают количество всех уникальных значений, исключая нулевые значения, в категориальном столбце с относительно небольшим количеством уникальных значений, ограниченным пороговым значением. На родной планете доминирует «Земля», за ней следует «Европа», затем «Марс». Что касается пункта назначения, наиболее популярным является «TRAPPIST-1e», за ним следует «55 Cancri 3», затем «PSO J318.5–22». Самая популярная каютная палуба — «F», а наименее — «T». В то время как борта кабины почти поровну распределены между «P» и «S». Большинство пассажиров не находятся в крио-сне. Лишь немногие из них являются VIP. Статус пассажира в транспортировке показывает почти одинаковые значения между транспортируемым и нетранспортированным.
# Plot histogram for all numerical features
train.hist(bins = 30, figsize = (13,4), layout = (-1,4), edgecolor = 'k')
plt.tight_layout()
Возраст пассажиров распределяется неравномерно, выглядит смещенным влево и имеет тенденцию накапливаться между 18–40 годами. Младенцев также больше, чем других молодых пассажиров и пожилых пассажиров. Для столбцов «RoomService», «FoodCourt», «ShoppingMall», «Spa», «VRDeck» записи накапливаются с низким значением, с подавляющим числом со значением 0, что означает, что большинство пассажиров не тратят деньги на удобства космического корабля. Пассажиры по группам распределены практически поровну. Однако, поскольку он показывает только идентификацию, распределение не имеет существенного значения, равно как и «Номер пассажира».
Построить модель
Обработка выбросов
# Function that checks and removes outliers def handle_outliers(df): df2 = df df = df[(df.select_dtypes(include = 'number').columns).to_list()] # Calculate the thresholds using quartile upper_threshold = df.quantile(0.75) + 1.5 * (df.quantile(0.75) - df.quantile(0.25)) lower_threshold = df.quantile(0.25) - 1.5 * (df.quantile(0.75) - df.quantile(0.25)) for i in df.select_dtypes(include = 'number'): # Check outliers x = df[(df[i] < lower_threshold[i]) | (df[i] > upper_threshold[i])][i].to_list() print('No of outliers (lower than',round(lower_threshold[i],2),'or greater than',round(upper_threshold[i],2),') presesnt in column', i, ":",len(x), '\n') # Keep entries that are not outliers df2 = df2[((df[i] >= lower_threshold[i]) & (df[i] <= upper_threshold[i]))] return df2
# The original shape of the train data print('Original data shape:',train.shape) # Check and remove outliers from train data train = handle_outliers(train) # The shape of train data after outliers removed print('Data shape after outliers removal:',train.shape)
Original data shape: (8693, 19) No of outliers (lower than -9.5 or greater than 66.5 ) presesnt in column Age : 77 No of outliers (lower than -70.5 or greater than 117.5 ) presesnt in column RoomService : 1861 No of outliers (lower than -114.0 or greater than 190.0 ) presesnt in column FoodCourt : 1823 No of outliers (lower than -40.5 or greater than 67.5 ) presesnt in column ShoppingMall : 1829 No of outliers (lower than -88.5 or greater than 147.5 ) presesnt in column Spa : 1788 No of outliers (lower than -69.0 or greater than 115.0 ) presesnt in column VRDeck : 1809 No of outliers (lower than -4527.0 or greater than 13729.0 ) presesnt in column PassengerId_Group : 0 No of outliers (lower than -0.5 or greater than 3.5 ) presesnt in column PassengerId_Number : 493 Data shape after outliers removal: (2888, 19)
Разработка функций
# Delete unnecessary features
def delete_irrelenvant_features(df):
df = df.drop(columns = ['PassengerId', 'Name', 'Cabin_Num', 'Cabin'], axis = 1, errors = 'ignore')
return df
Столбцы «Кабина», «PassengerId» были разделены на другие столбцы, а столбцы «Имя», «Номер кабины» представляют собой идентификацию. Эти столбцы удалены, так как они не нужны для построения модели данных.
# Function to convert categorical columns into numerical columns def onehot_encoder(df, feature): dummies = pd.get_dummies(df[feature], prefix = feature) df = pd.concat([df,dummies], axis = 1) df = df.drop(columns = [feature], axis = 1) return df # Function to convert boolean valued columns into numerical columns def onehot_encoder_boolean(df, feature): dummies = pd.get_dummies(df[feature], prefix = feature, drop_first = True) df = pd.concat([df,dummies], axis = 1) df = df.drop(columns = [feature], axis = 1) return df # Function to convert all non-numerical columns def label_encoder_onehot(df): df = onehot_encoder(df, 'HomePlanet') df = onehot_encoder_boolean(df, 'CryoSleep') df = onehot_encoder(df, 'Destination') df = onehot_encoder_boolean(df, 'VIP') df = onehot_encoder(df, 'Cabin_Deck') df = onehot_encoder(df, 'Cabin_Side') return df
# Function to replace spaces with underlines def replaceSpaces(df): df.columns = df.columns.str.replace(' ', '_') return df
# Function to do all of the preprocessing functions for train data def preProcess(df, imputingNulls = True): df = split(df) df = delete_irrelenvant_features(df) df = handle_outliers(df) if imputingNulls == True: df = imputeNull(df) df = label_encoder_onehot(df) df = replaceSpaces(df) return df
# Function to do all of the preprocessing functions for test result (without dropping any rows) def preProcess_analytics(df, imputingNulls = True): df = split(df) df = delete_irrelenvant_features(df) if imputingNulls == True: df = imputeNull(df) df = label_encoder_onehot(df) df = replaceSpaces(df) return df
Подготовка данных
# Do data prepatation to build the model
y = train['Transported']
X = train.drop(columns = 'Transported', axis = 1)
X_train , X_test , y_train , y_test = train_test_split(X, y, random_state = 5, test_size = 0.40)
Колонка «Перевезен» указывает на то, доставлен ли пассажир в пункт назначения или нет. Этот столбец состоит из логических значений. Это целевой столбец, который мы собираемся предсказать, поэтому мы отделяем этот столбец от данных поезда в другую переменную.
Затем основные данные поезда разделяются на данные поезда и тестовые данные для построения модели.
# Apply the preprocessing function to splitted train data X_train = preProcess(X_train, imputingNulls = True) X_test = preProcess(X_test, imputingNulls = True) # Apply the preprocessing function to the main train data X_preProcessed = preProcess_analytics(X, imputingNulls = True) # Apply the preprocessing function to the test data we are about to predict data_test_preProcessed = preProcess_analytics(test, imputingNulls = True)
No of outliers (lower than -15.0 or greater than 65.0 ) presesnt in column Age : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column RoomService : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column FoodCourt : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column ShoppingMall : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column Spa : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column VRDeck : 0 No of outliers (lower than -4308.0 or greater than 13614.0 ) presesnt in column PassengerId_Group : 0 No of outliers (lower than -0.5 or greater than 3.5 ) presesnt in column PassengerId_Number : 0 No of outliers (lower than -16.5 or greater than 67.5 ) presesnt in column Age : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column RoomService : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column FoodCourt : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column ShoppingMall : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column Spa : 0 No of outliers (lower than 0.0 or greater than 0.0 ) presesnt in column VRDeck : 0 No of outliers (lower than -4586.5 or greater than 13841.5 ) presesnt in column PassengerId_Group : 0 No of outliers (lower than -0.5 or greater than 3.5 ) presesnt in column PassengerId_Number : 0
Функция предварительной обработки применяется к данным поезда и к тестовым данным, и мы увидим результаты прогнозов в качестве нашей конечной цели. Предварительная обработка наших основных данных поезда «X» и тестовых данных «test» не включает удаление выбросов, потому что нам нужны все записи в этих данных.
# Check the number of columns on each splitted data print(len(X_train.columns),len(X_test.columns),len(X_preProcessed.columns),len(data_test_preProcessed.columns)) # Check which columns is missing print(set(data_test_preProcessed.columns)-set(X_test.columns)) print(set(data_test_preProcessed.columns)-set(X_train.columns)) print(set(data_test_preProcessed.columns)-set(X_preProcessed.columns)) # Add the missing columns for i in (set(data_test_preProcessed.columns)-set(X_test.columns)): X_test[i] = False X_train[i] = False X_preProcessed[i] = False print(X_test.columns)
25 25 25 26 {'Cabin_Deck_T'} {'Cabin_Deck_T'} {'Cabin_Deck_T'} Index(['Age', 'RoomService', 'FoodCourt', 'ShoppingMall', 'Spa', 'VRDeck', 'PassengerId_Group', 'PassengerId_Number', 'HomePlanet_Earth', 'HomePlanet_Europa', 'HomePlanet_Mars', 'CryoSleep_True', 'Destination_55_Cancri_e', 'Destination_PSO_J318.5-22', 'Destination_TRAPPIST-1e', 'VIP_True', 'Cabin_Deck_A', 'Cabin_Deck_B', 'Cabin_Deck_C', 'Cabin_Deck_D', 'Cabin_Deck_E', 'Cabin_Deck_F', 'Cabin_Deck_G', 'Cabin_Side_P', 'Cabin_Side_S', 'Cabin_Deck_T'], dtype='object')
При построении модели машинного обучения данные поезда, тестовые данные и окончательные тестовые данные, которые мы прогнозируем, должны иметь все одинаковые столбцы. Поскольку мы выполнили кодирование столбцов, которое преобразует категориальный столбец в отдельные числовые столбцы, возможно, что этот категориальный столбец преобразуется в различное количество числовых столбцов для разных фреймов данных одного и того же типа (данные обучения и тестирования). В этом случае столбец «Cabin_Deck» имеет значение «T» в наших тестовых данных, но не в наших данных поезда. Следовательно, после кодирования этого столбца наши тестовые данные имеют на один столбец больше, чем наши данные поезда. Чтобы обойти это, к основным данным поезда добавляется новый столбец со значением 0, что означает, что ни один пассажир не находится на палубе T.
Выберите метод
# Use some classifier methods simultaneously clf = LazyClassifier(verbose = 0, ignore_warnings = True, custom_metric = None, predictions = False, random_state = 12, classifiers = 'all') models, predictions = clf.fit(X_train, X_test ,y_train , y_test) models.sort_values(by=['Accuracy'], ascending = False)
100%|██████████| 29/29 [00:03<00:00, 7.60it/s]
px.line(data_frame = models.sort_values(by=['Accuracy'], ascending = False)[:], y = ['Accuracy','Balanced Accuracy','ROC AUC','F1 Score'], markers = True)
LazyClassifier используется для моделирования данных поезда с использованием 26 методов одновременно. Используемые метрики оценки: точность, сбалансированная точность, ROC AUC и F1 Score. Результат показан в таблице.
Модель, дающая наибольшую точность, — линейный дискриминантный анализ.
Более того, все показатели относительно высоки в SGD Classifier.
Предсказывать
y_pred = lda.predict(data_test_preProcessed) submission['Transported'] = y_pred.astype("bool") submission.to_csv("submission.csv",index = False) files.download('submission.csv') submission.head() submission
y_pred = sgd.predict(data_test_preProcessed) submission['Transported'] = y_pred.astype("bool") submission.to_csv("submission.csv",index = False) # files.download('submission.csv') submission.head()
Здесь мы прогнозируем тестовые данные, используя метод, который мы выбрали ранее. Результат прогноза, показанный в таблице, также автоматически загружается в формате CSV.
Полный код показан ниже и его можно увидеть здесь.