# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3.9.11 ('base')
#     language: python
#     name: python3
# ---

# +
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

import warnings

warnings.filterwarnings("ignore")

# -

tb_wine = pd.read_csv("data/wine-clustering.csv")
tb_wine.info()


# # Processamento de Dados

# ## Variáveis Continuas

# ### Scalers
#
# Scalers são transformações que buscam tornar as variáveis em um dataset comparáveis entre si. O mais comum de se utilizar é o StandardScaler, que utilizamos extensamente ao longo das últimas semanas.

# #### StandardScaler - Normalização
#
# O objeto `StandardScaler` subtrai a média e divide a variável pelo desvio padrão. Essa transformação é utilizada para remover a unidade (metros, litros, número de pessoas...) da variável: a nova variável calculada tem como unidade **Desvios Padrões**, *ela será 0 na média e 1 em média + 1 desvio padrão*.
#
# **QUANDO UTILIZAR:** Toda vez que formos utilizar features em um modelo.

from sklearn.preprocessing import StandardScaler


sns.kdeplot(data=tb_wine, x="Alcohol")


# Inicializamos o objeto
alcohol_scaler = StandardScaler()
# Utilizamos o método fit_transform para calcular a transformação
# e retornar a variável transformada de uma vez.
tb_wine["alcohol_sc"] = alcohol_scaler.fit_transform(tb_wine[["Alcohol"]])


fig, ax = plt.subplots(2, 1, figsize=(15, 10))
sns.kdeplot(data=tb_wine, x="Alcohol", ax=ax[0])
sns.kdeplot(data=tb_wine, x="alcohol_sc", ax=ax[1])
fig.suptitle("Visualizando o Efeito do StandardScaler\n sobre a variável Alcohol")


# Podemos utilizar o método `.fit` seguido do método `.transform`. O método `.fit` (assim como o `.fit_transform`) pode normalizar **muitas variáveis ao mesmo tempo**.

tb_wine = tb_wine.drop("alcohol_sc", axis=1)
wine_scaler = StandardScaler()
wine_scaler.fit(tb_wine)


# Todos os métodos da biblioteca `sklearn` retornam **arrays do numpy**, não dataframes. Se quisermos guardar o resultado de transformações em dataframes (para visualização por exemplo) precisamos faze-lo explicitamente.

tb_wine_sca = pd.DataFrame(wine_scaler.transform(tb_wine), columns=tb_wine.columns)
tb_wine_sca.describe()


sns.pairplot(tb_wine_sca, vars=["Alcohol", "Malic_Acid", "Ash"])


# Podemos ver no gráfico acima que as variáveis normalizadas tem média 0 e que estão variando entre aprox. -3 e 3 desvios padrões.

# #### PowerTransformer
#
# As transformações de potência buscam tornar uma variável mais **normal** (no sentido da distribuição probabilística). Ela é uma extensão da transformação logarítmica. O objeto `PowerTransformer` realiza essa transformação, retornando uma variável transformada e normalizada (no sentido do `StandardScaler`).
#
# **QUANDO UTILIZAR:** Sempre que pensarmos em utilizar o logaritmo de uma variável (resposta ou feature.)

from sklearn.preprocessing import PowerTransformer


# Vamos comparar o efeito do `PowerTransformer` para a variável `Malic_Acid` com a variável original e sua normalização.

# +
malic_ptran = PowerTransformer()
malic_scaler = StandardScaler()
tb_wine["malic_acid_pt"] = malic_ptran.fit_transform(
    tb_wine[["Malic_Acid"]]
)  # NOTEM O USO DE COLCHETES DUPLOS!!!
tb_wine["malic_acid_sc"] = malic_scaler.fit_transform(
    tb_wine[["Malic_Acid"]]
)  # NOTEM O USO DE COLCHETES DUPLOS!!!
fig, ax = plt.subplots(3, 1, figsize=(15, 15))
sns.kdeplot(data=tb_wine, x="Malic_Acid", ax=ax[0])

sns.kdeplot(data=tb_wine, x="malic_acid_sc", ax=ax[1])
sns.kdeplot(data=tb_wine, x="malic_acid_pt", ax=ax[2])
ax[0].set_title("Variável Original")
ax[1].set_title("StandardScaler")
ax[2].set_title("PowerTransform")
ax[0].set_xlim(-7, 7)
ax[1].set_xlim(-7, 7)
ax[2].set_xlim(-7, 7)
ax[0].set_xlabel("")
ax[1].set_xlabel("")
ax[2].set_xlabel("")

fig.suptitle("Visualizando o Efeito do PowerTransformer\n sobre a variável Malic_Acid")

# -

# Como podemos ver no gráfico acima o PowerTransformer torna a variável mais **simétrica**: os valores estão mais bem distribuidos ao redor da média. Podemos utilizar o método `.inverse_transform` para recuperar a variável original (esse método também existe para o `StandardScaler`):

tb_wine["malic_acid_it"] = malic_ptran.inverse_transform(tb_wine[["malic_acid_pt"]])
sns.kdeplot(data=tb_wine, x="malic_acid_it")
tb_wine = tb_wine.drop(["malic_acid_sc", "malic_acid_pt", "malic_acid_it"], axis=1)


# ### Criação de Features
# Além da normalização de features podemos utilizar alguns métodos do sub-módulo `sklearn.preprocessing` para criar novas features.

# #### PolynomialFeatures
#
# Muitas relações entre variáveis não são lineares - algumas destas são polinomias (como a Lei da Gravidade, que é uma relação quadrática). Podemos representar essas relações em modelos lineares utilizando uma transformação polinomial.
#
# **QUANDO UTILIZAR** Sempre que quisermos representar efeitos não-lineares em um modelo linear.

from sklearn.preprocessing import PolynomialFeatures

# Vamos criar features quadráticas (degree = 2) para a variável 
# Alcohol, possibilitando modelar impactos não lineares
# desta variável sobre outras variáveis.
alcohol_poly = PolynomialFeatures(degree = 2)
alcohol_poly.fit(tb_wine[['Alcohol']])
alcohol_poly.transform(tb_wine[['Alcohol']])

# Se quisermos guardar o resultado dessa transformação em um DataFrame precisamos faze-lo explicitamente.

tb_alcohol_quad = pd.DataFrame(alcohol_poly.transform(tb_wine[['Alcohol']]), columns = alcohol_poly.get_feature_names_out())
tb_alcohol_quad.head()

# #### Spline Transformer
#
# Muitas vezes a escala de uma variável é determinante no impacto desta sobre outra variável: como vimos no exemplo de seguros de saúde, o preço era impactado pelo BMI apenas quando este passava de 30. Em modelos mais complexos não conseguimos visualizar tão claramente essas relações mas podemos utilizar **b-splines** para tentar representa-los.
#
# **QUANDO UTILIZAR:** Quando suspeitarmos que o efeito de uma variável é **local**, por exemplo, considerando o feature `Tempo` os lockdowns devido ao COVID-19 foram efeitos locais do tempo.

from sklearn.preprocessing import SplineTransformer

# Os parâmetros n_knots e degree determinam quantos splines 
# criaremos para uma variavel: n_knots + degree - 1
alcohol_spline = SplineTransformer(n_knots = 5, degree = 5)
alcohol_spline.fit(tb_wine[['Alcohol']])
alcohol_spline.transform(tb_wine[['Alcohol']])

# Se quisermos utilizar o resultado em um DataFrame, precisamos construí-lo explicitamente.

tb_sp_alc = pd.DataFrame(alcohol_spline.transform(tb_wine[['Alcohol']]), columns = alcohol_spline.get_feature_names_out())
tb_sp_alc.head()

# ## Variáveis Categóricas
#
# As variáveis categóricas precisam ser transformadas em variáveis numéricas antes de podermos utiliza-las em modelos da sklearn. Podemos adotar duas estratégias para esta transformação: converte-las em variáveis ordinais ou em variáveis dummy.

# ### Variáveis Ordinais
# Variáveis ordinais são variáveis categóricas que tem uma escala, por exemplo uma variável com os níveis *Muito Infeliz, Infeliz, Neutro, Feliz, Muito Feliz*. Podemos converter esse tipo de variável em uma variável numérica assumindo que os degraus entre cada nível das variáveis é igual entre si.
#
# **QUANDO UTILIZAR:** Quando nossa variável categórica for ordenável.

tb_wine["alcohol_classif"] = pd.qcut(tb_wine["Alcohol"], 3, labels=["A", "B", "C"])
tb_wine["alcohol_classif"] = tb_wine["alcohol_classif"].astype(str)
tb_wine["alcohol_classif"]


dict_alcohol = {"A": 0, "B": 1, "C": 2}
tb_wine["alcohol_classif_num"] = tb_wine["alcohol_classif"].map(dict_alcohol)
tb_wine["alcohol_classif_num"]


# ### OneHotEncoder
# Podemos transformar utilizar o OneHotEncoder para criar variáveis dummies a partir de uma variável categórica, desta forma converteremos cada nível da variável categórica em uma nova variável binária.
#
# **QUANDO UTILIZAR:** Quando nossa variável categórica não tiver um ordenamento regular.

from sklearn.preprocessing import OneHotEncoder


# Vamos inicializar o OneHotEncoder com 2 hiperparâmetros.
# drop = 'first' criará n-1 variáveis dummies, onde n é o número de níveis.
# sparse = False permitirá a transformação do array resultante em um DataFrame.
ohe_fit = OneHotEncoder(drop="first", sparse=False)
ohe_fit.fit(tb_wine[['alcohol_classif']])
ohe_fit.transform(tb_wine[['alcohol_classif']])

# Se quisermos utilizar isto em um DataFrame, precisamos cria-lo explicitamente.

tb_ac = pd.DataFrame(ohe_fit.transform(tb_wine[['alcohol_classif']]), columns = ohe_fit.get_feature_names_out())
tb_ac.head()

tb_wine = tb_wine.drop(['alcohol_classif', 'alcohol_classif_num'], axis = 1)

# # Modelos Não-Supervisionados
#
# **Aulas** 
# * `aulas/64 DA PT NOV-2021 Aula 20220317 PCA.ipynb`
# * `aulas/64 DA PT NOV-2021 Aula 20220324 Unsupervised Learning I.ipynb`
# * `aulas/64 DA PT NOV-2021 Aula 20220324 Unsupervised Learning II.ipynb`
# * `aulas/64 DA PT NOV-2021 Aula 20220324 Unsupervised Learning III.ipynb`
#
#
# **Case**
# * **Padrões de Movimento de Urubus (Aglomerativo):** `cases/Padrões de Movimento - Urubu.ipynb` 
# * **Padrões de Movimento de Zebras (DBSCAN/HDBSCAN):** `cases/Padrões de Movimento - Zebras.ipynb`

# ## PCA
#
# PCA é um método de decomposição que transforma um conjunto de variáveis em outro conjunto de variáveis que não são correlatas entre si.
#
# **QUANDO UTILIZAR:**
# * **EDA:** para entender se nosso feature set tem muitas colinearidades, analisando a proporção entre o número de componentes, a variação explicada e o número de variáveis completas (scree-plot).
# * **MODELAGEM:** para tratar de colinearidade entre variáveis na entrada de um modelo.

from sklearn.decomposition import PCA

# ### Escolhendo número de componentes

pca_wine = PCA()
pca_wine.fit(wine_scaler.transform(tb_wine))  # SEMPRE FAZER PCA COM DADOS NORMALIZADOS


# Scree plot representa quantos % da variância total das variáveis cada componente representa

plt.plot(pca_wine.explained_variance_ratio_);


plt.plot(np.cumsum(pca_wine.explained_variance_ratio_));

# #### Método do cotovelo

from kneed import KneeLocator


knee_fit = KneeLocator(
    range(
        len(pca_wine.explained_variance_ratio_)
    ),  # vetor com número dos componentes de 0 à 12
    np.cumsum(
        pca_wine.explained_variance_ratio_
    ),  # soma acumulada da variância explicada por cada componente
)
print(f"Método do cotovelo sugere: {knee_fit.knee} Componentes")
knee_fit.plot_knee_normalized()


# Agora podemos utilizar o número de componentes calculado no atributo `knee_fit.knee` para calcular nosso PCA.

pca_wine = PCA(n_components=knee_fit.knee)
ar_pca_wine = pca_wine.fit_transform(
    wine_scaler.transform(tb_wine)
)  # SEMPRE NORMALIZAR DADOS ANTES DE PCA
tb_pca_wine = pd.DataFrame(
    ar_pca_wine, columns=["PC" + str(i) for i in range(knee_fit.knee)]
)
sns.pairplot(tb_pca_wine)


# ## KMeans
#
# KMeans é uma técnica de clusterização simples que utiliza um método iterativo para encontrar um número pré-definido de clusters.
#
# **QUANDO UTILIZAR:** é uma técnica rápida (computacionalmente) que nos permite criar uma nova variável categórica através da qual podemos explorar nossos dados. 
#
# > K-means is the simplest. To implement and to run. All you need to do is choose "k" and run it a number of times. 
# > 
# > Most more clever algorithms (in particular the good ones) are much harder to implement efficiently (you'll see factors of 100x in runtime differences) and have much more parameters to set. 
# > 
# > Plus, most people don't need quality clusters. They actually are happy with anything remotely working for them. Plus, they don't really know what to do when they had more complex clusters. K-means, which models clusters using the simplest model ever - a centroid - is exactly what they need: massive data reduction to centroids.

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

# ### Escolhendo número de clusters - Método do Cotovelo
# Vamos utilizar a inércia e a silhueta, duas medidas de qualidade de clusters para encontrar um K **que faça sentido**.

inertia_list = []
sil_list = []
tb_sca_wine = wine_scaler.transform(tb_wine)
for k in range(2, 16):
    kmeans_fit = KMeans(n_clusters = k)
    kmeans_fit.fit(tb_sca_wine) # SEMPRE NORMALIZAR ANTES DE KMEANS
    inertia_list.append(kmeans_fit.inertia_)
    sil_list.append(silhouette_score(tb_sca_wine, kmeans_fit.labels_))
tb_score = pd.DataFrame({'k' : range(2, 16), 'inertia' : inertia_list, 'silhouette' : sil_list})

fig, ax = plt.subplots(1, 2, figsize = (15, 7))
sns.lineplot(data = tb_score, x = 'k', y = 'inertia', ax = ax[0])
sns.lineplot(data = tb_score, x = 'k', y = 'silhouette', ax = ax[1])

knee_fit = KneeLocator(
    tb_score['k'],
    np.cumsum(tb_score['inertia'])
)
print(f"Método do cotovelo sugere: {knee_fit.knee} Componentes")
knee_fit.plot_knee_normalized()


# A avaliação automatica sugere 8 clusters, mas existe bastante evidencia para escolhermos 3 clusters:
# * Maior queda de inércia entre k = 2 e k = 3
# * Melhor silhueta entre todos os k's

# ### Fazendo fit

kmeans_fit = KMeans(n_clusters=3)
kmeans_fit.fit(wine_scaler.transform(tb_wine))


# ### Extraindo clusters

tb_pca_wine["km_clu"] = kmeans_fit.labels_
# ou tb_wine['km_clu'] = kmeans_fit.predict(tb_sca_wine)


# ### Utilizando PCA para visualizar resultados

sns.pairplot(data=tb_pca_wine, hue="km_clu")


# ## Aglomerativo (Hierárquico)
# Enquanto `KMeans` é um algoritmo que opera de cima para baixo, os algoritmos hierárquicos operam de baixo para cima: a partir de uma função de distância (`affinity` ou `metric`), junto cada ponto ao seu vizinho mais próximo. Com esses clusters, a partir de uma função de `linkage`, junta iterativemente cada grupo ao grupo mais próximo até juntar todos os pontos em um único grupo.
#
# Essa estrutura hierárquica pode ser visualizada através do dendograma.
#
# **QUANDO UTILIZAR:**
# * **EDA:** quando queremos explorar a estrutura de proximidade entre nossos pontos sem reduzi-los às medidas de inércia e silhueta.
# * **MODELAGEM:** 
#     * Quando precisamos entender uma clusterização a partir de um número pré-definido de clusters (por exemplo, a área de vendas nos informa que temos 3 tipos de clientes - queremos investigar como essa impressão qualitativa dialoga com os dados). 
#     * Quando nossos grupos não tem uma separação clara.
#
# **Padrões de voos de urubus:** `cases/Padrões de Movimento - Urubu.ipynb`

# ### Construindo dendograma

from scipy.cluster.hierarchy import dendrogram, linkage


dendrogram_ = dendrogram(linkage(wine_scaler.transform(tb_wine), method="ward"))


# ### 'Cortando' dendograma

from sklearn.cluster import AgglomerativeClustering


hierarchical = AgglomerativeClustering(
    n_clusters=3, affinity="euclidean", linkage="ward"
)
hierarchical.fit(wine_scaler.transform(tb_wine))
tb_pca_wine["ward_clu"] = [str(x) for x in hierarchical.labels_]


# ### Visualizando usando PCA

sns.pairplot(data=tb_pca_wine, hue="ward_clu")


# ## DBSCAN
# O algoritmo DBSCAN agrupa pontos que se encontram em uma mesma região densa. Pontos fora de regiões densas são marcados como outliers. É uma técnica avançada que possibilita a determinação do número de clusters a partir da propria distribuição desses pontos em nossos features
#
# **QUANDO UTILIZAR:** 
# * Quando não temos nenhuma idéia sobre a quantidade de clusters. 
# * Quando queremos estimar muitos clusters (mais que 10 por exemplo). 
#     * Funciona muito bem para dados geográficos (onde os features são a latitude e a longitude) para encontrar aglomerações de pontos.

from sklearn.cluster import DBSCAN


# ### Encontrando hiperparâmetro `eps`

def find_dbscan_eps(data, clu_vars, neigh):
    from sklearn.neighbors import NearestNeighbors
    scale_cludata = StandardScaler().fit_transform(data[clu_vars])
    neighbors = neigh
    nbrs = NearestNeighbors(n_neighbors=neighbors)
    nbrs.fit(scale_cludata)
    distances, indices = nbrs.kneighbors(scale_cludata)
    distance_desc = sorted(distances[:, -1], reverse=True)
    kneedle = KneeLocator(
        range(1, len(distance_desc) + 1),
        distance_desc,
        S=5,
        curve="convex",
        direction="decreasing",
    )
    l_bound = int(np.where(np.array(distance_desc) == kneedle.knee_y)[0] * 0.1)
    u_bound = int(np.where(np.array(distance_desc) == kneedle.knee_y)[0] * 1.9)

    hist, bins = np.histogram(distances[:, -1], bins=20)
    logbins = np.logspace(np.log10(bins[0]), np.log10(bins[-1]), len(bins))
    fig, ax = plt.subplots(1, 2, figsize=(10, 5))
    ax[0].hist(distances[:, -1], bins=logbins)
    ax[0].set_xscale("log")
    ax[0].set_title("Distribuição de Distância")
    ax[0].grid(which="both", linestyle="--")
    ax[1].plot(distance_desc[l_bound : u_bound + 1])
    ax[1].axvline(
        np.where(np.array(distance_desc[l_bound : u_bound + 1]) == kneedle.knee_y)
    )
    ax[1].set_title("")

    kneedle.plot_knee_normalized(figsize=(10, 5))
    fig.suptitle(
        f"DBSCAN Eps Optimization\nKnee found at: {round(kneedle.knee_y, 4)} w/ {neigh}-NN",
        y=1.05,
    )
    return round(kneedle.knee_y, 4)


eps_opt = find_dbscan_eps(tb_wine, tb_wine.columns, 3)

# ### Criando clusters

db_fit = DBSCAN(eps = eps_opt, min_samples = 3)
db_fit.fit(wine_scaler.transform(tb_wine))

# ### Visualizando resultados com PCA

tb_pca_wine['db_clu'] = [str(x) for x in db_fit.labels_]
sns.pairplot(data=tb_pca_wine, hue="db_clu")

tb_wine['classif_wine'] = [str(x) for x in hierarchical.labels_]

# # Modelos Supervisionados

# ## Classificação
#
# **Aulas**
# * `aulas/64 DA PT NOV-2021 Aula 20220329 Regressão Logistica.ipynb`
# * `aulas/64 DA PT NOV-2021 Aula 20220331 Métodos de Classificação.ipynb`
#
# **Cases**
# * `cases/64 DA PT NOV-2021 Case Hotel.ipynb`

# ### Regressão Logística
# Modelo de classificação mais simples- não tem hiperparâmetros e nos ajuda a construir um modelo compreensível, que nos fornece um bom baseline de erro de classificação.
#
# **QUANDO UTILIZAR** 
# * Quando queremos um resultado interpretável. 
# * Quando estamos mais interessados na estimativa de probabilidade do evento do que na classificação em si.
# * Como baseline de erro para modelos mais complexos.

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import f1_score, confusion_matrix, precision_score, recall_score

# Escolhendo as variáveis do modelo:

X_var = ['Alcohol', 'Malic_Acid']
y_var = 'classif_wine'

# Inicializando e fitando a regressão

log_scaler = StandardScaler().fit(tb_wine[X_var])
log_fit = LogisticRegression()
log_fit.fit(log_scaler.transform(tb_wine[X_var]), tb_wine[y_var])

# Guardando as previsões na tabela original

tb_wine['pred_log'] = log_fit.predict(log_scaler.transform(tb_wine[X_var]))

# Visualizando a matriz de confusão

confusion_matrix(tb_wine['classif_wine'], tb_wine['pred_log'])

# Visualizando os scores de classificação

print(f"Precisão: {precision_score(tb_wine['classif_wine'], tb_wine['pred_log'], average = 'weighted')}")
print(f"Recall: {recall_score(tb_wine['classif_wine'], tb_wine['pred_log'], average = 'weighted')}")
print(f"F1-Score: {f1_score(tb_wine['classif_wine'], tb_wine['pred_log'], average = 'weighted')}")

# ### Árvores de Decisão
#
# As árvores de decisão são um dos métodos mais tradicionais de ML, no entanto hoje em dia raramente as utilizamos: árvores com bom poder preditivo não são interpretáveis, e as intepretáveis muitas vezes tem erro pior que uma regressão logística. No entanto, como elas compõe a base dos modelos de ensemble, é importante conhece-las.
#
# **QUANDO UTILIZAR** Em alguns problemas, uma árvore de decisão simples tem performance melhor que uma regressão logística e é de fácil interpretação.

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split, GridSearchCV

# Separando em test e train

X_train, X_test, y_train, y_test = train_test_split(tb_wine[X_var], tb_wine[y_var], test_size = 0.2, random_state = 42)

tree_scaler = StandardScaler()
tree_scaler.fit(X_train)
tree_fit = DecisionTreeClassifier()
tree_fit.fit(tree_scaler.transform(X_train), y_train)

test_pred = tree_fit.predict(tree_scaler.transform(X_test))
print(f"Precisão: {precision_score(y_test, test_pred, average = 'weighted')}")
print(f"Recall: {recall_score(y_test, test_pred, average = 'weighted')}")
print(f"F1-Score: {f1_score(y_test, test_pred, average = 'weighted')}")

param_grid = {
    'max_depth' : [int(x) for x in np.linspace(2, 50, 5)] + [None],
    'min_samples_split' : [int(x) for x in np.linspace(2, 40, 5)],
    'min_samples_leaf' : [int(x) for x in np.linspace(1, 20, 5)]
}
tree_fit = DecisionTreeClassifier()
clf_fit = GridSearchCV(tree_fit, param_grid)
clf_fit.fit(tree_scaler.transform(X_train), y_train)

clf_fit.best_params_

test_pred = clf_fit.predict(tree_scaler.transform(X_test))
print(f"Precisão: {precision_score(y_test, test_pred, average = 'weighted')}")
print(f"Recall: {recall_score(y_test, test_pred, average = 'weighted')}")
print(f"F1-Score: {f1_score(y_test, test_pred, average = 'weighted')}")

# ### Métodos de Ensemble
#
# Os métodos de ensemble utilizam diversas árvores de decisão fracas para construir um estimador robusto, com risco menor de overfitting.

# #### Bagging
#
# O modelo de bagging mais tradicional é a floresta aleatória: construímo `n_estimators` árvores de decisão com amostrar dos dados originais. Dessa forma cada árvore tem um risco menor de overfitting. No fim utilizamos a previsão de todas em um sistema de votação.
#
# **QUANDO UTILIZAR:** Junto com boosting é um dos métodos de melhor performance preditiva (em geral). Sempre que não nos interessa ter um modelo interpretável - onde o único requisito é a precisão da estimativa.

from sklearn.ensemble import RandomForestClassifier

rf_fit = RandomForestClassifier()
rf_fit.fit(tree_scaler.transform(X_train), y_train)

test_pred = rf_fit.predict(tree_scaler.transform(X_test))
print(f"Precisão: {precision_score(y_test, test_pred, average = 'weighted')}")
print(f"Recall: {recall_score(y_test, test_pred, average = 'weighted')}")
print(f"F1-Score: {f1_score(y_test, test_pred, average = 'weighted')}")

param_grid = {
    'max_depth' : [int(x) for x in np.linspace(1, 20, 5)] + [None],
    'n_estimators' : [int(x) for x in np.linspace(100, 2000, 10)]
}
rf_fit = RandomForestClassifier()
clf_fit = GridSearchCV(rf_fit, param_grid)
clf_fit.fit(tree_scaler.transform(X_train), y_train)

test_pred = clf_fit.predict(tree_scaler.transform(X_test))
print(f"Precisão: {precision_score(y_test, test_pred, average = 'weighted')}")
print(f"Recall: {recall_score(y_test, test_pred, average = 'weighted')}")
print(f"F1-Score: {f1_score(y_test, test_pred, average = 'weighted')}")

# #### Boosting
#
# Representam a tecnologia de ponta preditiva através dos algoritmos CATBOOST, XGBOOST e LightGBM (junto aos modelos de deep learning).
#
# **QUANDO UTILIZAR:** Melhores métodos para performance preditiva.

import catboost as cat

cat_fit = cat.CatBoostClassifier(iterations = 20000, depth = 3, auto_class_weights= "Balanced", od_type = "Iter", od_wait = 500)
cat_fit.fit(tree_scaler.transform(X_train), y_train, eval_set = (tree_scaler.transform(X_test), y_test))

test_pred = cat_fit.predict(tree_scaler.transform(X_test))
print(f"Precisão: {precision_score(y_test, test_pred, average = 'weighted')}")
print(f"Recall: {recall_score(y_test, test_pred, average = 'weighted')}")
print(f"F1-Score: {f1_score(y_test, test_pred, average = 'weighted')}")

# ## Regressão

X_var = ['Malic_Acid', 'Ash', 'Ash_Alcanity', 'Magnesium',
       'Total_Phenols', 'Flavanoids', 'Nonflavanoid_Phenols',
       'Proanthocyanins', 'Color_Intensity', 'Hue', 'OD280', 'Proline']
y_var = 'Alcohol'

X_train, X_test, y_train, y_test = train_test_split(tb_wine[X_var], tb_wine[y_var], test_size = 0.2, random_state = 42)

# ### Regressão Linear

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

reg_scaler = StandardScaler()
reg_scaler.fit(X_train)
lin_fit = LinearRegression()
lin_fit.fit(reg_scaler.transform(X_train), y_train)

pred_reg = lin_fit.predict(reg_scaler.transform(X_test))
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, pred_reg))}")

pd.DataFrame(lin_fit.coef_, index = X_var)

# ### Regressões Regularizadas

# #### LASSO

from sklearn.linear_model import LassoCV

reg_scaler = StandardScaler()
reg_scaler.fit(X_train)
las_fit = LassoCV(cv = 5)
las_fit.fit(reg_scaler.transform(X_train), y_train)

pred_reg = las_fit.predict(reg_scaler.transform(X_test))
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, pred_reg))}")

pd.DataFrame(las_fit.coef_, index = X_var)

# #### Ridge

from sklearn.linear_model import RidgeCV

# +
reg_scaler = StandardScaler()
reg_scaler.fit(X_train)

spline_t = SplineTransformer(degree = 2, n_knots = 2)
spline_t.fit(reg_scaler.transform(X_train))

ridge_fit = RidgeCV(cv = 5)
ridge_fit.fit(spline_t.transform(reg_scaler.transform(X_train)), y_train)
# -

pred_reg = ridge_fit.predict(spline_t.transform(reg_scaler.transform(X_test)))
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, pred_reg))}")

# ### Métodos de Ensemble

# #### Bagging

from sklearn.ensemble import RandomForestRegressor

# +
reg_scaler = StandardScaler()
reg_scaler.fit(X_train)

rf_fit = RandomForestRegressor()
rf_fit.fit(reg_scaler.transform(X_train), y_train)
# -

pred_reg = rf_fit.predict(reg_scaler.transform(X_test))
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, pred_reg))}")

param_grid = {
    'max_depth' : [int(x) for x in np.linspace(1, 20, 5)] + [None],
    'n_estimators' : [int(x) for x in np.linspace(100, 2000, 10)]
}
rf_fit = RandomForestRegressor()
clf_fit = GridSearchCV(rf_fit, param_grid)
clf_fit.fit(reg_scaler.transform(X_train), y_train)

pred_reg = clf_fit.predict(reg_scaler.transform(X_test))
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, pred_reg))}")

# #### Boosting

cat_fit = cat.CatBoostRegressor(iterations = 20000, depth = 8, od_type = "Iter", od_wait = 500)
cat_fit.fit(reg_scaler.transform(X_train), y_train, eval_set = (reg_scaler.transform(X_test), y_test))

pred_reg = cat_fit.predict(reg_scaler.transform(X_test))
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, pred_reg))}")



StarcoderdataJupyterScriptsDedupFiltered