# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 2
#     language: python
#     name: python2
# ---

# # Goal: Classify vehicle as bus or car based on smartphone sensor data

import pandas as pd
from scipy.ndimage import gaussian_filter
from sklearn.ensemble import RandomForestClassifier
from sklearn.cross_validation import cross_val_score
from sklearn.metrics import accuracy_score
import numpy as np
# %matplotlib inline

# #### Load the processed sensor data for car trip (see "Process Smartphone Sensor Data" jupyter notebook).  On this trip, I drove my car from home to Censio and back and used SensorLog on my iPhone to track the trip.  The total time for the trip was about 15 minutes.

dfcar = pd.read_csv('../Data/shaneiphone_exp2_processed.csv', index_col='DateTime')

# #### Load the processed sensor data for bus trip (see "Process Smartphone Sensor Data" jupyter notebook).  On this trip, I took the 47 bus for about 10 minutes.

dfbus = pd.read_csv('../Data/shanebus20150827_processed.csv', index_col='DateTime')

# combine into a single dataframe
df = pd.concat([dfcar, dfbus])

# Use only userAcceleration and gyroscope data, since these features are expected to generalize well.
xyz = ['X', 'Y', 'Z']
measures = ['userAcceleration', 'gyroscope']
basefeatures = [i + j for i in measures for j in xyz]
features = [i + j for i in measures for j in xyz]

# Add Gaussian smoothed features
smoothfeatures = []
for i in features:
    df[i + 'sm'] = gaussian_filter(df[i], 3)
    df[i + '2sm'] = gaussian_filter(df[i], 100)
    smoothfeatures.append(i + 'sm')
    smoothfeatures.append(i + '2sm')
features.extend(smoothfeatures)

# Generate Jerk signal
jerkfeatures = []
for i in features:
    diffsignal = np.diff(df[i])
    df[i + 'jerk'] = np.append(0, diffsignal)
    jerkfeatures.append(i + 'jerk')
features.extend(jerkfeatures)

# +
# assign class labels
car0 = (df.index > '2015-08-25 14:35:00') & \
        (df.index <= '2015-08-25 14:42:00')

car1 = (df.index > '2015-08-25 14:43:00') & \
        (df.index <= '2015-08-25 14:48:00')

bus0 = (df.index > '2015-08-27 10:10:00') & \
        (df.index <= '2015-08-27 10:15:00')
bus1 = (df.index > '2015-08-27 10:15:00') & \
        (df.index <= '2015-08-27 10:20:00')

nc = len(df)
df['class'] = np.zeros(nc) - 1
df['class'][car0] = np.zeros(nc)
df['class'][car1] = np.zeros(nc)
df['class'][bus0] = np.ones(nc)
df['class'][bus1] = np.ones(nc)
# -

# separate into quarters for train and validation
q1 = df[car0]
q2 = df[car1]
q3 = df[bus0]
q4 = df[bus1]
traindf = pd.concat([q2, q4])
validationdf = pd.concat([q1, q3])

# check for NaNs in the dataframes
print(traindf.isnull().sum().sum())
print(validationdf.isnull().sum().sum())

# drop NaNs
traindf = traindf.dropna()
validationdf = validationdf.dropna()

# Make the training and validation sets
X_train = traindf[features].values
y_train = traindf['class'].values
X_test = validationdf[features].values
y_test = validationdf['class'].values

# train a random forest
clf = RandomForestClassifier(n_estimators=200)

# get the 5-fold cross-validation score
scores = cross_val_score(clf, X_train, y_train, cv=5)
print(scores, scores.mean(), scores.std())

# apply model to test set
clf.fit(X_train, y_train)
predict_y = clf.predict(X_test)

# obtain accuracy score
testscore = accuracy_score(y_test, predict_y)
print("Accuracy score on test set: %6.3f" % testscore)

# #### We're not overfitting the data, but we're also not really predicting the vehicle class very well, since we're only right about 65-70% of the time with any prediction we make.

# Inspect feature importances
for i, ifeature in enumerate(features):
    print(ifeature + ': %6.4f' % clf.feature_importances_[i])

# #### The smoothed gyroscopeZ data is the most useful feature.

# compare bus gyroscopeZ2sm and car gyroscopeZ2sm
q1['gyroscopeZ2sm'].plot(color='blue', figsize=(12,6), kind='hist', bins=40, alpha=0.4) # car
q3['gyroscopeZ2sm'].plot(color='green', kind='hist', bins=40, alpha=0.4) # bus

# #### Reflecting on this further, it occurs to me that this methodology is identifying that the bus trip and the car trip followed different routes and had different numbers and types of turns.  A better way to go might be to identify features for each turn (e.g., time to complete turn, average accelerometer and gyroscope signal during turn, etc.) and apply the random forest to those features.

# #### Another interesting avenue to pursue is features in Fourier space

# Generate Fourier Transform of features
fftfeatures = []
for i in features:
    reals = np.real(np.fft.rfft(df[i]))
    imags = np.imag(np.fft.rfft(df[i]))
    complexs = [reals[0]]
    n = len(reals)
    if n % 2 == 0:
        complexs.append(imags[0])
    for j in range(1, n - 1):
        complexs.append(reals[j])
        complexs.append(imags[j])
    complexs.append(reals[j])
    df['f' + i] = complexs
    fftfeatures.append('f' + i)
features.extend(fftfeatures)

# Make the training and validation sets
X_train = traindf[fftfeatures].values
y_train = traindf['class'].values
X_test = validationdf[fftfeatures].values
y_test = validationdf['class'].values

# train a random forest
clf = RandomForestClassifier(n_estimators=200)

# get the 5-fold cross-validation score
scores = cross_val_score(clf, X_train, y_train, cv=5)
print(scores, scores.mean(), scores.std())

# apply model to test set
clf.fit(X_train, y_train)
predict_y = clf.predict(X_test)

# obtain accuracy score
testscore = accuracy_score(y_test, predict_y)
print("Accuracy score on test set: %6.3f" % testscore)

# #### Much better accuracy on the test set: 87%.  We are definitely overfitting here, since we got 100% accuracy on the training set.  We are also probably suffering from the same problem using the time series data, where the classifier learns to classify based on the nature of the route, not the nature of the ride.

# Inspect feature importances
for i, ifeature in enumerate(fftfeatures):
    print(ifeature + ': %6.4f' % clf.feature_importances_[i])

# #### Interesting that the accelerometer signal is more important here.  This could be an indication that training in Fourier space helps mitigate the route-based issues that we encountered when using the time series data.



StarcoderdataJupyterScriptsDedupFiltered