کدام مسافر کشتی تایتانیک زنده میماند؟
در این مسئله میخواهیم ببینیم کدام یک مسافران کشتی تایتانیک زنده میمانند. دیتاستی که در اختیار داریم شامل اطلاعاتی مرتبط به سن و جنسیت و کلاس مسافران و مواردی از این قبیل است. میخواهیم پیش بینی کنیم که آیا مسافری با یک مشخصات خاص زنده خواهد ماند یا نه.

در روش رگرسیون، هدف ما عددی(و اکثرا پیوسته) است در صورتیکه در روش لاجستیک جنس هدف ما از نوع باینری (و گسسته)هست (بلی و خیر یا صفر و یک و …)
در حل مسائل به روش رگرسیون لاجستیک از تابع Sigmoid کمک میگیریم. رفتار این تابع طوری است که مقادیر در بازه صفر تا یک قرار میگیرند.(بالای شکل زیر). همانطور که در شکل مشخص است دادههایی که در بازه صفر تا نیم قرار گیرند، صفر تلقی شده و مقادیر بالای نیم را یک در نظر میگیریم.

ابتدا کتابخانههای مورد نیاز را import کرده و دیتای خود را میخوانیم:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
train=pd.read_csv('titanic_train.csv')
--> train.head()
--> train.info()
--> train.isnull() #to find the missing Data
--> train.isnull().sum() #for counting the number of missed data in each column
# to see the missed data graphically:
--> sns.heatmap(train.isnull(),yticklabels=False,cbar=False,cmap='viridis')
تقریبا 20 درصد دیتای مرتبط با سن مسافران را نداریم (Missed Data). این مقدار به حدی کم است که میتوان با خیال آسوده آن را با مقادیر مناسب جایگزین (imputation) نمود. اما این رفتار در مورد دیتای ستون Cabin صدق نمیکند. زیرا حدود 778 درصد دیتای این ستون از نوع missed Data هستند. بنابراین ممکن است به سادگی آن را حذف کنیم یا آن را به یک فیچر جدید به نام (اطلاع از وضعیت کابین) که صفر و یک هستند، تغییر دهیم.
با دستور زیر میتوانیم ببینیم که تعداد افراد زنده مانده و فوت کرده چه تعداد هستند:
sns.countplot(x='Survived',data=train,palette='RdBu_r')
اگر بخواهیم دستور بالا را با تفکیک جنسیتی نیز بررسی کنیم به صورت زیر خواهد بود:
sns.countplot(x='Survived',hue='Sex',data=train,palette='RdBu_r')
همچنین اگر بخواهیم خطوط افقی به نمودار اضافه کنیم، از دستور زیر استفاده میکنیم:
sns.set_style('whitegrid')
همچنین اجرای دستور زیر نشان میدهد که از هر کلاس چه تعداد زنده ماندهاند:
#sns.set_style('whitegrid')
sns.countplot(x='Survived',hue='Pclass',data=train,palette='rainbow')
برای مشاهده نمودار فراوانی سن افراد از دستور زیر استفاده میکنیم. فقط از آنجا که میدانیم تعدادی از دادهها در ستون age از نوع missing هستند، این موضوع را با متودdropna حل میکنیم:
sns.distplot(train['Age'].dropna(),kde=False,bins=30)
نکته: در کد بالا عبارت inplace=true را قرار نمیدهیم. زیرا نمیخواهیم فعلا دیتای اصلی را تغییر دهیم. (موقتا برای رسم این نمودار فقط). همچنین اگر بخواهیم دستور قبلی را با کتابخانه matplotرسم کنیم به شکل زیر خواهد بود:
train['Age'].plot.hist(bins=30)
نمودار boxplot :
زمانی از نمودار boxplot استفاده میکنیم که دیتای ما از نوع عددی باشد و همزمان بتوان آن دیتا را در نوع categorical نیز قرار داد. برای مثال درست است که سن افراد یک متغیر عددی یا numerical است اما میتوان آنها را در سه دسته کودک، میانسال و کهنسال در نظر گرفت. یا در مورد دیتای مسئله تایتانیک، ما سه کلاس1 , 2 و 3 داریم(pclass) و میخواهیم اطلاعات سن این افراد را با نمودار pclass بررسی کنیم:
plt.figure(figsize=(12, 7))
sns.boxplot(x='Pclass',y='Age',data=train,palette='winter')

حال میخواهیم متوسط سنی گروه یکم (Pclass==1) تا سوم از دیتای خود را پیدا کنیم:
print(train[train['Pclass']==1]['Age'].mean())#38.23
print(train[train['Pclass']==2]['Age'].mean())#29.87
print(train[train['Pclass']==3]['Age'].mean())#25.14
دیدن افرادی که در کلاس یک قرار دارند و همچنین مشاهده سن آنها:
train[train['Pclass']==1]
train[train['Pclass']==1]['Age']
میدانیم که ستون Age دارای مقادیر missing میباشد. حال یک تابع مینویسیم که این مقادیر را با مقدار میانگین هر دسته جایگذاری کند:

حال با متود apply تابع تعریف شده را روی دیتاست خود اعمال میکنیم:
train['Age'] = train[['Age','Pclass']].apply(impute_age,axis=1)
حال مجددا نگاهی به دیتای خود میاندازیم و میبینیم که دیتای Age فاقد مقادیر missing است:
#Now let's check that heat map again!
sns.heatmap(train.isnull(),yticklabels=False,cbar=False,cmap='viridis')
با دستور زیر ستون cabin را حذف میکنیم:
#Great! Let's go ahead and drop the Cabin column and also replace the data's in Embarked column that are NaN.
train.drop('Cabin',axis=1,inplace=True)
در مورد ستون Embarked با توجه به اینکه داده آن از نوع categorical است ابتدا باید ببینیم که از هر دسته چه تعداد وجود دارد:
train['Embarked'].value_counts()
S 644
C 168
Q 77
بنابراین دسته S پرتکرارترین داده در این ستون است و گزینه مناسبی برای جایگزینی با مقادیر missing است:
train['Embarked'].replace(np.nan, 'S', inplace=True)
و در نهایت یک info از دیتای خود میگیریم تا از آخرین وضعیت مطلع شویم: (نتیجه نشان میدهد که همه ستونها تکمیل هستند.)
train.info()
train.head()
با بررسی دستورhead متوجه میشویم که تعدادی از ستونها واقعا تاثیری بر پیشبینی زنده ماندن افراد ندارند. برای مثال ستون مربوط به نام و نام خانوادگی , passengerid , ticket سه ستونی هستند که میتوان آنها را حذف نمود.
از ستونهای باقیمانده ، ستون sex و embark وpclass مواردی هستند که در زنده مانده یا نماندن مسافر تاثیر گذار هستند. اما باید مسئبه اینجاست که باید به متغیر عددی تبدیل شوند. زیرا مدل تنها میتواند با دادههای عددی آموزش ببیند.
برای حل این مسئله ازروش dummy variable استفاده میکنیم که در کدهای زیر نمایش داده شده است:
sex = pd.get_dummies(train['Sex'],drop_first=True)
embark = pd.get_dummies(train['Embarked'],drop_first=True)
pclass = pd.get_dummies(train['Pclass'],drop_first=True)
با دستور زیر ستونهایی که در محاسبات ما نقشی نداشتند و همچنین آنهایی را که تمایل به حذف آن داشتیم را حذف میکنیم:
train.drop(['PassengerId','Sex','Embarked','Name','Ticket','Pclass'],axis=1,inplace=True)
با دستور بعدی آن مواردی را که به روش dummy variable تغییر داده بودیم را در دیتای خود قرار میدهیم:
train = pd.concat([train,sex,embark,pclass],axis=1)
train.head()

همانگونه که در نتیجه نمایش داده میشود، تمام دادهها ار نوع عددی (numerical) هستند. به دادهای که اکنون در دسترس داریم preprocessed data گویند.
آموزش دادن مدل:
ابتدا کتابخانه مورد نظر خود را ایمپورت میکنیم:
from sklearn.model_selection import train_test_split
و با دستور زیر دیتای خود را به چهار قسمت تقسیم میکنیم:
X_train, X_test, y_train, y_test = train_test_split\
(train.drop('Survived',axis=1), \
train['Survived'], test_size=0.30, \
random_state=101)
1-دسته ایکسها: هفتاد درصد کل train به جز ستون survived
2- دسته ایکس train : سی درصد کل train به جز ستون survived
3- دسته y_train: هفتاد درصد ستون survived
4- دسته y_test : سی درصد ستون survived
با دستورات زیر ، سیستم را تعلیم میدهیم:
from sklearn.linear_model import LogisticRegression
logmodel = LogisticRegression()
logmodel.fit(X_train,y_train)
predictions = logmodel.predict(X_test)
حال با دستور زیر سیستم خود را تست میکنیم:
predictions = logmodel.predict(X_test)
predictions
y_test
با استفاده از confusion matrix میخواهیم سیستم خود را تست کنیم:
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test,predictions)
چند تعریف در سامانه ارزیابی Confusion Matrix:
true positive : زمانی که به درستی خروجی 1 بدست آمده باشد(پیش بینی شده باشد)
true negative : زمانی که به درستی خروجی صفر بدست آمده باشد.
false positive : زمانی که خروجی یک بدست آمده باشد، اما غلط باشد.
false negative: زمانی که خروجی صفر بدست آمده باشد، اما غلط باشد.

خروجی دستور زیر نشان میدهد که میزان دقت پیشبینی سیستم ما چقدر بوده است:
confusion_matrix(y_test,predictions)
که نشان میدهد 137 مورد true positive و 76 مورد true negative داریم.
اما این موارد ممکن است به تنهایی معیار خوبی برای بررسی میزان دقت ما نباشد. به همین دلیل از موارد زیر نیز ممکن است استفاده کنیم:

یعنی نسبت کل Positiveها نسبت به کل پیشبینیها
پارامتر Precision:

که نشان دهنده کل true positiveها به کل positive هاست
recall :
نسبت آنچه مدل درست گفته به آنچه باید درست میگفته:)

شاید باز هم پیچیده شده باشد. اما فرمول زیر که به آن F1 Score گویند عددی است که در نهایت ما برای بررسی میزان دقت سیستم طراحی شده به آن رجوع میکنیم:

نکته:
بررسی دقت متناسب با نوع مسئله ممکن است متفاوت باشد. برای مثال در مورد تشخیص سرطان ،ما ترجیح میدهیم به تعداد اشتباهات مثبت (پیشبینی سرطان داشتن به اشتباه) بیشتر از حالت اشتباه منفی(پیشبینی عدم سرطان به اشتباه) باشد.
گزارش کامل از دقت سیستم:
کتابخانه scikit_learn با دستورات زیر گزارش کاملی از دقت سیستم را به ما میدهد:
from sklearn.metrics import classification_report
print(classification_report(y_test,predictions))
منابع و فایلها: