# === Imports === # - La base - import pandas as pd import numpy as np # - Pour le plot import matplotlib.pyplot as plt # - Pour la séparation train/test stratifiée from sklearn.model_selection import StratifiedShuffleSplit # - Pour le OneHotEncoder (categories) - from sklearn.preprocessing import OneHotEncoder # - Pour le pipeline et le std-scaler - from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler # - Pour le ColumnTransformer (fullPipeline) - from sklearn.compose import ColumnTransformer # - Pour les modèles - from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsRegressor # - Pour la mesure d'erreur sur la prediction - from sklearn.metrics import mean_squared_error # =-=-=-=-=-=-= Récupération du dataset =-=-=-=-=-=-= payment = pd.read_csv('payment_fraud.csv',delimiter=',') # =-=-=-=-=-=-=-= Comprendre la structure du dataset =-=-=-=-=-=-= # - Affichage des cinq premiers exemples d'entrainement - print("\n - Payment Head - ") print(payment.head()) # - Informations sur le dataset - print("\n - Payment Info - ") payment.info() # -> Le dataset comporte 39 221 lignes # -> Il va falloir faire attention au feature 'paymentMethod' qui est un objet # -> Le reste des features sont soit des int soit des float # -> Mais pas de valeurs nulles # - A quoi resemble le feature 'paymentMethod' - print("\n - paymentMethod Sample - ") print(payment['paymentMethod'].sample(10)) # - Nombre d'occurences 'paymentMethod' - print("\n - paymentMethod ValueCounts - ") print(payment['paymentMethod'].value_counts()) # -> Visiblement nous sommes face à des catégorires # - Distribution Statistique - print("\n - Payment Describe - ") print(payment.describe()) # -> Ici je remarque 2 choses: # -> 1 - Les labels de fraudes effectives représentent seulement moins de 25% du dataset # -> 2 - Plusieurs valeurs sont très inégales aux quartiles (numItem max 29 pour +75% à 1), (paymentMethodAgeDays max 1999.58 pour +50% à 0 et 25% à 87.5) # -> Il faudrait vérifier s'il s'agit que d'une seule valeur à chaque fois qui perturbe le dataset ou si elles sont plusieurs à s'écarter du reste du dataset # - Analyse de la distribution (Histogramme) - payment.hist(bins=50, figsize=(20,15)) # - Uncomment the next line to display the graph - #plt.show() # -> Grace aux histogrammes, on peut voir qu'il n'y a pas qu'une valeur à 1999.58 mais bien une fine population # -- qui part de 0 pour aller s'écraser vers 2000. Avec tout de même un grand pique en 0 qui monte à plus de 25 000 de population # -> Concernant le feature 'numItem' c'est pareil, il y a quelques valeurs qui tentent de se rapprocher de 29 avec tout de même # -- une grande concentration autour de 1. # =-=-=-=-=-=-= Création de l'ensemble d'entrainement et de test =-=-=-=-=-=-= # -> Ici test_size représente la proportion du dataset consacrée au set d'entrainement. # -> Et random_state représente la graine de séparation "aléatoire". Cela nous permet d'avoir # -- toujours la même répartition si l'on utilise la même graine. # -> Nous allons séparer notre en dataset en Train_Set et Test_Set. Mais nous allons # -- directement procéder à une séparation stratifiée afin d'obtenir une proportion # -- équivalente de paiements frauduleux et légitimes. split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42) for train_index, test_index in split.split(payment, payment['label']): strat_train_set = payment.loc[train_index] strat_test_set = payment.loc[test_index] # =-=-=-=-=-=-= Analyse de la corrélation des données =-=-=-=-=-=-=-= corr_matrix = payment.corr() print("\n - Payment Correlation Matrix - ") print(corr_matrix["label"].sort_values(ascending=False)) # =-=-=-=-=-=-= Selection des features =-=-=-=-=-=-= # -> Nous sommes en présence d'un dataset qui ne compte que 5 features # -> On va garder tous les features car nous n'en avons pas beaucoup # =-=-=-=-=-=-= Extraction des Labels =-=-=-=-=-=-= payment = strat_train_set.drop("label", axis=1) labels = strat_train_set['label'].copy() # =-=-=-=-=-=-= Nettoyage du dataset =-=-=-=-=-=-= # - Verification de la présence potentielle de valeurs nulles - print("\n - Valeurs nulles ? - ") print(payment.isnull().sum()) # -> Toutes les valeurs sont nulles, on continue # - On ne va pas faire d'imputer car nous n'avons pas de valeurs manquantes - # =-=-=-=-=-=-= Attribut sous forme de catégorie =-=-=-=-=-=-= payment_cat = payment[['paymentMethod']] cat_encoder = OneHotEncoder() payment_cat_1hot = cat_encoder.fit_transform(payment_cat) # =-=-=-=-=-=-= Transformation en Pipelines =-=-=-=-=-=-= # - On ne va pas mettre d'imputer dans le pipeline car on ne s'en est pas servi - num_pipeline = Pipeline([ ('std_scaler', StandardScaler()), ]) payment_num = payment.drop("paymentMethod", axis=1) payment_num_tr = num_pipeline.fit_transform(payment_num) num_attribs = list(payment_num) cat_attribs = ["paymentMethod"] full_pipeline = ColumnTransformer([ ("num", num_pipeline, num_attribs), ("cat", OneHotEncoder(), cat_attribs), ]) payment_prepared = full_pipeline.fit_transform(payment) # =-=-=-=-=-=-= Entraînement et évaluation sur l'ensemble d'entraînement =-=-=-=-=-=-= # - Entrainement 1 (LogisticRegressor) - log_reg = LogisticRegression() log_reg.fit(payment_prepared, labels) # - Prediction - print("\n - Prediction on some data - ") some_data = payment.iloc[:5] some_labels = labels.iloc[:5] some_data_prepared = full_pipeline.transform(some_data) print("Predictions:", log_reg.predict(some_data_prepared)) print("Real values:", some_labels) print("\n - Mesure d'erreur sur la prediction (Train_Set) [LogisticRegressor] -") payment_predictions = log_reg.predict(payment_prepared) log_mse = mean_squared_error(labels, payment_predictions) log_rmse = np.sqrt(log_mse) print(log_rmse) # - Entrainement 2 (Knn -> k-nearest neighbors) - knn_reg = KNeighborsRegressor() knn_reg.fit(payment_prepared, labels) # - Prediction - print("\n - Prediction on some data - ") some_data = payment.iloc[:5] some_labels = labels.iloc[:5] some_data_prepared = full_pipeline.transform(some_data) print("Predictions:", knn_reg.predict(some_data_prepared)) print("Real values:", some_labels) print("\n - Mesure d'erreur sur la prediction (Train_set) [KnnRegressor] -") payment_predictions = knn_reg.predict(payment_prepared) knn_mse = mean_squared_error(labels, payment_predictions) knn_rmse = np.sqrt(knn_mse) print(knn_rmse) # -> On obtient de meilleurs résultats avec Knn, on va tout de même vérifier que l'on n'est pas face à de l'overfitting # =-=-=-=-=-=-= Evaluation du modèle sur le Test_Set =-=-=-=-=-=-= # Ensemble de test X_test = strat_test_set.drop("label", axis=1) # Les étiquettes y_test = strat_test_set["label"].copy() X_test_prepared = full_pipeline.transform(X_test) # - Mesure de performance sur le Test_Set - print("\n - Mesure d'erreur sur la prediction (Test_Set) [LogisticRegressor] -") payment_predictions = log_reg.predict(X_test_prepared) log_mse = mean_squared_error(y_test, payment_predictions) log_rmse = np.sqrt(log_mse) print(log_rmse) print("\n - Mesure d'erreur sur la prediction (Test_Set) [KnnRegressor] -") payment_predictions = knn_reg.predict(X_test_prepared) knn_mse = mean_squared_error(y_test, payment_predictions) knn_rmse = np.sqrt(knn_mse) print(knn_rmse) # -> Visiblement nous ne sommes pas face à de l'overfitting, en effet les scores sont proches de ceux # -- obtenus avec le Train_Set. Ils sont certes légèrement moins satisfaisants mais restent respectables. # -> Je confirme les meilleurs résultats en utilisant le modèle basé sur l'algorithme Knn.