#************************************************************************************
# Rezwan Matin
# Thesis B
# Filename: SVM_Parselmouth_Complete_Clean_Noise.py
# Date: 10/24/20
#
# Features:
# 36 features - Functionals on Pitch (f0), Loudness(Intensity), MFCC, formants, formants BW, HNR, Jitter, and Shimmer.
#
#*************************************************************************************

import pandas as pd
import math
import time
import numpy as np
import matplotlib.pyplot as plt
import librosa as rosa
import os
import itertools
from sklearn.model_selection import train_test_split
from sklearn.model_selection import ShuffleSplit
from sklearn.model_selection import learning_curve
from sklearn.metrics import precision_recall_fscore_support
from sklearn.svm import SVC
from sklearn.utils import resample


# Initialize timer
t_1 = time.perf_counter()

# Set the random seeds for replicating results over multiple runs.
#np.random.seed(0)

# Import dataframe/dataset into an instance/object 'df' using Pandas. Use first row as column header and first column as row header!
df = pd.read_csv(r'/home/r_m727/All_Files/Corpus/Simulated/RAVDESS+TESS+CREMA-D/RAVDESS+TESS+CREMA-D_Parselmouth_Clean_Noise.csv', header=0, index_col=0)

sum_train = 0
sum_val = 0
avg_train = 0
avg_val = 0

# Replace NaN values with zeroes
df = df.fillna(0)

# Rename target labels.
#df['Emotion'].replace({"Neutral" : 1.0, "Happy" : 2.0, "Sad" : 3.0, "Angry" : 4.0, "Fearful" : 5.0, "Disgust" : 6.0, "Surprised" : 7.0}, inplace=True)
df['Emotion'].replace({1.0 : 1.0, 3.0 : 2.0, 4.0 : 3.0, 5.0 : 4.0, 6.0 : 5.0, 7.0 : 6.0, 8.0 : 7.0}, inplace=True)

# Take data samples of each class from dataframe into separate dataframes.
df_happy = df.loc[df.Emotion==2.0]
df_sad = df[df.Emotion==3.0]
df_angry = df[df.Emotion==4.0]
df_fearful = df[df.Emotion==5.0]
df_disgust = df[df.Emotion==6.0]
df_neutral = df[df.Emotion==1.0]
df_surprised = df[df.Emotion==7.0]

# Join only the majority classes, leaving out Neutral and Surprised.
df_less = pd.concat([df_happy, df_sad, df_angry, df_fearful, df_disgust])

# Extract labels of majority classes.
y_less = df_less.iloc[0:18630, 36].values
# Extract features of majority classes.
X_less = df_less.iloc[0:18630, list(range(36))].values
print(y_less)

# Split and stratify majority class samples for training and testing.
X_train_temp_less, X_test_less, y_train_temp_less, y_test_less = train_test_split(X_less, y_less, test_size=1863, random_state=0, stratify=y_less) # training split = 90%, test split = 10%



for i in range(0,10):
	
	# Further split and stratify majority class training samples for training data for training and validating.
	X_train_less, X_val_less, y_train_less, y_val_less = train_test_split(X_train_temp_less, y_train_temp_less, test_size=1863, random_state=None, stratify=y_train_temp_less) # training split = 80%, validation split = 10%

	# Take minority data samples from dataframe to array
	neutral_array = df_neutral.to_numpy()
	surprised_array = df_surprised.to_numpy()

	# Shuffle the data samples of minority class
	np.random.shuffle(neutral_array)
	np.random.shuffle(surprised_array)

	# Split minority class Neutral in 80:10:10 ratio.
	train_neutral = neutral_array[0:2534, :]
	val_neutral = neutral_array[2534:2850, :]
	test_neutral = neutral_array[2850:3166, :]

	# Resample Neutral data to match majority class samples.
	train_neutral_resampled = resample(train_neutral, n_samples=2982, replace=True, random_state=None)
	val_neutral_resampled = resample(val_neutral, n_samples=372, replace=True, random_state=None)
	test_neutral_resampled = resample(test_neutral, n_samples=372, replace=True, random_state=0)

	# Separate features and target labels for Neutral data.
	X_train_neutral = train_neutral_resampled[:, 0:36]
	X_val_neutral = val_neutral_resampled[:, 0:36]
	X_test_neutral = test_neutral_resampled[:, 0:36]
	y_train_neutral = train_neutral_resampled[:, 36]
	y_val_neutral = val_neutral_resampled[:, 36]
	y_test_neutral = test_neutral_resampled[:, 36]

	# Split minority class Surprised in 80:10:10 ratio.
	train_surprised = surprised_array[0:948, :]
	val_surprised = surprised_array[948:1066, :]
	test_surprised = surprised_array[1066:1184, :]

	# Resample Surprised data to match majority class samples.
	train_surprised_resampled = resample(train_surprised, n_samples=2982, replace=True, random_state=None)
	val_surprised_resampled = resample(val_surprised, n_samples=372, replace=True, random_state=None)
	test_surprised_resampled = resample(test_surprised, n_samples=372, replace=True, random_state=0)

	# Separate features and target labels for Surprised data.
	X_train_surprised = train_surprised_resampled[:, 0:36]
	X_val_surprised = val_surprised_resampled[:, 0:36]
	X_test_surprised = test_surprised_resampled[:, 0:36]
	y_train_surprised = train_surprised_resampled[:, 36]
	y_val_surprised = val_surprised_resampled[:, 36]
	y_test_surprised = test_surprised_resampled[:, 36]

	# Join upsampled minority data samples with majority data samples.
	X_train_almost = np.concatenate((X_train_less, X_train_neutral), axis=0)
	X_train = np.concatenate((X_train_almost, X_train_surprised), axis=0)
	X_val_almost = np.concatenate((X_val_less, X_val_neutral), axis=0)
	X_val = np.concatenate((X_val_almost, X_val_surprised), axis=0)
	X_test_almost = np.concatenate((X_test_less, X_test_neutral), axis=0)
	X_test = np.concatenate((X_test_almost, X_test_surprised), axis=0)
	y_train_almost = np.concatenate((y_train_less, y_train_neutral), axis=0)
	y_train = np.concatenate((y_train_almost, y_train_surprised), axis=0)
	y_val_almost = np.concatenate((y_val_less, y_val_neutral), axis=0)
	y_val = np.concatenate((y_val_almost, y_val_surprised), axis=0)
	y_test_almost = np.concatenate((y_test_less, y_test_neutral), axis=0)
	y_test = np.concatenate((y_test_almost, y_test_surprised), axis=0)

	# Join Training and Validation split to create CV results in Learning Curves below.
	X_train_temp = np.concatenate((X_train, X_val), axis=0)
	y_train_temp = np.concatenate((y_train, y_val), axis=0)

	mean_vals = np.mean(X_train, axis=0)
	std_val = np.std(X_train, axis=0)

	# Standardize the inputs
	X_train_temp_centered = (X_train_temp - mean_vals)/std_val
	X_train_centered = (X_train - mean_vals)/std_val
	X_val_centered = (X_val - mean_vals)/std_val
	X_test_centered = (X_test - mean_vals)/std_val


	print(X_train_centered.shape, y_train.shape)
	print(X_val_centered.shape, y_val.shape)
	print(X_test_centered.shape, y_test.shape)



	# Best settings from tuning
	svm = SVC(kernel='rbf', C=10, gamma=0.01, random_state=None)

	# This is training the model
	svm.fit(X_train_centered, y_train)

	# Test the model data using validation data
	y_pred_val = svm.predict(X_val_centered)

	# Print out the performance metrics
	print('Misclassified validation samples: %d' % (y_val != y_pred_val).sum())
	print('Training Accuracy: %.2f' % svm.score(X_train_centered, y_train))
	print('Validation Accuracy: %.2f' % svm.score(X_val_centered, y_val))

	sum_train = sum_train + svm.score(X_train_centered, y_train)
	sum_val = sum_val + svm.score(X_val_centered, y_val)


avg_train = sum_train / 10
avg_val = sum_val / 10

print('Average Training Accuracy: %.2f%%' % (avg_train * 100))
print('Average Validation Accuracy: %.2f%%' % (avg_val * 100))

t_2 = time.perf_counter()

print('Time taken to execute code: % seconds' % (t_2-t_1))

