Un point est suspect s'il est nettement moins entouré que ses voisins. LOF compare la densité autour de chaque ligne à celle autour de ses plus proches voisins. Il repère ainsi des anomalies locales, invisibles pour une règle globale : une dépense banale chez certains clients, mais inhabituelle dans son contexte.
En ville, une maison isolée au fond d'un champ se remarque. À la campagne, la même distance au voisin le plus proche est banale. LOF juge l'isolement par rapport au quartier.
Pour chaque transaction, on repère ses k plus proches voisines dans l'espace des variables standardisées.
La densité d'un point est l'inverse de sa distance moyenne d'accessibilité à ses voisins : plus ils sont proches, plus elle est forte.
Le score LOF est le rapport entre la densité moyenne des voisins et celle du point. Autour de 1, le point est aussi entouré que ses voisins. Nettement au-dessus, il est isolé.
Montant, heure, nombre de transactions du porteur sur 24 h, distance au domicile, nouveau marchand ou non. Montant et distance sont passés au logarithme, puis toutes les variables sont standardisées.
Chaque transaction reçoit un score LOF. L'équipe fraude contrôle les 50 plus élevés. La colonne fraude_confirmee ne sert qu'à évaluer le résultat après coup.
Le jeu contient 40 fraudes qui se ressemblent entre elles. Avec 20 voisins, LOF n'en trouve aucune dans son top 50 : chaque fraude a pour voisines d'autres fraudes, tout aussi isolées, et paraît normale. Avec 50 voisins, plus que le nombre de fraudes, les 40 sont dans le top 50.
Noms donnés pour R (dbscan) et Python (scikit-learn).
Taille du voisinage, le réglage décisif. Elle doit dépasser la taille des groupes d'anomalies que l'on veut détecter, sinon ils se masquent entre eux. Dans le package R dbscan, minPts compte le point lui-même : minPts = 21 correspond à n_neighbors = 20. Les auteurs de la méthode conseillent de calculer le score sur une plage de valeurs et de garder le maximum.
LOF repose sur des distances : on passe au logarithme les variables très asymétriques (montants, distances) et on standardise. Sans cela, une seule variable dicte le voisinage.
LOF donne un score, pas une décision. En Python, contamination fixe la part de lignes déclarées anormales par fit_predict. En pratique, on fixe la taille de la file selon la capacité de contrôle de l'équipe.
# Fraude à la carte : Local Outlier Factor en R
library(dbscan)
transactions <- read.csv("transactions.csv")
X <- transactions[, c("montant", "heure", "nb_transactions_24h", "distance_km", "nouveau_marchand")]
# Log sur les variables très asymétriques, puis même échelle pour toutes
X$montant <- log(X$montant)
X$distance_km <- log1p(X$distance_km)
X <- scale(X)
# La taille du voisinage change tout quand les fraudes se ressemblent entre elles
for (k in c(20, 50)) {
score <- lof(X, minPts = k) # autour de 1 = normal, bien au-dessus = suspect
top <- order(score, decreasing = TRUE)[1:50]
cat("minPts =", k, ": fraudes confirmées dans le top 50 =",
sum(transactions$fraude_confirmee[top]), "sur", sum(transactions$fraude_confirmee), "\n")
}
# Fraude à la carte : Local Outlier Factor en Python
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import LocalOutlierFactor
transactions = pd.read_csv("transactions.csv")
variables = ["montant", "heure", "nb_transactions_24h", "distance_km", "nouveau_marchand"]
X = transactions[variables].copy()
# Log sur les variables très asymétriques, puis même échelle pour toutes
X["montant"] = np.log(X["montant"])
X["distance_km"] = np.log1p(X["distance_km"])
X = StandardScaler().fit_transform(X)
# Le choix de n_neighbors change tout quand les fraudes se ressemblent entre elles
for k in [20, 50]:
lof = LocalOutlierFactor(n_neighbors=k)
lof.fit(X) # aucune étiquette : fraude_confirmee n'est pas utilisée
score = -lof.negative_outlier_factor_ # autour de 1 = normal, bien au-dessus = suspect
top = np.argsort(score)[::-1][:50]
print(f"n_neighbors={k} : fraudes confirmées dans le top 50 =",
transactions["fraude_confirmee"].iloc[top].sum(), "sur", transactions["fraude_confirmee"].sum())
Un score proche de 1 signifie que le point est aussi entouré que ses voisins. Nettement au-dessus, il est plus isolé. Il n'y a pas de seuil universel : le score dépend des données et du nombre de voisins, on raisonne donc en classement. scikit-learn le renvoie avec un signe moins (negative_outlier_factor_).
Une valeur qui n'est pas extrême dans l'absolu mais l'est dans son voisinage. Une dépense de 300 € est banale au milieu de clients qui dépensent beaucoup, inhabituelle au milieu de clients qui dépensent 20 €. Un seuil global de montant ne la voit pas ; LOF, oui.
Isolation Forest est plus rapide et plus simple à régler : c'est souvent le premier essai. LOF est meilleur quand les anomalies sont locales, dans des données aux densités très variables. Les deux sont non supervisés et se comparent sur un même échantillon contrôlé.
Isole les points par des coupes aléatoires. Plus rapide sur les grandes bases, pas de voisinage à régler.
Voir la fiche → même notion de densitéClustering par densité qui classe certains points comme du bruit. LOF en donne une version graduée, avec un score.
Voir la fiche → la brique de baseLOF repose entièrement sur la recherche des plus proches voisins et hérite de ses limites en grande dimension.
Voir la fiche →Dataistudio forme les équipes au machine learning et à l'IA, sur des cas concrets.
Nous utilisons des cookies de mesure d'audience et de suivi publicitaire pour comprendre la fréquentation du site et l'efficacité de nos annonces. Rien n'est déposé sans votre accord. En savoir plus