print("Missing values")
titanic_data.isnull().any(axis=1).sum()

"""
Explanation: Data  Preprocessing
End of explanation
"""


titanic_data.isnull().sum()

"""
Explanation: That's a lot of missing values, let's see how they are distributed
End of explanation
"""


treated_data = titanic_data.drop(['Cabin','Name', 'PassengerId', 'Ticket'], axis=1)

"""
Explanation: We can just drop the cabin column as it isn't important, we will also drop the Name, PassengerId and Ticket columns
End of explanation
"""


treated_data = treated_data.dropna()
treated_data.isnull().any(axis=1).sum()

"""
Explanation: Also, the embarked column NaN indicates that a passenger did not embark, we can just drop those passenger, along with the ones with NaN ages
End of explanation
"""


treated_data['Age'].hist()
print(treated_data['Age'].min(), treated_data['Age'].max())

"""
Explanation: We no longer have any null value, lets check for outliers in the age column
End of explanation
"""


treated_data = treated_data.replace(['male', 'female'],[0,1]).replace(['S','C','Q'], [0,1,2])
treated_data=treated_data.astype(np.float32)

treated_data.head()

"""
Explanation: Alright, that seems to be ok, lastly, we will drop the ticket column, as it does not seems relevant and will convert male/female to 0/1 and the embarking ports to 0/1/2
End of explanation
"""


train = treated_data.sample(frac=0.7)
test = treated_data.drop(train.index)
train_Y = train['Survived'].apply(lambda x: [0.,1.] if x == 0 else [1.,0.]).as_matrix()
train.drop('Survived', axis=1, inplace=True)
test_Y = test['Survived'].apply(lambda x: [0.,1.] if x == 0 else [1.,0.]).as_matrix()
test.drop('Survived', axis=1, inplace=True)
train.head()

"""
Explanation: We are now ready to do the machine learning bit
Machine Learning
First Off, let's take our splits, we will use a 70/30 split scheme to train/test, let's also prepare the Y values (survive/no-survive), we will convert the survived column into a one-hot encoding, [0, 1] begin no-survive and [1, 0] being survive
End of explanation
"""


# Network architeture
# Hidden layers size, you can mess with these sizes to
# see if there's an gain in accuracy
n_hidden_1 = 15
n_hidden_2 = 15
# These are the inputs and outputs size
# You shouldn't have to touch these
n_inputs = 7
n_classes = 2

# Creating the input and output placeholders
x = tf.placeholder(tf.float32, [None, n_inputs],  name="input")
y = tf.placeholder(tf.float32, [None, n_classes], name="survival")

# We want probabilities that the passenger belongs to a given class (survive/no-survive)
# so we will use the softmax activation function
def makeModel(x, w, biases):
    # Hidden layer with Softmax activation
    layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'], name='layer_1')
    layer_1 = tf.nn.softmax(layer_1)
    # Hidden layer with Softmax activation
    layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'], name='layer_2')
    layer_2 = tf.nn.softmax(layer_2)
    # Output layer with softmax activation
    out_layer = tf.nn.softmax(tf.add(tf.matmul(layer_2, weights['out']), biases['out'], name='out_layer'))
    return out_layer

# Store layers weight & bias
weights = {
    'h1': tf.Variable(tf.random_normal([n_inputs, n_hidden_1]), name='h1'),
    'h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2]), name='h2'),
    'out': tf.Variable(tf.random_normal([n_hidden_2, n_classes]), name='out')
}
biases = {
    'b1': tf.Variable(tf.random_normal([n_hidden_1]), name='b1'),
    'b2': tf.Variable(tf.random_normal([n_hidden_2]), name='b2'),
    'out': tf.Variable(tf.random_normal([n_classes]), name='out_bias')
}

# Construct model
pred = makeModel(x, weights, biases)

# Our cost function will be softmax cross entropy between classes
logits = tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=pred, name="final_ouput")
cost = tf.reduce_mean(logits, name='Loss')
# You can change the LR to see how the training behaves
optimizer = tf.train.AdamOptimizer(learning_rate=0.05).minimize(cost)

# To calculate the correct class we verify if the greatest of the two scores 
# is in the same index both in the predicition and the ground truth (GT)
correct = tf.equal(tf.argmax(y, 1, name='GT'), tf.argmax(pred,1, name='predicted'), name='correct')
accuracy = tf.reduce_mean(tf.cast(correct, "float"), name='Acc')

# TensorBoard reporting for accuracy and loss
tf.summary.scalar('LossSummary', cost)
tf.summary.scalar('AccSummary', accuracy)

r_acc = []
r_loss = []
r_t_loss = []
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    writer = tf.summary.FileWriter('./logs', sess.graph)
    # Training cycle
    summary = tf.summary.merge_all()
    # Changing the iteration number may also change the results
    for epoch in range(5001):
        # Run the summaries and the traing step
        log, _ ,loss= sess.run([summary, optimizer, cost], 
                                   feed_dict={x:train.as_matrix(), y:[k for k in train_Y]})

        writer.add_summary(log, epoch)
        writer.flush()
        # Every 100 iterations, log the acc and the loss on the test data
        if epoch%50 == 0:
            t_acc, t_loss = sess.run([accuracy, cost],feed_dict={x:test.as_matrix(), y: [k for k in test_Y]})
            r_acc.append(t_acc)
            r_t_loss.append(t_loss)
        r_loss.append(loss)
    # Pass all the test data by the classifier
    p = sess.run(pred,{x:test.as_matrix(), y: [k for k in test_Y]})
    sess.close()
writer.close()

"""
Explanation: We will now create our TensorFlow model for training
End of explanation
"""


print(p[:5])
print(test_Y[:5])

"""
Explanation: Now, let's take a look in the first 5 predictions and it's ground truths
End of explanation
"""


plt.figure(figsize=(8,10))
plt.subplot(211)
plt.title("Training Loss over iterations")
plt.xlabel("Iteration")
plt.ylabel("Softmax Loss")
plt.grid()
plt.plot(r_loss)
plt.subplot(212)
plt.title("Test Loss over iterations")
plt.xlabel("Iteration (x50)")
plt.ylabel("Softmax Loss")
plt.grid()
plt.plot(r_t_loss)


plt.title("Accuracy Over Iterations in the Test Data")
plt.xlabel("Iterations (x50)")
plt.ylabel("Accuracy")
plt.grid()
plt.plot(r_acc)

"""
Explanation: Let's visualize the loss and accuracy over time (the same data is on tensorboard)
End of explanation
"""


# First we will decode the survival of the passengers, note tha [1, 0] OHE means the passenger survived, so we
# can use the first index of the encoding
seriesTest = pd.Series(test_Y)
seriesTest = seriesTest.apply(lambda x: x[0])
seriesTest.head()

# Now we just have to sum all the values and divide by the total
seriesTest.sum() / len(seriesTest)

"""
Explanation: We can see our accuracy in the test data is aroun 74%, let's see if this is better than random guessing
End of explanation
"""


import sklearn.tree as skltr

"""
Explanation: It looks like out model is better than random guessing the values, since only 39% of the passengers survived and we are correct predicting the survival of about 74% of them.
Let's see if we can discover what were the most significant attributes for survival, we will use a decision tree from the sklearn toolkit.
End of explanation
"""


train = treated_data.sample(frac=0.7)
test = treated_data.drop(train.index)
train_Y = train['Survived'].as_matrix()
train.drop('Survived', axis=1, inplace=True)
test_Y = test['Survived'].as_matrix()
test.drop('Survived', axis=1, inplace=True)
train.head()

# We will run the decision tree with the default parameters
decision_tree = skltr.DecisionTreeClassifier()
decision_tree.fit(train, train_Y)

# Now we will predict the results and see the accuracy
prediction = decision_tree.predict(test)
print("Accuracy: ", sum((prediction == test_Y))/len(test_Y))

# We must now see if this is better than random guessing
print(sum(test_Y)/len(test_Y))

# Now we can see which features are more important
plt.xticks(range(0, 70, 10), train.columns, rotation=90)

for x, h in zip(range(0,70,10), decision_tree.feature_importances_):
    plt.bar(x, h, 5, color=(min(h*5, 1), 0, 1-min(h*5,1)))

"""
Explanation: First, we will split the model again, using the same splits as before (70/30)
End of explanation
"""


