```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(tidyverse) # metapackage of all tidyverse packages
library(tidymodels)
library(GGally)
# library(doParallel)
```

```{r cars}
telco <- readr::read_csv("https://raw.githubusercontent.com/curso-r/intro-ml-mestre/master/dados/telco.csv") 

glimpse(telco)
```

## Bases Treino/Teste

```{r}
set.seed(32)
telco_initial_split <- initial_split(telco %>% select(-customerID), prop = 0.8, strata = "Churn")
telco_train <- training(telco_initial_split)
telco_test <- testing(telco_initial_split)
```

## Exploratória

```{r}
skimr::skim(telco_train)
```

```{r}
visdat::vis_miss(telco_train)
```

```{r}
telco_train %>% 
  select(where(is.numeric)) %>% 
  cor(use = "p") %>% 
  corrplot::corrplot()
```

```{r, fig.height=12, message=FALSE, warning=FALSE}
telco_train %>% 
  select(where(is.numeric), Churn) %>%
  ggpairs(aes(colour = Churn))
```

```{r, fig.height=8}
contagens <- telco_train %>% 
  select(c(where(is.character), Churn)) %>%
  pivot_longer(-Churn, names_to = "variavel", values_to = "valor") %>%
  count(Churn, variavel, valor)

# tabela
contagens %>%
  pivot_wider(names_from = Churn, values_from = n)
```

```{r, fig.height=12, fig.width=12}
contagens %>%
  ggplot(aes(y = valor, x = n, fill = Churn)) +
  geom_col(position = "fill") +
  geom_label(aes(label = n), position = position_fill(vjust = 0.5)) +
  facet_wrap(~variavel, scales = "free_y", ncol = 3) +
  ggtitle("Churn vs. Variáveis Categóricas")


library(DataExplorer)
library(esquisse)

telco_train$Contract


step_corr()
step_lincomb()

```

```{r, fig.height=8}
telco_train %>% 
  select(c(where(is.numeric), Churn)) %>%
  pivot_longer(-Churn, names_to = "variavel", values_to = "valor") %>%
  ggplot(aes(y = Churn, x = valor, fill = Churn)) +
  geom_boxplot() +
  facet_wrap(~variavel, scales = "free_x") +
  # scale_x_log10() +
  ggtitle("Churn vs. Variáveis Numéricas")
```

```{r, fig.height=8}
telco_train %>% 
  select(c(where(is.numeric), Churn)) %>%
  pivot_longer(-Churn, names_to = "variavel", values_to = "valor") %>%
  ggplot(aes(x = valor, colour = Churn)) +
  stat_ecdf() +
  facet_wrap(~variavel, scales = "free_x") +
  labs(title = "Churn vs. Variáveis Numéricas",
       subtitle = "Distribuição Acumulada")
```

```{r, fig.height=13, fig.width=8}
grafico_de_barras_das_vars_continuas <- function(dados) {
  dados %>% 
    select(c(where(is.numeric), Churn)) %>%
    pivot_longer(-Churn, names_to = "variavel", values_to = "valor") %>%
    dplyr::group_by(variavel) %>%
    dplyr::mutate(
      valor = factor(dplyr::ntile(valor, 10), levels = 1:10)
    ) %>%
    dplyr::count(Churn, variavel, valor) %>%
    ggplot(aes(y = (valor), x = n, fill = Churn)) +
    geom_col(position = "fill") +
    geom_label(aes(label = n), position = position_fill(vjust = 0.5)) +
    facet_wrap(~variavel, scales = "free_y", ncol = 3) +
    ggtitle("Churn vs. Variáveis Categóricas")
}

grafico_de_barras_das_vars_continuas(telco_train)
```

## Pré-processamento

```{r}
telco_recipe <- recipe(Churn ~ ., telco_train) %>% 
  step_corr(all_numeric_predictors()) %>%
  #step_impute_mean(TotalCharges) %>% 
  step_dummy(all_nominal_predictors(), one_hot = TRUE)
```

```{r}
telco_recipe

recipe_preparada <- prep(telco_recipe)
recipe_preparada

bake(recipe_preparada, new_data = telco_test) %>% glimpse()
```

## Definição da Validação Cruzada

```{r}
telco_resamples <- vfold_cv(telco_train, v = 5)
telco_resamples
```

## Estratégia de Tunagem de Hiperparâmetros

### Passo 1:

Achar uma combinação `learning_rate` e `trees` que funciona relativamente bem. Usando uma learning_rate alta. Vamos fixar os valores dos outros parâmetros.

-   `min_n`: usar um valor entre 1 e 30 é razoável no começo.
-   `max_depth`: geralmente começamos com algo entre 4 e 6.
-   `loss_reduction`: vamos começar com 0, geralmente começamos com valores baixos.
-   `mtry`: começamos com +- 80% do número de colunas na base.
-   `sample_size`: também fazemos approx 80% do número de linhas.

Em seguida vamos tunar o `learn_rate` e `trees` em um grid assim:

-   `learn_rate` - 0.05, 0.1, 0.3
-   `trees` - 100, 500, 1000, 1500

```{r}

cores = 4
telco_model <- boost_tree(
  mtry = 0.8, 
  trees = tune(), # <---------------
  min_n = 5, 
  tree_depth = 4,
  loss_reduction = 0, # lambda
  learn_rate = tune(), # epsilon
  sample_size = 0.8
) %>% 
  set_mode("classification") %>%
  set_engine("xgboost", nthread = cores, counts = FALSE)
telco_model
```

#### Workflow

```{r}
telco_wf <- workflow() %>% 
    add_model(telco_model) %>% 
    add_recipe(telco_recipe)
```

#### Grid

```{r}
telco_grid <- expand.grid(
    learn_rate = c(0.05, 0.1, 0.3),
    trees = c(100, 500, 1000, 1500)
)
telco_grid
```

```{r, cache=TRUE}
telco_tune_grid <- telco_wf %>% 
    tune_grid(
     resamples = telco_resamples,
     grid = telco_grid,
     control = control_grid(save_pred = TRUE, verbose = TRUE, allow_par = TRUE),
     metrics = metric_set(roc_auc)
    )
```

#### Melhores hiperparâmetros

```{r}
autoplot(telco_tune_grid)
telco_tune_grid %>% show_best(metric = "roc_auc", n = 6)
telco_select_best_passo1 <- telco_tune_grid %>% 
  select_best(metric = "roc_auc")
telco_select_best_passo1
```

### Passo 2:

Vimos que com os parâmetros da árvore fixos:

-   `trees` = `r telco_select_best_passo1$trees`
-   `learn_rate` = `r telco_select_best_passo1$learn_rate`

São bons valores inciais. Agora, podemos tunar os parâmetros relacionados à árvore.

-   `tree_depth`: vamos deixar ele variar entre 3 e 10.
-   `min_n`: vamos deixar variar entre 5 e 90.

Os demais deixamos fixos como anteriormente.

```{r,  cache=TRUE}
telco_model <- boost_tree(
  mtry = 0.8,
  trees = telco_select_best_passo1$trees,
  min_n = tune(),
  tree_depth = tune(), 
  loss_reduction = 0, 
  learn_rate = telco_select_best_passo1$learn_rate, 
  sample_size = 0.8
) %>% 
  set_mode("classification") %>%
  set_engine("xgboost", nthread = cores, counts = FALSE)

#### Workflow
telco_wf <- workflow() %>% 
    add_model(telco_model) %>% 
    add_recipe(telco_recipe)

#### Grid
telco_grid <- expand.grid(
  tree_depth = c(3, 4, 6), 
  min_n = c(30, 60, 90)
)

telco_tune_grid <- telco_wf %>% 
  tune_grid(
    resamples = telco_resamples,
    grid = telco_grid,
    control = control_grid(save_pred = TRUE, verbose = FALSE, allow_par = TRUE),
    metrics = metric_set(roc_auc)
  )


#### Melhores hiperparâmetros
autoplot(telco_tune_grid)
telco_tune_grid %>% show_best(metric = "roc_auc", n = 5)
telco_select_best_passo2 <- telco_tune_grid %>% select_best(metric = "roc_auc")
telco_select_best_passo2
```

### Passo 3:

Agora temos definidos:

-   `trees` = `r telco_select_best_passo1$trees`
-   `learn_rate` = `r telco_select_best_passo1$learn_rate`
-   `min_n` = `r telco_select_best_passo2$min_n`
-   `tree_depth` = `r telco_select_best_passo2$tree_depth`

Vamos então tunar o `loss_reduction`:

`loss_reduction`: vamos deixar ele variar entre 0 e 2

```{r,  cache=TRUE}
telco_model <- boost_tree(
  mtry = 0.8,
  trees = telco_select_best_passo1$trees,
  min_n = telco_select_best_passo2$min_n,
  tree_depth = telco_select_best_passo2$tree_depth, 
  loss_reduction = tune(), 
  learn_rate = telco_select_best_passo1$learn_rate, 
  sample_size = 0.8
) %>% 
  set_mode("classification") %>%
  set_engine("xgboost", nthread = cores, counts = FALSE)

#### Workflow
telco_wf <- workflow() %>% 
    add_model(telco_model) %>% 
    add_recipe(telco_recipe)

#### Grid
telco_grid <- expand.grid(
  loss_reduction = c(0, 0.05, 1, 2)
)

telco_tune_grid <- telco_wf %>% 
  tune_grid(
    resamples = telco_resamples,
    grid = telco_grid,
    control = control_grid(save_pred = TRUE, verbose = FALSE, allow_par = TRUE),
    metrics = metric_set(roc_auc)
  )

#### Melhores hiperparâmetros
autoplot(telco_tune_grid)
telco_tune_grid %>% show_best(metric = "roc_auc", n = 5)
telco_select_best_passo3 <- telco_tune_grid %>% select_best(metric = "roc_auc")
telco_select_best_passo3
```

### Passo 4:

Não parece que o `lossreduction` teve tanto efeito, mas, vamos usar `r telco_select_best_passo3$loss_reduction` que deu o melhor resultado. Até agora temos definido:

-   `trees` = `r telco_select_best_passo1$trees`
-   `learn_rate` = `r telco_select_best_passo1$learn_rate`
-   `min_n` = `r telco_select_best_passo2$min_n`
-   `tree_depth` = `r telco_select_best_passo2$tree_depth`
-   `lossreduction` = `r telco_select_best_passo3$loss_reduction`

Vamos então tunar o `mtry` e o `sample_size`:

-   `mtry`: de 10% a 100%
-   `sample_size`: de 50% a 100%

```{r}
telco_model <- boost_tree(
  mtry = tune(),
  trees = telco_select_best_passo1$trees,
  min_n = telco_select_best_passo2$min_n,
  tree_depth = telco_select_best_passo2$tree_depth, 
  loss_reduction = telco_select_best_passo3$loss_reduction, 
  learn_rate = telco_select_best_passo1$learn_rate, 
  sample_size = tune()
) %>% 
  set_mode("classification") %>%
  set_engine("xgboost", nthread = cores, counts = FALSE)

#### Workflow
telco_wf <- workflow() %>% 
    add_model(telco_model) %>% 
    add_recipe(telco_recipe)

#### Grid
telco_grid <- expand.grid(
    sample_size = seq(0.5, 1.0, length.out = 5),
    mtry = seq(0.1, 1.0, length.out = 5)
)

telco_tune_grid <- telco_wf %>% 
  tune_grid(
    resamples = telco_resamples,
    grid = telco_grid,
    control = control_grid(save_pred = TRUE, verbose = FALSE, allow_par = TRUE),
    metrics = metric_set(roc_auc)
  )

#### Melhores hiperparâmetros
autoplot(telco_tune_grid)
telco_tune_grid %>% show_best(metric = "roc_auc", n = 5)
telco_select_best_passo4 <- telco_tune_grid %>% select_best(metric = "roc_auc")
telco_select_best_passo4
```

### Passo 5:

Vimos que a melhor combinação foi

-   `mtry` = `r telco_select_best_passo4$mtry`
-   `sample_size` = `r telco_select_best_passo4$sample_size`

Agora vamos tunar o `learn_rate` e o `trees` de novo, mas deixando o `learn_rate` assumir valores menores.

```{r}
telco_model <- boost_tree(
  mtry = telco_select_best_passo4$mtry,
  trees = tune(),
  min_n = telco_select_best_passo2$min_n,
  tree_depth = telco_select_best_passo2$tree_depth, 
  loss_reduction = telco_select_best_passo3$loss_reduction, 
  learn_rate = tune(), 
  sample_size = telco_select_best_passo4$sample_size
) %>% 
  set_mode("classification") %>%
  set_engine("xgboost", nthread = cores, counts = FALSE)

#grid_regular()
#grid_random()
#tune_bayes()

#### Workflow
telco_wf <- workflow() %>% 
    add_model(telco_model) %>% 
    add_recipe(telco_recipe)

#### Grid
telco_grid <- expand.grid(
    learn_rate = c(0.025, 0.04, 0.05, 0.06, 0.07, 0.08),
    trees = c(50, 75, 90, 100, 250)
)

telco_tune_grid <- telco_wf %>% 
  tune_grid(
    resamples = telco_resamples,
    grid = telco_grid,
    control = control_grid(save_pred = TRUE, verbose = FALSE, allow_par = TRUE),
    metrics = metric_set(roc_auc)
  )

#### Melhores hiperparâmetros
autoplot(telco_tune_grid)
telco_tune_grid %>% show_best(metric = "roc_auc", n = 5)
telco_select_best_passo5 <- telco_tune_grid %>% select_best(metric = "roc_auc")
telco_select_best_passo5
```

## Desempenho do Modelo Final

```{r}
telco_model <- boost_tree(
  mtry = telco_select_best_passo4$mtry,
  trees = telco_select_best_passo5$trees,
  min_n = telco_select_best_passo2$min_n,
  tree_depth = telco_select_best_passo2$tree_depth, 
  loss_reduction = telco_select_best_passo3$loss_reduction, 
  learn_rate = telco_select_best_passo5$learn_rate, 
  sample_size = telco_select_best_passo4$sample_size
) %>% 
  set_mode("classification") %>%
  set_engine("xgboost", nthread = cores, counts = FALSE)

#### Workflow
telco_wf <- workflow() %>% 
    add_model(telco_model) %>% 
    add_recipe(telco_recipe)

telco_last_fit <- telco_wf %>% 
  last_fit(
    split = telco_initial_split,
    control = control_grid(save_pred = TRUE, verbose = FALSE, allow_par = TRUE),
    metrics = metric_set(roc_auc, f_meas, accuracy, precision, recall)
  )

#### Métricas
collect_metrics(telco_last_fit)

#### Variáveis Importantes



telco_last_fit %>% 
  pluck(".workflow", 1) %>%   
  pull_workflow_fit() %>% 
  vip::vip(num_features = 20)

#### Curva ROC
telco_last_fit %>% 
    collect_predictions() %>% 
    roc_curve(Churn, .pred_No) %>% 
    autoplot()

```

## MODELO FINAL FINAL

```{r}
telco_modelo_final <- telco_wf %>% fit(telco)

saveRDS(telco_modelo_final, "telco_modelo_final.rds")

predict(telco_modelo_final, new_data = telco_test, type="prob") %>%
  arrange(desc(.pred_Yes))

table(
  predict(telco_modelo_final, new_data = telco_test, type="prob")$.pred_Yes > 0.5,
  telco_test$Churn
)

```
