<jupyter_start><jupyter_text>BITume - Experiments     1. import    2. merge    3. prev > dataframe    4. étude de la distribution des données    5. Normalisation    6. Sous-sampling et sur-sempling    7. fonctions d'exploitation des modèles        - param : modèle + normalisation        - indicateurs de classification        - validation croisée    8. lancement des fonctions sur les modèles + exploitation        - une cellule par modèle        - Dummy        - Gaussien            + données pas normalisées                * sous-samplées                * sur-samplées            + données normalisées                * sous-samplées                * sur-samplées        - Decision tree            + données pas normalisées                * sous-samplées                * sur-samplées            + données normalisées                * sous-samplées                * sur-samplées        - Linear Regression            + données pas normalisées                * sous-samplées                * sur-samplées            + données normalisées                * sous-samplées                * sur-samplées        - SVM            + données pas normalisées                * sous-samplées                * sur-samplées            + données normalisées                * sous-samplées                * sur-samplées        - ... ?<jupyter_code># Imports

# Scoring
from sklearn.pipeline import make_pipeline
from sklearn.metrics import precision_score, recall_score, f1_score, roc_auc_score, accuracy_score, classification_report
from sklearn.metrics import mean_absolute_error, mean_squared_error

# Pre-proc
from sklearn import preprocessing

# Data Balance
"""
`pip install imbalanced-learn`
Ref: https://towardsdatascience.com/how-to-balance-a-dataset-in-python-36dff9d12704
"""
from imblearn.over_sampling import SMOTE, ADASYN
from imblearn.under_sampling import NearMiss

# Plot
import matplotlib.pyplot as plt

# Utils
from collections import Counter
import numpy as np
import pandas as pd

# Classifiers
from sklearn.model_selection import train_test_split<jupyter_output><empty_output><jupyter_text>Merging des tablesIl s'agit d'une étape optionnelle.Il est important de lancer les cellues dans l'ordre car elles sont dépendantes entre elles  Lecture des tables et génération de la table de base result<jupyter_code># Merge
import datetime

tbl_df = pd.read_csv('../samples/data/data_mining_DB_clients_tbl.csv')
bis_df = pd.read_csv('../samples/data/data_mining_DB_clients_tbl_bis.csv')

result = pd.concat([tbl_df, bis_df], ignore_index=True, sort=False)

# Consolidation de ANNEE_DEM
result['ANNEE_DEM'] = np.where(result['DTDEM'] != '1900-12-31', result['DTDEM'].apply(lambda x: int(x.split('-')[0])), result['ANNEE_DEM'])

# Consolidation de AGEAD
result['AGEAD'] = np.where(result['AGEAD'].isnull() & result['DTNAIS'].notnull(), (result['DTADH'].apply(lambda x: int(x.split('-')[0]))) - (result['DTNAIS'].apply(lambda x: 0 if type(x) == float else int(x.split('-')[0]))), result['AGEAD'])

# Application de la médianne de AGEAD pour valeurs aberrantes
result['AGEAD'] = np.where(result['AGEAD'], result['AGEAD'].apply(lambda x: int(result['AGEAD'].median()) if x > 500 else x), result['AGEAD'])

# Nettoyage et regénération des ID
result = result.drop(['Id'], axis=1)
result.insert(0, 'Id', result.index + 1)

# Consolidation de adh
result['adh'] = np.where(
    result['adh'].isnull(),  # & result['DTDEM'] != '1900-12-31'
    (result['DTDEM'].apply(lambda x: int(x.split('-')[0]))) - (result['DTADH'].apply(lambda x: 0 if type(x) == float else int(x.split('-')[0]))),
    result['adh']
)

# Factualisation des valeurs, considérant que adh est 2007 pour les non-démissionnaires
# 2007 - 1900 = 107
result['adh'] = np.where(result['adh'], result['adh'].apply(lambda x: 107 + x if x < 0 else x), result['adh'])

# Consolidation de agedem
result['agedem'] = np.where(result['agedem'].isnull() & result['DTDEM'].notnull() & result['DTNAIS'].notnull(),
                            (result['DTDEM'].apply(lambda x: int(x.split('-')[0]))) - (result['DTNAIS'].apply(lambda x: 0 if type(x) == float else int(x.split('-')[0]))),
                            result['agedem'])

result['agedem'] = np.where(result['agedem'], result['agedem'].apply(lambda x: 0 if x < 0 else x), result['agedem'])
result['agedem'] = np.where(result['agedem'], result['agedem'].apply(lambda x: 0 if x == 1900 else x), result['agedem'])

# Nettoyage de la structure
result = result.drop(['rangdem'], axis=1)
result = result.drop(['rangadh'], axis=1)
result = result.drop(['rangagead'], axis=1)
result = result.drop(['rangagedem'], axis=1)

# Sauvegarde en CSV
result.to_csv('result.csv')<jupyter_output><empty_output><jupyter_text>Création du fichier de prédiction<jupyter_code># Utilisation de la table de résultats nettoyée
prev = result

# Création de l'indexe de prévision (Y)
date_1901 = datetime.datetime(year=1901, month=1, day=1)
prev['dem'] = ((~ prev['CDDEM'].isnull()) | (prev['DTDEM'] != '1900-12-31') | (~ prev['ANNEE_DEM'].isnull())).astype(int)

# Déplacement de la colonne de prédiction en première position
prev.insert(0, 'dem', prev.pop("dem"))

# Suppression des tables inutiles, comme expliqué dans le rapport
prev = prev.drop(['Id'], axis=1)
prev = prev.drop(['DTADH'], axis=1)

prev = prev.drop(['CDDEM'], axis=1)
prev = prev.drop(['DTDEM'], axis=1)
prev = prev.drop(['ANNEE_DEM'], axis=1)
prev = prev.drop(['CDMOTDEM'], axis=1)
prev = prev.drop(['agedem'], axis=1)
prev = prev.drop(['DTNAIS'], axis=1)
# Cette col est Incomplete
prev = prev.drop(['Bpadh'], axis=1)

# Cette col n'est pas utile en l'état
prev = prev.drop(['adh'], axis=1)

# Correction des types
prev['AGEAD'] = prev['AGEAD'].astype(int)

prev.to_csv('previsions_nd.csv', index=False)

# disjonction
dummies = ['CDTMT', 'CDSITFAM', 'CDTMT', 'CDCATCL']
prev = pd.get_dummies(prev, prefix=dummies, columns=dummies)

# Sauvegarde en CSV pour exploitation
prev.to_csv('previsions.csv', index=False)<jupyter_output><empty_output><jupyter_text>Exportation en base de donnéesL'exportation en base SQL permet de réaliser des traitement génériques SQL sur les données<jupyter_code>from sqlalchemy import create_engine

engine = create_engine('sqlite:///clients_tbl.db', echo=False)

tbl_df.to_sql('clients', con=engine, if_exists='replace')
bis_df.to_sql('bis', con=engine, if_exists='replace')
result.to_sql('result', con=engine, if_exists='replace')
prev.to_sql('prev', con=engine, if_exists='replace')<jupyter_output><empty_output><jupyter_text>Importation des donnéesCette étape a pour but de restorer les données en l'état depuis le fichier CSV généré<jupyter_code># Importer dans une dataframe pandas le CSV
prev_df = pd.read_csv('previsions.csv', delimiter=',')
# Afficher son contenu pour s'assurer qu'il est correctement lu
prev_df.head()

# Conversion de la dataframe en numpy pour calculuer la moyenne et les X / Y
users_array = prev_df.to_numpy()
# Calcul de la "drop rate" moyenne, le taut de démission
average_drop_rate = (np.mean([i[0] for i in users_array]) * 100)

# Séparation des variables (X) de la cible de prévision (Y)
X = users_array[:, 1:]
Y = users_array[:, 0]<jupyter_output><empty_output><jupyter_text>Étude de la distribution des donnéesCette étape permet de constater s'il existe des disparités dans la distribution des résultats.Une source d'inspiration a été [l'article suivant](https://machinelearningmastery.com/undersampling-algorithms-for-imbalanced-classification/)<jupyter_code># Mise en place d'un compteur de distribution
counter = Counter(Y)

# Décompte des valeurs de la cible
print(f"Distribution normale : {counter}")

from sklearn.utils import compute_class_weight
# Calcul du poids des classes
class_weight = compute_class_weight('balanced', np.unique(Y), Y)

print(class_weight)<jupyter_output>Distribution normale : Counter({1: 30880, 0: 14474})
[1.56674036 0.73435881]<jupyter_text>Normalisation<jupyter_code>min_max = preprocessing.MinMaxScaler()
X_minmax = min_max.fit_transform(X)

std_scale = preprocessing.StandardScaler()
X_scale = std_scale.fit_transform(X)

X_l1 = preprocessing.normalize(X, norm="l1")
X_l2 = preprocessing.normalize(X, norm="l2")<jupyter_output><empty_output><jupyter_text>Sous-sampling et sur-sempling NearMissLe principe du NearMiss est de réaliser un sous-échantillonage. La référence se trouve à [l'adresse suivante](https://imbalanced-learn.org/stable/under_sampling.html)Note: l'execution prends trop de temps pour mon PC<jupyter_code>from imblearn.under_sampling import NearMiss

# Création d'un resampler NearMiss 1
nm1 = NearMiss(version=1)
# Équilibrage des échantillions
X_nearmiss, Y_nearmiss = nm1.fit_resample(X, Y)

X_nearmiss_scale, Y_nearmiss_scale = nm1.fit_resample(X_scale, Y)
X_nearmiss_l1, Y_nearmiss_l1 = nm1.fit_resample(X_l1, Y)
X_nearmiss_l2, Y_nearmiss_l2 = nm1.fit_resample(X_l2, Y)

# Mise en place d'un compteur de distribution
counter = Counter(Y_nearmiss)

# Décompte des valeurs de la cible
print(f"Sous-échantillonnage / distribution NearMiss : {counter}")<jupyter_output><empty_output><jupyter_text>SMOTELe principe de la Technique de sur-échantillonnage des Minorités Synthétiques (SMOTE) est de réaliser un sur-échantillonage.La référence se trouve à [l'adresse suivante](https://imbalanced-learn.org/stable/over_sampling.html)<jupyter_code>from imblearn.over_sampling import SMOTE

sm = SMOTE()
# Création d'un resampler et équilibrage des échantillions
X_smote, Y_smote = sm.fit_resample(X, Y)

X_smote_scale, Y_smote_scale = sm.fit_resample(X_scale, Y)
X_smote_l1, Y_smote_l1 = sm.fit_resample(X_l1, Y)
X_smote_l2, Y_smote_l2 = sm.fit_resample(X_l2, Y)

# Mise en place d'un compteur de distribution
counter = Counter(Y_smote)

# Décompte des valeurs de la cible
print(f"Sur-échantillonnage / distribution SMOTE : {counter}")<jupyter_output><empty_output><jupyter_text>ADASYNLe principe de l'Adaptation Synthétique (ADASYN) est de réaliser un sur-échantillonage.La référence se trouve à [l'adresse suivante](https://imbalanced-learn.org/stable/over_sampling.html)<jupyter_code>from imblearn.over_sampling import ADASYN

asn = ADASYN()

X_as, Y_as = asn.fit_resample(X, Y)

X_as_scale, Y_as_scale = asn.fit_resample(X_scale, Y)
X_as_l1, Y_as_l1 = asn.fit_resample(X_l1, Y)
X_as_l2, Y_as_l2 = asn.fit_resample(X_l2, Y)

# Mise en place d'un compteur de distribution
counter = Counter(Y_as)

# Décompte des valeurs de la cible
print(f"Sur-échantillonnage / distribution ADASYN : {counter}")<jupyter_output><empty_output><jupyter_text>Fonctions d'exploitation des modèles<jupyter_code>from sklearn.model_selection import cross_val_score

def calc_accuracy_score(model, name: str, p_X=X, p_Y=Y):
    scores = cross_val_score(model, p_X, p_Y, cv=10)
    scores_precision = cross_val_score(model, p_X, p_Y, cv=10, scoring='precision')
    scores_recall = cross_val_score(model, p_X, p_Y, cv=10, scoring='recall')
    scores_f1 = cross_val_score(model, p_X, p_Y, cv=10, scoring='f1')
    print(f"""
Évaluation de {name} sous Validation Croisée 10-étapes

Exactitude   : {scores.mean():.3f} (+/- {scores.std() * 2:.3f})
Précision    : {scores_precision.mean():.3f} (+/- {scores_precision.std() * 2:.3f})
Ratio Rappel : {scores_recall.mean():.3f} (+/- {scores_recall.std() * 2:.3f})
F-score      : {scores_f1.mean():.3f} (+/- {scores_f1.std() * 2:.3f})
    """)

def calc_model_score(pipeline, p_x_test, p_y_test, name: str):
    print(f'Précision de {name} {pipeline.score(p_x_test, p_y_test)}')

def calc_MAE_MSE_EMSE_from(prediction, p_y_test, name):
    print(f"MAE sur {name} {mean_absolute_error(p_y_test, prediction)}")
    print(f"MSE sur {name} {mean_squared_error(p_y_test, prediction)}")
    print(f"RMSE sur {name} {np.sqrt(mean_squared_error(p_y_test, prediction))}")
    print("")

def get_model_data(model_instance, X_dat=X, Y_dat=Y):
    X_train, X_test, y_train, y_test = train_test_split(X_dat, Y_dat, test_size=0.2, random_state=42)
    pipeline = make_pipeline(model_instance)
    model = pipeline.fit(X_train, y_train)
    prediction = model.predict(X_test)
    return X_test, y_test, model, prediction, pipeline

def report_model(p_model, name:str, X_dat=X, Y_dat=Y):
    X_test, y_test, model, prediction, pipeline = get_model_data(p_model, X_dat, Y_dat)
    calc_model_score(pipeline, X_test, y_test, name)
    calc_accuracy_score(model, name)
    calc_MAE_MSE_EMSE_from(prediction, y_test, name)

def report_all_model(p_model, name:str):
    report_model(p_model, name)
    print("Et diminuées\n=============\n")
    report_model(p_model, name+" NearMiss", X_dat=X_nearmiss, Y_dat=Y_nearmiss)
    print("\n\nEt augmentées\n=============\n")
    report_model(p_model, name+" SMOTE", X_dat=X_smote, Y_dat=Y_smote)
    report_model(p_model, name+" ADASYN", X_dat=X_as, Y_dat=Y_as)

    print("\n\nAvec des données normalisées")
    print("Et non-resamplées\n=============\n")

    report_model(p_model, name+" avec Répartitions Normales", X_dat=X_scale, Y_dat=Y)
    report_model(p_model, name+" avec Normalisation L1", X_dat=X_l1, Y_dat=Y)
    report_model(p_model, name+" avec Normalisation L2", X_dat=X_l2, Y_dat=Y)
    print("\n\nEt diminuées\n=============\n")
    report_model(p_model, name+" NearMiss avec Répartitions Normales", X_dat=X_nearmiss_scale, Y_dat=Y_nearmiss_scale)
    report_model(p_model, name+" NearMiss avec Normalisation L1", X_dat=X_nearmiss_l1, Y_dat=Y_nearmiss_l1)
    report_model(p_model, name+" NearMiss avec Normalisation L2", X_dat=X_nearmiss_l2, Y_dat=Y_nearmiss_l2)
    print("\n\nEt augmentées\n=============\n")
    report_model(p_model, name+" SMOTE avec Répartitions Normales", X_dat=X_smote_scale, Y_dat=Y_smote_scale)
    report_model(p_model, name+" SMOTE avec Normalisation L1", X_dat=X_smote_l1, Y_dat=Y_smote_l1)
    report_model(p_model, name+" SMOTE avec Normalisation L2", X_dat=X_smote_l2, Y_dat=Y_smote_l2)

    report_model(p_model, name+" ADASYN avec Répartitions Normales", X_dat=X_as_scale, Y_dat=Y_as_scale)
    report_model(p_model, name+" ADASYN avec Normalisation L1", X_dat=X_as_l1, Y_dat=Y_as_l1)
    report_model(p_model, name+" ADASYN avec Normalisation L2", X_dat=X_as_l2, Y_dat=Y_as_l2)<jupyter_output><empty_output><jupyter_text>Étude des modèles  le Dummy ClassifierLe dummy classfier est un classifier naif qui estime à coup de fréquence la valeur<jupyter_code>from sklearn.dummy import DummyClassifier

# Mise en place d'un DummyClassifier permettant d'évaluer les performances
# des autres algoritmes
dummycl_model = DummyClassifier(strategy="most_frequent")

name = "le Dummy Classifier"
report_model(dummycl_model, name)<jupyter_output><empty_output><jupyter_text>le GaussienLe Gaussien est un modèle probabiliste sur une distribution normale, qui suit la formule`P(class|data) = ( P(data|class) x P(class) ) / P(data)`<jupyter_code>from sklearn.naive_bayes import GaussianNB

# Mise en place d'une classification naïve bayésienne
gmb_model = GaussianNB()

name = "<NAME>"<jupyter_output><empty_output><jupyter_text>Avec des données non normalisées Et non-resamplées<jupyter_code>report_model(gmb_model, name)<jupyter_output><empty_output><jupyter_text>Et diminuées<jupyter_code>report_model(gmb_model, name+" NearMiss", X_dat=X_nearmiss, Y_dat=Y_nearmiss)<jupyter_output><empty_output><jupyter_text>Et augmentées<jupyter_code>report_model(gmb_model, name+" SMOTE", X_dat=X_smote, Y_dat=Y_smote)
report_model(gmb_model, name+" ADASYN", X_dat=X_as, Y_dat=Y_as)<jupyter_output><empty_output><jupyter_text>Avec des données normalisées Et non-resamplées<jupyter_code>report_model(gmb_model, name+" avec Répartitions Normales", X_dat=X_scale, Y_dat=Y)
report_model(gmb_model, name+" avec Normalisation L1", X_dat=X_l1, Y_dat=Y)
report_model(gmb_model, name+" avec Normalisation L2", X_dat=X_l2, Y_dat=Y)<jupyter_output><empty_output><jupyter_text>Et diminuées<jupyter_code>report_model(gmb_model, name+" NearMiss avec Répartitions Normales", X_dat=X_nearmiss_scale, Y_dat=Y_nearmiss_scale)
report_model(gmb_model, name+" NearMiss avec Normalisation L1", X_dat=X_nearmiss_l1, Y_dat=Y_nearmiss_l1)
report_model(gmb_model, name+" NearMiss avec Normalisation L2", X_dat=X_nearmiss_l2, Y_dat=Y_nearmiss_l2)<jupyter_output><empty_output><jupyter_text>Et augmentées<jupyter_code>report_model(gmb_model, name+" SMOTE avec Répartitions Normales", X_dat=X_smote_scale, Y_dat=Y_smote_scale)
report_model(gmb_model, name+" SMOTE avec Normalisation L1", X_dat=X_smote_l1, Y_dat=Y_smote_l1)
report_model(gmb_model, name+" SMOTE avec Normalisation L2", X_dat=X_smote_l2, Y_dat=Y_smote_l2)

report_model(gmb_model, name+" ADASYN avec Répartitions Normales", X_dat=X_as_scale, Y_dat=Y_as_scale)
report_model(gmb_model, name+" ADASYN avec Normalisation L1", X_dat=X_as_l1, Y_dat=Y_as_l1)
report_model(gmb_model, name+" ADASYN avec Normalisation L2", X_dat=X_as_l2, Y_dat=Y_as_l2)
# =====================================================<jupyter_output><empty_output><jupyter_text>l'Arbre de décisionL'Arbre de décision est un modèle de décision probabiliste basé sur un apprentissage etconsidérant une faible variation entre les données d'apprentissage et de test.Il se révèle particulièrement performant face à des cas déjà connus.<jupyter_code>from sklearn import tree

# Mise en place d'un arbre de décision
dectree_model = tree.DecisionTreeClassifier()

name = "l'Arbre de décision"

report_all_model(dectree_model, name)<jupyter_output><empty_output><jupyter_text>la Régression LinéaireLa Régression Linéaire est une manière de prédire les données en les séparant en plusieurs classesElle est connue pour rapidement donner des résultats cohérents<jupyter_code>from sklearn.linear_model import LogisticRegression

# Mise en place d'un modèle de régression linéaire, connu pour
# avoir un important rapport performance / temps d'execution
lr_model = LogisticRegression()

name = "la Régression Linéaire"
report_all_model(lr_model, name)<jupyter_output><empty_output><jupyter_text>la Classification Supporté par les VecteursLa Classification Supporté par les Vecteurs est très intense pour mon toaster Avec LinearSVC<jupyter_code>from sklearn.svm import SVC
from sklearn.svm import LinearSVC

# Le SVC avec un meilleur suppot sur les rgands dataset
lsvc_model = LinearSVC()

name = "la Classification Linéaire Supporté par les Vecteurs"

report_all_model(lsvc_model, name)<jupyter_output><empty_output><jupyter_text>Avec SVC (libsvm)<jupyter_code># Mise en place d'un Support Vecteur Machine, présentant
# en général de meilleurs performances
svm_model = SVC()

name = "la Classification Supporté par les Vecteurs"<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12331053793549082, 'fraction_numerical': 0.013300306930159926, 'mean_word_length': 5.654245790039412, 'pattern_counts': {'":': 0, '<': 95, '<?xml version=': 0, '>': 95, 'https://': 5, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '23', 'dir': 'jupyter-structured-clean-dedup', 'id': '440239', 'license': 'MIT', 'n_tokens_mistral': 7026, 'n_tokens_neox': 6834, 'n_words': 1510, 'path': 'bitume/main.ipynb', 'repo_name': 'TheRainbowPhoenix/-BITume', 'size': '18588'}