"""
# Import Necessary Libraries
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline
"""
# Read the Dataset
"""
df = pd.read_csv('../input/titanic/train.csv')
"""
# Dataset Overview
"""
"""
| Variable | Definition | Key |
| --- | --- | --- |
| survival | Survival | 0 = No, 1 = Yes |
| pclass | Ticket class | 1 = 1st, 2 = 2nd, 3 = 3rd |
| sex | sex |  |
| Age | Age in years |  |
| sibsp | # of siblings / spouses aboard the Titanic |  |
| parch | # of parents / children aboard the Titanic |  |
| ticket | Ticket number |  |
| fare | Passenger fare |  |
| cabin | Cabin number |  |
| embarked | Port of Embarkation | C = Cherbourg, Q = Queenstown, S = Southampton |
"""
df
df.info()
df.shape
"""
# EDA
"""
df.describe()
plt.figure(figsize=(18, 11))
matrix = np.triu(df.corr())
sns.heatmap(df.corr(),center= 0, annot=True, linewidth=0.8, mask=matrix)

plt.title('Features Correlation Heatmap', fontsize = 25)
fig, axes = plt.subplots(1,2, figsize = (16,7))
fig.suptitle('Useful Boxplots', fontsize = 18)

sns.boxplot(data = df, x = 'Pclass', y = 'Age', ax = axes[0])
sns.boxplot(data = df, x = 'Pclass', y = 'Fare', ax = axes[1])
fig, axes = plt.subplots(2,2, figsize = (15,8))
fig.suptitle('Useful Countplots', fontsize = 18)

sns.countplot(data = df, x = 'Survived', ax = axes[0,0])
sns.countplot(data = df, x = 'Embarked', ax = axes[0,1])
sns.countplot(data = df, x = 'Sex', ax = axes[1,0])
sns.countplot(data = df, x = 'Pclass', ax = axes[1,1])
fig, axes = plt.subplots(1,2, figsize = (15,5))
fig.suptitle('Useful Histograms', fontsize = 18)

sns.histplot(data = df, x = 'Fare', kde= True, bins = 25, ax = axes[0])
sns.histplot(data = df, x = 'Age', kde= True, bins = 25, ax = axes[1])
"""
# Data Cleaning
"""
df
df.isnull().sum()
unnecassary_features = ['PassengerId', 'Name', 'Ticket', 'Cabin'] 
# cabin number seems to be important, but lots of its data is missing so it's better to be droped 
df.drop(unnecassary_features, axis = 1, inplace=True)
"""
### Feature adjustment
"""
df['Sex'] = df['Sex'].map( {'female': 0, 'male': 1} )
df['FamilySize'] = df['SibSp'] + df['Parch']
df.drop(['SibSp', 'Parch'], axis = 1, inplace = True)
df
"""
# missing data
"""
df.isnull().sum()
"""
**Embarked** and **Age** has missing data and in this part we are going to deal with them:
"""
"""
# 1) Embarked
"""
df[df['Embarked'].isnull()]
df['Fare'].describe()
"""
It seems our null data on "Embarked" feature, are two independant **rich** ( because of their high "Fare" ) **women**...

Most of the rich people embarked on the trip from **cherbourg harbour** (C); so it's very likely that they embarked from cherbourg too...

To better understand this geuss, let's look at some charts:
"""
dfs = df[df['Embarked'] == 'S']
dfc = df[df['Embarked'] == 'C']
dfq = df[df['Embarked'] == 'Q']

fig = plt.figure()

ax2 = fig.add_axes([1.2,0,1,1])
ax2.pie(dfs['Pclass'].value_counts(),explode = (0,0,0.3), labels = dfs['Pclass'].value_counts().keys(),
        labeldistance = 1.03, shadow = True, autopct = '%1.1f%%', 
        startangle = 40, radius = 1.15, rotatelabels = True,
        textprops = {'fontsize': 12}, colors = [ '#ff2e2e', '#3cf527','#2aafdb'])

ax2.set_title('Passengers Class on Southampton harbour', y = 1.05, fontsize = 18)

ax1 = fig.add_axes([0,0,1,1])
ax1.pie(dfc['Pclass'].value_counts(),explode = (0.1,0,0), labels = dfc['Pclass'].value_counts().keys(),
        labeldistance = 1.03, shadow = True, autopct = '%1.1f%%', 
        startangle = -90, radius = 1.15, rotatelabels = True,
        textprops = {'fontsize': 12}, colors = ['#2aafdb','#3cf527', '#ff2e2e'])

ax1.set_title('Passengers Class on Cherbourg harbour', y = 1.05, fontsize = 18)

ax3 = fig.add_axes([0,-1.2,1,1])
ax3.pie(dfq['Pclass'].value_counts(),explode = (0,0,0.5), labels = dfq['Pclass'].value_counts().keys(),
        labeldistance = 1.03, shadow = True, autopct = '%1.1f%%', 
        startangle = 10, radius = 1.15, rotatelabels = True,
        textprops = {'fontsize': 12}, colors = [ '#ff2e2e', '#3cf527','#2aafdb'])

ax3.set_title('Passengers Class on Queenstown harbour', y = 1.05, fontsize = 18)
sns.barplot(y = df['Fare'], x = df['Embarked'])
df = df.fillna({'Embarked': 'C'})
"""
# 2) Age
"""
df.isnull().sum()
df.corr()
"""
As you can see, **Age** is highly correlated with **Pclass**. So it's better to guess the **Age** of passengers according to their **Pclass**...

In this section we use the **median** to keep the age distribution in each class unchanged. 
"""
for i in range(1, 4):
    
        df.loc[(df.Age.isnull()) & (df.Pclass == i), 'Age'] = df[(df['Pclass'] == i)]['Age'].median()
df.isnull().sum()
"""
# Categorical Data (Embarked)
"""
df1 = pd.get_dummies(df['Embarked'], drop_first=True)
final_df = pd.concat([df.drop('Embarked', axis = 1),df1], axis=1)
final_df.head()
"""
Ready to build some models
"""
"""
# Model Building
"""
"""
# Split the Dataset to Train and Test
"""
X = final_df.drop('Survived', axis = 1)
y = final_df['Survived']
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=101)
"""
# 1) Logistic Regression
"""
# libraries
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
"""
### Pipeline
"""
logistic_pipe = Pipeline([('scaler', StandardScaler()), ('logistic', LogisticRegression())])
logistic_pipe.fit(X_train, y_train)
logistic_pipe['logistic'].coef_
"""
### Predict
"""
y_log_pred = logistic_pipe.predict(X_test)
"""
### Evaluation
"""
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, plot_confusion_matrix
accuracy_score(y_test, y_log_pred)
confusion_matrix(y_test, y_log_pred)
plot_confusion_matrix(logistic_pipe, X_test, y_test)
print(classification_report(y_test, y_log_pred))
"""
### Evaluation Curves
"""
from sklearn.metrics import plot_precision_recall_curve, plot_roc_curve
plot_precision_recall_curve(logistic_pipe, X_test, y_test)
plot_roc_curve(logistic_pipe, X_test, y_test)
"""
# 2) KNN
"""
# libraries
from sklearn.neighbors import KNeighborsClassifier
"""
### Pipeline
"""
knn_pipe = Pipeline([('scaler', StandardScaler()), ('knn', KNeighborsClassifier(n_neighbors = 1))])
knn_pipe.fit(X_train, y_train)
"""
### Predict
"""
y_knn_pred = knn_pipe.predict(X_test)
"""
### Evaluation
"""
accuracy_score(y_test, y_knn_pred)
"""
### Finding the Best K
"""
error_rate = []
for i in range(1,31):
    knn_pipe = Pipeline([('scaler', StandardScaler()), ('knn', KNeighborsClassifier(n_neighbors = i))])
    
    knn_pipe.fit(X_train, y_train)
    y_knn_pred = knn_pipe.predict(X_test)
    
    error = 1 - accuracy_score(y_test, y_knn_pred)
    error_rate.append(error)
plt.figure(figsize=(15,4))

plt.plot(range(1,31), error_rate)
plt.axhline(min(error_rate), c = 'r', ls = '--')
plt.grid()

plt.title('Elbow method to choose best K', fontsize=18, y = 1.05)
plt.xticks(range(1,31,2))
plt.xlabel('K Neighbors', fontsize=15)
plt.ylabel('Error Rate', fontsize=15)
"""
Best **K** is number 4 (or 6,12,18 ... but we choose 4 for less complexity and same error rate)
"""
"""
### Prediction and Evaluation using best **K**
"""
final_knn_pipe = Pipeline([('scaler', StandardScaler()), ('knn', KNeighborsClassifier(n_neighbors = 4))])

final_knn_pipe.fit(X_train, y_train)

final_y_knn_pred = final_knn_pipe.predict(X_test)
accuracy_score(y_test, final_y_knn_pred)
confusion_matrix(y_test, final_y_knn_pred)
plot_confusion_matrix(final_knn_pipe, X_test, y_test)
print(classification_report(y_test, final_y_knn_pred))
"""
### Evaluation Curves
"""
plot_precision_recall_curve(final_knn_pipe, X_test, y_test)
plot_roc_curve(final_knn_pipe, X_test, y_test)
"""
# Conclusion
"""
print('**KNN Model**\n', classification_report(y_test, final_y_knn_pred))
print('\n**Logistic Regression Model**\n\n', classification_report(y_test, y_log_pred))