#!/usr/bin/env python
import arff
import numpy as np
import sys
from sklearn import preprocessing
#from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.feature_selection import RFE
from sklearn.pipeline import Pipeline
from sklearn.grid_search import GridSearchCV
from sklearn.cross_validation import StratifiedKFold
from sklearn import cross_validation
from sklearn import metrics

#Parameters:
filename = sys.argv[1]
n_estimators = [100,200,300,500,1000,3000]
#n_estimators = [1000]
n_featuresToSelect = [2,3,5,10,20]
#n_featuresToSelect = [20]
maxDepth = [2,3,4,5,10]
#maxDepth = [2]
verboseLevel=10
n_jobs=10
n_crossValidation=10
n_accuracy=10
#n_accuracy=2
n_folds=10
# 10% removal of features
stepSize=0.1

print "Load dataset"
# Load dataset
arffDecoder = arff.ArffDecoder()
dataset = arffDecoder.decode(open(filename, 'rb'), encode_nominal=True)

print "Preprocess dataset"
# Get categorical features
categoricals = []
# NOTE: skip last (class) 'feature'
for feature in range(0,len(dataset['attributes'])-1):
    if isinstance(dataset['attributes'][feature][1], list):
        categoricals.append(feature)
print "Categorical indices: {0}".format(categoricals)

# Apply OneHotEncoder
oneHotEncoder = preprocessing.OneHotEncoder(categorical_features=categoricals, sparse=False)
print "Number of features: {0}".format(len(dataset['data'][0]))
print "Number of samples: {0}".format(len(dataset['data']))
binData = oneHotEncoder.fit_transform(np.array(dataset['data']))
print "n-values: {0}".format(oneHotEncoder.n_values_)
print "feature indices: {0}".format(oneHotEncoder.feature_indices_)
print "Number of binarised features: {0}".format(len(binData[0]))
print "Number of binarised samples: {0}".format(len(binData))

# Setting up input and outputs
inputs = binData[:,:-1]
output = binData[:,-1]

print "Start grid search"
# Setup experimental pipeline
scaler = preprocessing.RobustScaler()
#classifier = RandomForestClassifier(n_estimators=n_estimators[0],max_depth=maxDepth[0],oob_score=True,bootstrap=True)
classifier = ExtraTreesClassifier(n_estimators=n_estimators[0],max_depth=maxDepth[0],oob_score=True,bootstrap=True)
selector = RFE(classifier,n_features_to_select=n_featuresToSelect[0],step=stepSize)
pipeline = Pipeline([("scaler",scaler),("RFE",selector),("classifier",classifier)])
paramGrid = dict(RFE__n_features_to_select=n_featuresToSelect, classifier__max_depth=maxDepth, classifier__n_estimators=n_estimators)

# Do grid search
gridSearch = GridSearchCV(pipeline,param_grid=paramGrid,verbose=verboseLevel,n_jobs=n_jobs,cv=n_crossValidation)
gridSearch.fit(inputs,output)
estimator = gridSearch.best_estimator_
print "Results: "
print "Selected features: {0}".format(estimator.named_steps['RFE'].n_features_to_select)
print "Max depth: {0}".format(estimator.named_steps['classifier'].max_depth)
print "Number of trees: {0}".format(estimator.named_steps['classifier'].n_estimators)

# Calculate accuracies
print "Calculate accuracies"
accuracy = []
for count in range(0,n_accuracy):
    cv = StratifiedKFold(output,n_folds=n_folds,shuffle=True)
    predicted = cross_validation.cross_val_predict(estimator,inputs,output,cv=cv,verbose=verboseLevel,n_jobs=n_jobs,)
    score = metrics.accuracy_score(output,predicted,normalize=True)
    accuracy.append(score)
print "Accuracy array: {0}".format(accuracy)
print "Cross-validation accuracy of final model {0}".format(np.mean(accuracy))