198 lines
7.7 KiB
Python
198 lines
7.7 KiB
Python
# === 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.
|