# Getting set up
import numpy as np;
import scipy as sp;
from sklearn import tree;
from sklearn.neighbors import KNeighborsClassifier;
from sklearn.linear_model import LogisticRegression;
from sklearn.cross_validation import cross_val_score;
from sklearn import svm;

from pandas import DataFrame, read_csv # Importing specific library functions

import matplotlib.pyplot as plt # Importing library but no functions + give nickname
import pandas as pd #this is how I usually import pandas

# Enable inline plotting
%matplotlib inline

#Tutorial: http://nbviewer.jupyter.org/urls/bitbucket.org/hrojas/learn-pandas/raw/master/lessons/01%20-%20Lesson.ipynb
#Intro to pandas and datastructures: http://www.gregreda.com/2013/10/26/intro-to-pandas-data-structures/

# Load in the data
walk_location = r'walking.csv'
walk_raw = pd.read_csv(walk_location)
drive_location = r'driving.csv'
drive_raw = pd.read_csv(drive_location)
static_location = r'static.csv'
static_raw = pd.read_csv(static_location)
upstairs_location = r'upstair.csv'
upstairs_raw = pd.read_csv(upstairs_location)
run_location = r'running.csv'
run_raw = pd.read_csv(run_location)

"""
Explanation: Emma's A3 Assignment
End of explanation
"""


walk_raw[['attitude_roll']].plot(xlim = (5000, 7000))
walk_raw[['attitude_pitch']].plot(xlim = (5000, 7000))
walk_raw[['attitude_yaw']].plot(xlim = (5000, 7000))

walk_raw[['rotation_rate_x']].plot(xlim = (5000, 7000))
walk_raw[['rotation_rate_y']].plot(xlim = (5000, 7000))
walk_raw[['rotation_rate_z']].plot(xlim = (5000, 7000))

"""
Explanation: What should my features be?
I plotted 20 seconds of data for each type of data for walking:
End of explanation
"""


walk_raw[['gravity_x']].plot(xlim = (5000, 7000))
walk_raw[['gravity_y']].plot(xlim = (5000, 7000))
walk_raw[['gravity_z']].plot(xlim = (5000, 7000))

"""
Explanation: rotation_rate_z doesn't look too good, so we'll pull that one out.
End of explanation
"""


walk_raw[['user_acc_x']].plot(xlim = (5000, 7000))
walk_raw[['user_acc_y']].plot(xlim = (5000, 7000))
walk_raw[['user_acc_z']].plot(xlim = (5000, 7000))

"""
Explanation: gravity_x and gravity_y are not periodic like the rest of our "pretty" data, so we'll cut those out as well.
End of explanation
"""


walk_raw[['magnetic_field_x']].plot(xlim = (5000, 7000))
walk_raw[['magnetic_field_y']].plot(xlim = (5000, 7000))
walk_raw[['magnetic_field_z']].plot(xlim = (5000, 7000))

"""
Explanation: Hm. None of these are beautiful, but for mean and standard deviation I think that user_acc_y and user_acc_z will be the most helpful.
End of explanation
"""


# http://stackoverflow.com/questions/17315737/split-a-large-pandas-dataframe
# input - df: a Dataframe, chunkSize: the chunk size
# output - a list of DataFrame
# purpose - splits the DataFrame into smaller of max size chunkSize (last may be smaller)
def splitDataFrameIntoSmaller(df, chunkSize = 1000): 
    listOfDf = list()
    numberChunks = len(df) // chunkSize + 1
    for i in range(numberChunks):
        listOfDf.append(df[i*chunkSize:(i+1)*chunkSize])
    return listOfDf

# Set up the 10-second chunks
walk_chunked = splitDataFrameIntoSmaller(walk_raw)
for idx, df in enumerate(walk_chunked):
    walk_chunked[idx] = pd.DataFrame(df)
    
drive_chunked = splitDataFrameIntoSmaller(drive_raw)
for idx, df in enumerate(drive_chunked):
    drive_chunked[idx] = pd.DataFrame(df)
    
static_chunked = splitDataFrameIntoSmaller(static_raw)
for idx, df in enumerate(static_chunked):
    static_chunked[idx] = pd.DataFrame(df)

upstairs_chunked = splitDataFrameIntoSmaller(upstairs_raw)
for idx, df in enumerate(upstairs_chunked):
    upstairs_chunked[idx] = pd.DataFrame(df)    
    
run_chunked = splitDataFrameIntoSmaller(run_raw)
for idx, df in enumerate(run_chunked):
    run_chunked[idx] = pd.DataFrame(df)

"""
Explanation: Again, none of these are beautiful, but for mean and standard deviation I think that magnetic_field_y and magnetic_field_z will be the most helpful.
That gives us a "who made the cut" feature list:
attitude_roll
attitude_pitch
attitude_yaw
rotation_rate_x
rotation_rate_y
gravity_z
user_acc_y
user_acc_z
magnetic_field_y
mangetic_Field_z
Still way too many! My next cut could be for which wave forms drift up or down a lot-- drifting would mess up mean and standard deviation. I'll go with attitude_roll, rotation_rate_x, and user_acc_z.
Next step: chunk up the data
End of explanation
"""


# This is where the feature data will go. The array for each activity will have length 30.
walk_featured = []
drive_featured = []
static_featured = []
upstairs_featured = []
run_featured = []

# Populate the features
for df in walk_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    walk_featured.append(features)
    
for df in drive_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    drive_featured.append(features)
    
for df in static_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    static_featured.append(features)
    
for df in upstairs_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    upstairs_featured.append(features)
    
for df in run_chunked:
    features = df.mean()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist() + df.std()[['attitude_roll','rotation_rate_x','user_acc_z','user_acc_x']].values.tolist()
    run_featured.append(features)

# Combine all of the feature sets into one big one. Along the way, generate my target array.
all_featured = walk_featured + drive_featured + static_featured + upstairs_featured + run_featured
target = [] + [0] * len(walk_featured)
target = target + [1] * len(drive_featured)
target = target + [2] * len(static_featured)
target = target + [3] * len(upstairs_featured)
target = target + [4] * len(run_featured)

# If I accidentally didn't add the right numbers to the target array, throw an error!
if target.count(0) != 30 or target.count(1) != 30 or target.count(2) != 30 or target.count(3) != 30 or target.count(4) != 30:
    raise ValueError('Target is corrupt')

"""
Explanation: Now it's time to add those features
End of explanation
"""


# Create and run cross-validation on a K-Nearest Neighbors classifier
knn = KNeighborsClassifier()
knn_scores = cross_val_score(knn, all_featured, target, cv = 5)
print 'K-NEAREST NEIGHBORS CLASSIFIER'
print knn_scores

# Create and run cross-validation on a Logistic Regression classifier
lr = LogisticRegression()
lr_scores = cross_val_score(lr, all_featured, target, cv = 5)
print 'LOGISTIC REGRESSION CLASSIFIER'
print lr_scores

# Create and run cross-validation on a Decision Tree classifier
svc = svm.SVC(kernel='linear')
svc_scores = cross_val_score(svc, all_featured, target, cv = 5)
print 'DECISION TREE CLASSIFIER'
print svc_scores

# Create and run cross-validation on a Support Vector Machine classifier
dtree = tree.DecisionTreeClassifier()
dtree_scores = cross_val_score(dtree, all_featured, target, cv = 5)
print 'SUPPORT VECTOR MACHINE CLASSIFIER'
print dtree_scores

# How I started figuring out features:
print walk_raw[['attitude_yaw']].describe()[2:3]
print run_raw[['attitude_yaw']].describe()[2:3]
print static_raw[['attitude_yaw']].describe()[2:3]
print upstairs_raw[['attitude_yaw']].describe()[2:3]
print drive_raw[['attitude_yaw']].describe()[2:3]

"""
Explanation: Running Cross-Validation
End of explanation
"""


walk_raw.plot(x='timestamp', y='attitude_yaw', title='walk attitude_yaw data', ylim=(-3,3), xlim= (681840,681850))

# Plotting one chunk (10 seconds) of walking data
chunk = walk_raw[['attitude_yaw']][1000:2000]
chunk.plot(title='10 second chunk')

# Reformat the chunk
formatted_chunk = chunk.as_matrix().tolist() # Get it out of pandas DataFrame format
formatted_chunk = [item[0] for item in formatted_chunk]

# Do a Fast Fourier Transform on attitude_yaw, chop off low frequency stuff
walk_fft = np.abs(np.fft.fft(formatted_chunk)) # Taking absolute value gives us the magnitude
plt.plot(walk_fft)
walk_fft

#walk_fft = [elem for elem in walk_fft if elem > 800]
# walk_fft = walk_fft[800:] # Low pass filter
plt.plot(walk_fft)

# Reverse the Fast Fourier Transform
walk_ifft = np.abs(np.fft.ifft(walk_fft))
plt.plot(walk_ifft)

"""
Explanation: I didn't end up getting FFT stuff to work, but here's the work that I was doing trying to get an FFT of the graph below (attitude_yaw for walking data)
End of explanation
"""


