# Libraries and environment
Load libraries and set the seed.
```{r include=FALSE}
library(tidyverse)
library(data.table)
library(caret)
library(rattle)
set.seed(19930212)
```

# Load data
Load data into R
```{r}
train_url <- "https://d396qusza40orc.cloudfront.net/predmachlearn/pml-training.csv"
test_url <- "https://d396qusza40orc.cloudfront.net/predmachlearn/pml-testing.csv"

raw_train <- fread(train_url)
raw_test <- fread(test_url)
```

# Feature selection

## Cleaning the data
```{r}
# Create a new dataframe for training
training <- as.data.frame(raw_train)

# clean the data

# turn the classe column into a factor
training$classe <- factor(training$classe)

# turn all integers into numeric
for (i in colnames(training)){
  class_i <- class(training[[i]])
  if(class_i == "integer"){training[[i]] <- as.numeric(training[[i]])}
}

# how many features are there?
dim(training)
```

## Removing zero covariates
```{r}
# check for near zero variance
nsv <- nearZeroVar(training, saveMetrics = TRUE)
nsv[nsv$nzv == TRUE,]

# remove the columns with near zero variances
drop_cols <- rownames(nsv[nsv$nzv == TRUE,])
training <- training %>% select(-drop_cols)

# how many features are there?
dim(training)
```

## Remove missing values
The data has some columns with many missing values. Generally, the columns that look to be summary level statistics are missing values in most cases (e.g. columns starting with max, min, var, etc.)
```{r}
# total missing values
sum(is.na(training))

# missing values by column
sapply(training, function(x) sum(is.na(x))/nrow(training))

# Remove columns that do not have enough data. We will remove any column that does not have data for at least 95% of observations.
greater_than_95 <- sapply(training, function(x) sum(is.na(x))/nrow(training)<0.05)
training <- training[, greater_than_95]

# how many features are there?
dim(training)
```


## Remove specific columns
Remove specific features that are not relevant:

- "V1" should be removed as it is an index and is random
- "user_name" should be removed since we want to predict the outcome based on the sensor data, not the person performing the exercise
- "raw_timestamp_part_1", "raw_timestamp_part_2", "cvtd_timestamp", "new_window", and "num_window" should all be removed because they are timestamps, and do not have any meaningful relationship to how well the movement was performed
- any column that starts with "total" because it is summary statistic and not capturing the same level of detail as the rest of the data

```{r}
# drop selected columns
drop_cols <- c("V1", "user_name", "raw_timestamp_part_1", "raw_timestamp_part_2", "cvtd_timestamp", "num_window")
training <- training %>% select(-one_of(drop_cols))

# remove columns that start with "total"
training <- training %>% select(-starts_with("total"))

# how many features are there?
dim(training)
```

## Selected features
Our model will use the following features.
```{r}
str(training)
```

# Cross validation
Partition the training data into a subset so we can better estimate the out of sample error rate
```{r}
inTrain <- createDataPartition(y = training$classe, p = 0.75, list = FALSE)
training_train <- training[inTrain,]
training_test <- training[-inTrain,]
```


# Model #1: Classificaiton Tree (rpart)
## Build the model
```{r}
# fit the model
model_1 <- rpart::rpart(classe ~ .,
                 data = training_train,
                 method = "class")
```

## View the model
```{r}
# view model
model_1

# plot model
fancyRpartPlot(model_1)
```

## Assess the model results

### In sample error
First we will assess the accuracy of our model using the same data that was used to build the model.
```{r}
predict_in_sample <- predict(model_1, newdata = training_train, type = "class")
conf_in_sample <- confusionMatrix(predict_in_sample, training_train$classe)
conf_in_sample

conf_in_sample$overall[[1]]

```

Our model has predicted the correct classe with an accuracy of `r conf_in_sample$overall[[1]]`. This number gives us a good sense of what the out of sample error may be. However we can get a better estimate by using the portion of the training data that we set aside to estimate the out of sample error rate.

### Out of sample error

Predict using the training data that was set aside
```{r}
predict_out_sample <- predict(model_1, newdata = training_test, type = "class")
conf_out_sample <- confusionMatrix(predict_out_sample, training_test$classe)
conf_out_sample
```

Our model has predicted the correct classe with an accuracy of `r conf_out_sample$overall[[1]]`. This number is a good estimate of the out of sample error rate, as these records were not used to build the model.

# Predictions

The final step is to apply our model to the test data. Before applying the model, we must first make the same transformations to the test data that we made to the training data.

```{r}
# Create a new data frame
testing <- as.data.frame(raw_test)

# turn all integers into numeric
for (i in colnames(testing)){
  class_i <- class(testing[[i]])
  if(class_i == "integer"){testing[[i]] <- as.numeric(testing[[i]])}
}

# Keep only the same columns that were used in the training model
keep_cols <- names(training)
keep_cols <- keep_cols[!keep_cols == "classe"] # remove classe, since this does not exist in the testing data
testing <- testing[ ,keep_cols]
```

Our data is now ready to be fed into the model for predictions.

```{r}
predict_final <- predict(model_1, newdata = testing, type = "class")
(predict_final)
```

