import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
"""
# Let's read data!
"""
"""
**There are 110527 rows & 14 features.**
"""
df = pd.read_csv('../input/noshowappointments/KaggleV2-May-2016.csv')
print('Count of rows', str(df.shape[0]))
print('Count of Columns', str(df.shape[1]))
df.head()
df.describe()
"""
**There is no null(NAN) values in dataset which is good! :)**
"""
df.isnull().any().any()
"""
**Checking how pandas read datatypes in that dataframe**
"""
df.info()
"""
**Check unique values in features. So, we can choose right methods to encode features.**
"""
for i in df.columns:
    print(i+":",len(df[i].unique()))
"""
**There are 110527 rows & all values in "AppointmentID" feature is unique. So, we do not need that feature because in every appointment there is an unique appointment ID even it's same person who looks for.**
"""
df.drop('AppointmentID', axis=1,inplace = True)
"""
# Preprocess
## PatientID: Float -> String (we are going to assume that feature like it's categorical)
## No-show: String -> Integer (categorical feature)
## Gender: String -> Integer (categorical feature)
"""
df
df['PatientId'] = df['PatientId'].apply(int).apply(str)
df['Handcap'] = df['Handcap'].apply(lambda x: 0 if x == 0 else 1)
df['No-show'] = df['No-show'].map({'No':0, 'Yes':1})
df['Gender'] = df['Gender'].map({'F':0, 'M':1})
"""
## We also have to parse dates to correctly classify the data.
## Let's convert it to datetime object for both "ScheduledDay" & "AppointmentDay"
"""
df['ScheduledDay'] = pd.to_datetime(df['ScheduledDay']).dt.strftime('%Y-%m-%d')
df['ScheduledDay'] = pd.to_datetime(df['ScheduledDay'])
df['ScheduledDay']
df['AppointmentDay'] = pd.to_datetime(df['AppointmentDay']).dt.strftime('%Y-%m-%d')
df['AppointmentDay'] = pd.to_datetime(df['AppointmentDay'])
df['AppointmentDay']
"""
## There can be a time between Appointment Day and Scheduled Day which can be a good feature to classify. 
## So, let's create that feature
"""
df['Day_diff'] = (df['AppointmentDay'] - df['ScheduledDay']).dt.days
df['Day_diff'].unique()
"""
## To give the data a classifier we have to digitilaze that features. 
## Let's seperate the date columns into year, month, day. (Also, weekday and season column can be added but I did not used it here)
"""
df['ScheduledDay_Y'] = df['ScheduledDay'].dt.year
df['ScheduledDay_M'] = df['ScheduledDay'].dt.month
df['ScheduledDay_D'] = df['ScheduledDay'].dt.day
df.drop(['ScheduledDay'], axis=1, inplace=True)

df['AppointmentDay_Y'] = df['AppointmentDay'].dt.year
df['AppointmentDay_M'] = df['AppointmentDay'].dt.month
df['AppointmentDay_D'] = df['AppointmentDay'].dt.day
df.drop(['AppointmentDay'], axis=1, inplace=True)
"""
## There are negative integers in Age column which is not possible, so easyly drop them.
"""
df.drop(df[df['Age'] < 0].index, inplace = True)
"""
## Neighbourhood: str -> integer (Categorical)
## PatientId: integer -> Categorical Integer (Categorical)
"""
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
df['Neighbourhood'] = le.fit_transform(df['Neighbourhood'])
le = preprocessing.LabelEncoder()
df['PatientId'] = le.fit_transform(df['PatientId'])
"""
# Now, our dataframe is ready to split & classify. It's all digitized.
"""
df
"""
## Last Check
"""
df.info()
"""
## Split dataframe into train and test
"""
X = df.drop(['No-show'], axis=1)
y = df['No-show']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25)
"""
## Let's classify the dataset into a few classifiers.
"""
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import classification_report
lr = LogisticRegression(solver='liblinear')
lr.fit(X_train, y_train)

y_pred_lr = lr.predict(X_test)
clf_report = classification_report(y_test, y_pred_lr)
print(f"Classification Report : \n{clf_report}")
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)

y_pred_knn = knn.predict(X_test)

clf_report = classification_report(y_test, y_pred_knn)
print(f"Classification Report : \n{clf_report}")
dtc = DecisionTreeClassifier()
dtc.fit(X_train, y_train)

y_pred_dtc = dtc.predict(X_test)
clf_report = classification_report(y_test, y_pred_dtc)

print(f"Classification Report : \n{clf_report}")
rd_clf = RandomForestClassifier()
rd_clf.fit(X_train, y_train)

y_pred_rd_clf = rd_clf.predict(X_test)
clf_report = classification_report(y_test, y_pred_rd_clf)

print(f"Classification Report : \n{clf_report}")
ada = AdaBoostClassifier(base_estimator = dtc)
ada.fit(X_train, y_train)

y_pred_ada = ada.predict(X_test)
clf_report = classification_report(y_test, y_pred_ada)

print(f"Classification Report : \n{clf_report}")
gb = GradientBoostingClassifier()
gb.fit(X_train, y_train)

y_pred_gb = gb.predict(X_test)
clf_report = classification_report(y_test, y_pred_gb)

print(f"Classification Report : \n{clf_report}")
"""
## Average test set accuracy in these models is %80.
## We did not scaled whole data. What changes if we scale whole data?
## Let's try it!
"""
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25)
"""
## Even we scale the data into 0,1 the accuracy is not changed as expected!
"""
lr = LogisticRegression(solver='liblinear')
lr.fit(X_train, y_train)

y_pred_lr = lr.predict(X_test)
clf_report = classification_report(y_test, y_pred_lr)
print(f"Classification Report : \n{clf_report}")
"""
## Let's try a cross validation to validate our results.
"""
from sklearn.model_selection import cross_val_score

accuracy = cross_val_score(estimator = lr, X = X, y =y, cv = 10)
print("avg acc: ",np.mean(accuracy))
print("acg std: ",np.std(accuracy))
"""
## As seen above, average test accuracy is 80% & average standard deviation is very low which is ok for now.
"""