# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" 
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
"""
# Titanic - Machine Learning from Disaster
"""
"""


<html>
<h3>Data Dictionary</h3>
<table border=1>
<tbody>
<tr><th><b>Variable</b></th><th><b>Definition</b></th><th><b>Key</b></th></tr>
<tr>
<td>survival</td>
<td>Survival</td>
<td>0 = No, 1 = Yes</td>
</tr>
<tr>
<td>pclass</td>
<td>Ticket class</td>
<td>1 = 1st, 2 = 2nd, 3 = 3rd</td>
</tr>
<tr>
<td>sex</td>
<td>Sex</td>
<td></td>
</tr>
<tr>
<td>Age</td>
<td>Age in years</td>
<td></td>
</tr>
<tr>
<td>sibsp</td>
<td># of siblings / spouses aboard the Titanic</td>
<td></td>
</tr>
<tr>
<td>parch</td>
<td># of parents / children aboard the Titanic</td>
<td></td>
</tr>
<tr>
<td>ticket</td>
<td>Ticket number</td>
<td></td>
</tr>
<tr>
<td>fare</td>
<td>Passenger fare</td>
<td></td>
</tr>
<tr>
<td>cabin</td>
<td>Cabin number</td>
<td></td>
</tr>
<tr>
<td>embarked</td>
<td>Port of Embarkation</td>
<td>C = Cherbourg, Q = Queenstown, S = Southampton</td>
</tr>
</tbody>
</table>

<h3>Variable Notes</h3><h6>
<p><b>pclass</b>: A proxy for socio-economic status (SES)<br> 1st = Upper<br> 2nd = Middle<br> 3rd = Lower<br><br> <b>age</b>: Age is fractional if less than 1. If the age is estimated, is it in the form of xx.5<br><br> <b>sibsp</b>: The dataset defines family relations in this way...<br> Sibling = brother, sister, stepbrother, stepsister<br> Spouse = husband, wife (mistresses and fiancés were ignored)<br><br> <b>parch</b>: The dataset defines family relations in this way...<br> Parent = mother, father<br> Child = daughter, son, stepdaughter, stepson<br> Some children travelled only with a nanny, therefore parch=0 for them.</p><h6>

</html>
"""
#import required libraries
import warnings
warnings.filterwarnings("ignore")

import matplotlib.pyplot as plt
import seaborn as sns
import pandas_profiling as pp
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler 
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier,GradientBoostingClassifier  
from sklearn.metrics import accuracy_score,f1_score,auc,roc_curve,classification_report,confusion_matrix 

#read the csv files

train = pd.read_csv('/kaggle/input/titanic/train.csv')
test = pd.read_csv('/kaggle/input/titanic/test.csv')

train.shape , test.shape
train.head()
#number of passangers who survived 
sdf = train.Survived.value_counts()
(sdf/len(train.Survived))*100
"""
Passanger survival rate is less
"""
#check for null/missing values
def  check_null(df):
    null_df = train.isnull().sum()
    null_df = null_df[null_df!=0]
    return null_df

print(check_null(train)) 
print("----"*5)
print(check_null(test))
pp.ProfileReport(train)
#rate of men and women who survived
male = train[train.Sex=='male']
rate_male = sum(male['Survived'])/len(male['PassengerId'])*100

female = train[train.Sex=='female']
rate_female = sum(female['Survived'])/len(female['PassengerId'])*100

print("Rate Male who survived :",rate_male)
print("Rate Felame who survived :",rate_female)
g = sns.FacetGrid(train, col="Sex",row='Survived', height=3.5);
g.map(sns.histplot, "Age");

sns.countplot( x='Survived', data=train, hue="Pclass");
sns.countplot( x='Survived', data=train, hue="Embarked");
train.groupby('Survived')['SibSp'].plot.hist(histtype= 'bar');
train[['SibSp','Survived']].groupby('SibSp').sum().sort_values(by='Survived',ascending=False)
plt.legend();
train.groupby('Survived')['Parch'].plot.hist(histtype= 'bar');
train[['Parch','Survived']].groupby('Parch').sum().sort_values(by='Survived',ascending=False)
plt.legend();
g = sns.FacetGrid(train, col='Survived', height=3.5);
g.map(sns.histplot, "Fare");
g = sns.FacetGrid(train, col='Embarked',size=4.5, aspect=1.6);
g.map(sns.pointplot, 'Pclass','Survived','Sex');
g.add_legend();
sns.boxplot(data = train['Age'], orient = "h")
sns.boxplot(data = train['Fare'], orient = "h")
"""
# Feature Engineering
"""
#drop unused cols
print("Before : ",train.shape,test.shape)

train = train.drop(['PassengerId','Ticket','Cabin'],axis=1)
test = test.drop(['PassengerId','Ticket','Cabin'],axis=1)

print("After : ",train.shape,test.shape)
"""
**Extarct isalone feature**
"""
train['family_count'] = train['SibSp']+train['Parch']
test['family_count'] = test['SibSp']+test['Parch']

data = [train, test]
for dataset in data:
    dataset.loc[dataset['family_count'] > 0, 'isalone'] = 0
    dataset.loc[dataset['family_count'] == 0, 'isalone'] = 1
axes = sns.factorplot('family_count','Survived', 
                      data=train, aspect = 2.5, );
train.isalone = train.isalone.astype('int')
test.isalone = test.isalone.astype('int')
train.head()
"""
**Extract Title feature**
"""
#Extract Title feature

for dataset in [train,test]:
    dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\.', expand=False)

pd.crosstab(train['Title'], train['Sex'])
#We can replace many titles with a more common name or classify them as Rare.

for dataset in [train,test]:
    dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess','Capt', 'Col','Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Rare')

    dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss') #Mlle is a traditional alternative for an unmarried woman aka Miss.
    dataset['Title'] = dataset['Title'].replace('Ms', 'Miss') #Ms for unmarrid woman aka Miss
    dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs') #Mme is used before the name of a married woman 
    
train[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
g = sns.FacetGrid(train, col="Title",row='Survived', height=3.5);
g.map(sns.histplot, "Age");
g = sns.FacetGrid(train, col="Title",hue='Survived', height=3.5);
g.map(sns.histplot, "family_count");
#drop Name col
train = train.drop(['Name'],axis=1)
test = test.drop(['Name'],axis=1)
train.head()
"""
# Handle Missing Values
"""
print(check_null(train)) 
print("----"*5)
print(check_null(test))
train[train.Embarked.isnull()]
num_cols = train.select_dtypes(exclude='O').columns
num_cols = [col for col in num_cols if col!='Survived']
num_cols
num_cols = train.select_dtypes(exclude='O').columns
num_cols = [col for col in num_cols if col!='Survived']
cat_cols = train.select_dtypes(include='O').columns

print("Numerical cols :",num_cols,"\n","---"*25)
print("Categorical cols :",cat_cols)
#Impute missing values using simple imputer

#numeric col
imputer = SimpleImputer(strategy='mean')
train[num_cols] = imputer.fit_transform(train[num_cols])
test[num_cols] = imputer.fit_transform(test[num_cols])

#categorical col
imputer = SimpleImputer(strategy='most_frequent')
train[cat_cols] = imputer.fit_transform(train[cat_cols])
test[cat_cols] = imputer.fit_transform(test[cat_cols])
print(check_null(train)) 
print("----"*5)
print(check_null(test))
#train.iloc[[61,829]] 
#corelation plot
plt.figure(figsize=(12,9))
matrix = np.triu(train.corr())
sns.heatmap(train.corr(), annot=True, mask=matrix)
"""
# Convert Categorical cols
"""
print('Before : ',train.shape,test.shape)
train_dummies = pd.get_dummies(train[cat_cols],prefix=cat_cols,drop_first=True)
test_dummies = pd.get_dummies(test[cat_cols],prefix=cat_cols,drop_first=True)

train = pd.concat([train,train_dummies],axis=1)
test = pd.concat([test,test_dummies],axis=1)

train = train.drop(cat_cols,axis=1)
test = test.drop(cat_cols,axis=1)

print('After : ',train.shape,test.shape)
train.describe()
scaler = StandardScaler()
train[num_cols] = scaler.fit_transform(train[num_cols])
test[num_cols] = scaler.fit_transform(test[num_cols])
train.describe()
"""
# Feature Selection Using RandomForestClassifier 
"""
X = train.copy()
y = X.Survived
del X['Survived']

X.shape , y.shape , test.shape
X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=42,test_size=0.3,stratify=y)

X_train.shape, X_test.shape, y_train.shape, y_test.shape
#Baseline accuracy
b = y_train.value_counts()
(b/sum(b))*100
#Fitting Random Forest Classification to the training set to get important features

classifier = RandomForestClassifier(n_estimators = 100,random_state=42)
classifier.fit(X,y)
#importances = classifier.feature_importances_
plt.figure(figsize=(10,10))

feat_importances = pd.Series( classifier.feature_importances_, index=X.columns)
feat_importances.sort_values(ascending=True)
feat_importances.nlargest(20).plot(kind='barh');
X_filtered_features = feat_importances.nlargest(7).index #select first 7 features
X_filtered_features
"""
### Logistic Regression
"""
#Logistic Regression

logreg = LogisticRegression() #create instance 
logreg.fit(X_train[X_filtered_features], y_train) #fit the model

y_pred = logreg.predict(X_test[X_filtered_features]) #prediction

#accuracy score
train_acc = round(logreg.score(X_train[X_filtered_features], y_train) * 100, 2)
test_acc = round(accuracy_score(y_test,y_pred) * 100, 2)
print("Train Acc : ",train_acc)
print("Test Acc : ",test_acc)
print("Confusion matrix :\n",confusion_matrix (y_test,y_pred))

print("\nClassification_report : \n",classification_report (y_test,y_pred))

fpr,tpr,t = roc_curve(y_test,y_pred)
print("\nAUC : ",auc(fpr,tpr))
"""
### RandomForestClassifier
"""
rf_model = RandomForestClassifier(n_estimators =100,random_state=42)
rf_model.fit(X_train[X_filtered_features],y_train)

y_pred = rf_model.predict(X_test[X_filtered_features]) #prediction

#accuracy score
train_acc = round(rf_model.score(X_train[X_filtered_features], y_train) * 100, 2)
test_acc = round(accuracy_score(y_test,y_pred) * 100, 2)
print("Train Acc : ",train_acc)
print("Test Acc : ",test_acc)
print("Confusion matrix :\n",confusion_matrix (y_test,y_pred))

print("\nClassification_report : \n",classification_report (y_test,y_pred))

fpr,tpr,t = roc_curve(y_test,y_pred)
print("\nAUC : ",auc(fpr,tpr))
"""
**Hyperparameter Tunning**
"""
from sklearn.model_selection import GridSearchCV 
#Use the GridSearch CV to search for best hyperparameters

parameters = {
    "max_depth":[1,2,3],
    "n_estimators":[25,50,75,100],
    "max_features":[5,6,7]
    
}

#First create the base model to tune
rf = RandomForestClassifier(random_state=42)

#Grid search of parameters, using 5 fold cross validation, 

rf_random = GridSearchCV(estimator=rf, param_grid=parameters, cv=5)

#Fit the random search model
rf_random.fit(X_train[X_filtered_features], y_train)
#best parameters
rf_random.best_params_
rf_model = RandomForestClassifier(n_estimators =25,max_depth=3,max_features=5,random_state=42)
rf_model.fit(X_train[X_filtered_features],y_train)

y_pred = rf_model.predict(X_test[X_filtered_features]) #prediction

#accuracy score
train_acc = round(rf_model.score(X_train[X_filtered_features], y_train) * 100, 2)
test_acc = round(accuracy_score(y_test,y_pred) * 100, 2)
print("Train Acc : ",train_acc)
print("Test Acc : ",test_acc)
print("Confusion matrix :\n",confusion_matrix (y_test,y_pred))

print("\nClassification_report : \n",classification_report (y_test,y_pred))

fpr,tpr,t = roc_curve(y_test,y_pred)
print("\nAUC : ",auc(fpr,tpr))
"""
### GradientBoosting
"""
xgb = GradientBoostingClassifier (random_state=42)
xgb.fit(X_train[X_filtered_features],y_train)

y_pred = xgb.predict(X_test[X_filtered_features]) #prediction

#accuracy score
train_acc = round(xgb.score(X_train[X_filtered_features], y_train) * 100, 2)
test_acc = round(accuracy_score(y_test,y_pred) * 100, 2)
print("Train Acc : ",train_acc)
print("Test Acc : ",test_acc)
"""
Overfitted
"""
"""
**Hyperparameter Tunning**
"""
parameters = {
    "max_depth":[1,2,3,4,5],
    "n_estimators":[25,50,75,100],
    "learning_rate":[0.001,0.01,0.1]
    
}

#First create the base model to tune
xgb = GradientBoostingClassifier(random_state=42,max_features='sqrt')

#Grid Search of parameters, using 5 fold cross validation, 

xgb_model = GridSearchCV(estimator=xgb,param_grid=parameters, cv=5)

#Fit the random search model
xgb_model.fit(X_train[X_filtered_features], y_train)
xgb_model.best_params_
xgb = GradientBoostingClassifier (learning_rate=0.1 ,max_depth = 4,max_features='sqrt',n_estimators=100 ,random_state=42)
xgb.fit(X_train[X_filtered_features],y_train)

y_pred = xgb.predict(X_test[X_filtered_features]) #prediction

#accuracy score
train_acc = round(xgb.score(X_train[X_filtered_features], y_train) * 100, 2)
test_acc = round(accuracy_score(y_test,y_pred) * 100, 2)
print("Train Acc : ",train_acc)
print("Test Acc : ",test_acc)
print("Confusion matrix :\n",confusion_matrix (y_test,y_pred))

print("\nClassification_report : \n",classification_report (y_test,y_pred))

fpr,tpr,t = roc_curve(y_test,y_pred)
print("\nAUC : ",auc(fpr,tpr))
model = RandomForestClassifier(n_estimators =25,max_depth=3,max_features=5,random_state=42)
model.fit(X[X_filtered_features],y)
predictions = model.predict(test[X_filtered_features])

submission = pd.read_csv('../input/titanic/gender_submission.csv')
submission['Survived'] = predictions
submission.to_csv('Submission_rf.csv', index=False)
"""
Compared to Logistic Regression Model and GradientBoostingClassifier , RandomForestClassifier performed well which is more generalized model 
"""
"""
## Public Score : 0.78468
"""
"""
## Hey Kagglers , this is my first kernel on Kaggle 
## Please do upvote if this kernel finds helpful and
## do leave comment/feedback if you can suggest improvements.
## 
## Thank-You!!
"""