Similarité de Jaccard : la métrique de similarité la plus simple en IA
Une division entre l'intersection et l'union de deux ensembles. Implémentation TypeScript, fuzzy matching par n-grams, et les cas où elle ne suffit pas.
4 min de lecture

Tu veux comparer deux ensembles et savoir à quel point ils se ressemblent ? La similarité de Jaccard fait ça en une seule division.
La formule
Elle fait peur à première vue 😅:
J(A, B) = |A ∩ B| / |A ∪ B|
En français :
Nombre d'éléments en commun ÷ Nombre total d'éléments uniques
Le résultat est toujours entre 0 (rien en commun) et 1 (identiques). Plus on approche de 1, plus il y a de similitude.
Un exemple concret
Imaginons deux utilisateurs et les films qu'ils ont aimés (exemple très simple) :
const userA = new Set(["Inception", "Matrix", "Interstellar", "Dune"]);
const userB = new Set(["Matrix", "Dune", "Blade Runner", "Arrival"]);
// Intersection (en commun) : Matrix, Dune → 2 éléments
// Union (tous les films uniques) : 6 éléments
const jaccard = 2 / 6; // = 0.33
Ces deux utilisateurs ont 33% de similarité dans leurs goûts cinéma, c’est pas énorme.
Implémentation en TypeScript
function jaccardSimilarity<T>(setA: Set<T>, setB: Set<T>): number {
const intersection = new Set([...setA].filter(x => setB.has(x)));
const union = new Set([...setA, ...setB]);
if (union.size === 0) return 1; // deux ensembles vides = identiques
return intersection.size / union.size;
}
// Usage
const a = new Set(["chat", "chien", "oiseau"]);
const b = new Set(["chat", "poisson", "oiseau"]);
console.log(jaccardSimilarity(a, b)); // 0.5
Où c'est utilisé en IA ?
| Domaine | Usage |
|---|---|
| Recommandation | Trouver des utilisateurs avec des goûts similaires |
| NLP / Recherche | Comparer des documents via leurs mots-clés |
| Détection de duplicatas | Repérer des textes quasi-identiques |
| Fuzzy matching | Comparer des strings malgré les fautes de frappe |
| Clustering | Regrouper des éléments similaires |
Cas pratique : fuzzy matching avec les n-grams
La puissance de Jaccard vient pas de la formule, mais de comment tu construis tes ensembles avant de l'appliquer.
Exemple : comparer deux textes malgré une faute de frappe.
// Fonction pour découper un texte en bigrams (groupes de 2 caractères)
function toBigrams(str: string): Set<string> {
const bigrams = new Set<string>();
const normalized = str.toLowerCase();
for (let i = 0; i < normalized.length - 1; i++) {
bigrams.add(normalized.slice(i, i + 2));
}
return bigrams;
}
const textA = "bonjour";
const textB = "bojour"; // il manque le 'n'
const bigramsA = toBigrams(textA); // {"bo", "on", "nj", "jo", "ou", "ur"}
const bigramsB = toBigrams(textB); // {"bo", "oj", "jo", "ou", "ur"}
console.log(jaccardSimilarity(bigramsA, bigramsB)); // ≈ 0.57
Résultat : 57% de similarité malgré la faute. C'est comme ça que fonctionnent beaucoup d'outils de détection de plagiat ou de recherche approximative.
Les limites
Jaccard c'est simple et efficace, mais pas adapté à tout :
- Pas de notion de fréquence : un mot présent 1 fois ou 100 fois, c'est pareil
- Pas adapté aux vecteurs continus : pour les embeddings, on utilise plutôt la cosine similarity
- Sensible aux petits ensembles : 2 éléments en commun sur 3 (66%) vs sur 100 (2%).
Je suis actuellement en formation IA et je documente ma progression ici. Si t'as des questions ou des retours, n'hésite pas !
Sujets abordés
- IA
- machine learning
- TypeScript