---
title: "💻 LSE DS202A 2024: Week 03 - Homework"
author: "MY NAME (MY CANDIDATE NUMBER)"
date: 16 October 2024
format: html
self-contained: true
---

# Tasks
  
## Part I - Getting Ready 

### ⚙️ Setup

**Import required libraries:**

```{r setup, message=FALSE, warning=FALSE}
# Remember to install.packages() any missing packages

# Tidyverse packages we will use
library(dplyr)     
library(ggplot2)
library(tidyr)     
library(readr)
library(stringr)
library(rsample) #this is what allows you to split data in training and test set!

# Tidymodel packages we will use
library(yardstick) # this is where metrics like mae() are
library(parsnip)   # This is where models like linear_reg() are
library(recipes)   # You will learn about this today
library(workflows) # You will learn about this today

# And finally a package for cleaning variable names
library(janitor)

```

**Read the data set:**

It is the same data set you used in the previous lab:

It is the same data set you used in this week's lab:

```{r}
# Modify the filepath if needed
filepath <- "data/WVS_Wave_7.csv"
wvs <- read_csv(filepath)
```

As in this week's lab, if you find that your laptop is unable to handle the full data set without running slowly, try experimenting with the following code (reminder: this takes a random sample of the data, stratifying by country so the sampling algorithm doesn't take more data from one country and less from another).

```{r}
# Set a seed for reproducibility

set.seed(123)
  
wvs <-
  # Load the .csv
  read_csv("data/WVS_Wave_7.csv") %>%
  # Sample a proportion of the data set for each country.
  # We have used 25% but you can experiment depending on
  # the capability of your machine.
  group_by(iso3c) %>% 
  slice_sample(prop = 0.25) %>% 
  ungroup()

```


## Part II - Tidymodels recipes

Say our goal is to predict `satisfaction` using the variables available in the dataset with the exception of `-iso3c` (just as you did in this week's lab!). That is:

``` {r}
# Split the data with 75% being used to train the model

wvs_split <- initial_split(wvs, prop = 0.75)

# Create tibbles of the training and test set

wvs_train <- training(wvs_split)
wvs_test <- testing(wvs_split)
    

# Then train it with tidymodels:
model <- 
    linear_reg() %>%
    set_engine("lm") %>%
    fit(satisfaction ~ .-iso3c, data = wvs_train)
```

But today, we want to show you a different way of pre-processing your data before fitting a model!

**We start with a recipe**

The problem with the code above is that, if you need to use the fitted `model` to make predictions on new data, you will need to apply the same pre-processing steps to that new data. This is not a problem if you are only doing it once, but if you need to do it many times, it can become a bit of a pain. This is where the [recipes](https://recipes.tidymodels.org/) package (of tidymodels) comes in handy.

A recipe is a set of instructions of how the data should be *pre-processed* before it is used in a model. It is a bit like a cooking recipe, where you have a list of ingredients and a set of instructions on how to prepare them. In our case, the ingredient is the data frame, and the instructions are the pre-processing steps we want to apply to them.

Here's how we would construct a recipe to pre-process our data before fitting a linear regression model:

```{r}
# Create a recipe
rec <- 
  recipe(satisfaction ~ . , data = wvs) %>%
  step_rm(iso3c)


summary(rec)

```

In this particular case, the only pre-processing we do is remove the variable `iso3c` from consideration in the model: that's what's `step_rm` does. Other examples of pre-processing could include removing the observations/rows that contain missing values ((`step_naomit()`)[https://recipes.tidymodels.org//reference/step_naomit.html]) or imputing (i.e 'taking a best guess' at) them (e.g (`step_impute_median`)[https://recipes.tidymodels.org//reference/step_impute_median.html] or (`step_impute_linear`)[https://recipes.tidymodels.org//reference/step_impute_linear.html]).

:::callout-warning
The previous syntax is enough if all you're going to do is use your recipe to train a model. However, if you want to, say check the correctness of your pre-processing by printing out the actual dataset that you obtain after pre-processing, then you'll need a few extra steps to get there.

The previous code will look like this:

```{r}
rec <- 
  recipe(satisfaction ~ . , data = wvs) %>%
  step_rm(iso3c) %>%
  prep() %>%
  bake(data = wvs_train)
```

`prep()` estimates all the quantities and statistics needed for the recipe you've specified (i.e preprocessing steps) to be applied by the `bake` function (note that you specify which dataset the `bake` function, and ultimately the pre-processing steps, apply on - in this case the training set). You can think of it as the (ingredients) prepping step in a cooking recipe before you bake/cook everything and get the final meal! 

⚠️ If you don't go through the prep and bake steps, the pre-processing steps will not be applied. The recipe is just a recipe, not the final cooked meal. You need to bake it first.

How do we use this recipe? Well, we can use the `bake()` function to apply the recipe to our data frame:
:::

**How to use this in a model?**

For this we need a **workflow** to which we can attach the recipe and the model. Creating a workflow of a linear regression looks like this:

```{r}
# Create the specification of a model but don't fit it yet
lm_model <- 
  linear_reg() %>%
  set_engine("lm")

# Create a workflow to add the recipe and the model
wflow <- 
  workflow() %>% 
  add_recipe(rec) %>% 
  add_model(lm_model)

print(wflow)
```

Then, you can fit the workflow to the data:

```{r}
model <- 
  wflow %>% 
  fit(data = wvs_train)

model
```

Do you want to **confirm** what recipe was used to pre-process the data? You can `extract_recipe()`:

```{r}
model %>% 
  extract_recipe()
```

You can use your model to `augment()` the data with predictions:

```r
model %>% 
  augment(new_data = wvs_test) %>% 
  head()
```

Notice that this augments our data frame with `.pred` and `.resid` columns.

How well did our model perform?

```{r}
reg_metrics <- metric_set(rsq, rmse, mae, mape)
model %>%
    augment(new_data=wvs_test)%>%
    reg_metrics(truth = satisfaction, estimate = .pred)
```

You already know about the $Rˆ2$ (`rsq`) and $RMSE$ (`rmse`) metrics: can you make sense of the other two metrics (`mae` for MAE and `mape` for MAPE)?

```text
#Your answer here
```

(Bonus) See if you can plot the metrics for a few countries of your choice.

```{r}
# Delete this line and add your code here
```

Let's look at the residuals plot for our model!

```{r}
model %>% 
  augment(new_data = wvs_test ) %>%
  ggplot(aes(.pred, .resid)) +
  geom_point() +
  geom_hline(yintercept = 0, linetype = "dashed") +
  theme_bw() +
  labs(x = "Predicted life satisfaction", y = "Residuals")
```

## Part III - Practice with recipes and workflows

0.  Consider the following simulated data frame.

```r

set.seed(123)

df_sim <-
  tibble(outcome = rnorm(100, 0, 1),
         feature1 = rnorm(100, 0, 1),
         feature2 = rnorm(100, 0, 20))

```

We have created a data set with one outcome (creatively labeled `outcome`) and two features `feature1` and `feature2` where one variable has been assigned a standard deviation 20 times the size of the other variable. In some cases like linear regression, it will be okay to use `outcome ~ feature1 + feature2` as a formula. However, with other models, variables with larger variation will play a disproportionate role. As a result, we can use standardization (a.k.a. normalization) to counter this issue. This involves subtracting the mean of the variable and dividing the result by the standard deviation. Create a recipe for this data frame using a step from the `tidymodels` manual, and make use of graphs to demonstrate that the standardization process "worked". 

```{r}
# Delete this line and add your code here
```

1.  Some models will only require numeric input, meaning that variables like `employment` or `better_living` into several dummy variables. Using the `tidymodels` manual, can you find a step that can help us achieve this end?
   
```{r}
# Delete this line and add your code here
```

2.  Can you tweak the model we wrote in Part I to include the dummy step from Question 1? Evaluate the performance of this new model (first on the training then the test set).

```{r}
# Delete this line and add your code here
```   

3.  Write a function called `plot_residuals()` that takes a fitted workflow model and a data frame in its original form (that is, before any pre-processing) and plot the residuals against the fitted values. The function should return a ggplot object.


```{r}
plot_residuals <- function(wflow_model, data) {
    
    ... # Replace this part with your code

    g <- ggplot(plot_df, aes(x=.pred, y=.resid, ...)) +
        ... # Replace this part with your code

    return(g)
    
}
```

Using the `plot_residuals` function you've just created, can you create:

 (a) one residuals against the fitted values plot for the training data used in Question 2 
 (b) and another, separate residuals against the fitted values plot for the test data used in Question 2?

```{r}
# Delete this line and add your code here
```

4. (Bonus) Can you train a LASSO model on the data and evaluate its performance using recipes and workflows? How does this model compare to linear regression?
  
```{r}
#Write your own code here
```