BRAGA

02 Writing02-02 Notes02-02-171

Jaccard Similarity (IoU)

Published 16 Apr 20267 min read
Contents
  1. 01Deep Breakdown
  2. 02Flowchart
  3. 03Real-world use
  4. 04The formula, piece by piece
  5. 05The formula in words
  6. 06Critical Concepts
  7. 07Step-by-step calculation (tiny example)
  8. 08Code examples
J(A,B)=∣A∩B∣∣A∪B∣J(A,B) = \frac{|A \cap B|}{|A \cup B|}
(1)

Deep Breakdown

What it tells me (Situation Awareness): Jaccard similarity (also called Intersection over Union, IoU) measures the overlap between two sets: the size of their intersection divided by the size of their union. Range: [0,1][0, 1]. J=1J = 1 means the sets are identical; J=0J = 0 means they share nothing. In DA/DS: Jaccard is the standard similarity metric for sets and binary data. Object detection (IoU of predicted vs. ground-truth bounding boxes), recommendation systems (user purchase set overlap), NLP (n-gram or word set similarity), duplicate detection (MinHash/LSH approximation of Jaccard), and A/B test audience overlap analysis are all core use cases.

Flowchart

Sets A and B (orbinary vectors a,b)Countintersection: |A∩ B| = elementsin BOTHCount union: |A∪ B| = elementsin EITHER (noduplicates)Jaccard: J = |A∩ B| / |A ∪ B|J = 1: A = B(identical sets)J = 0: A ∩ B =empty (nooverlap)Jaccarddistance: d_J =1 - J(A,B)For binaryvectors: J =dot(a,b) / (||a||^2+ ||b||^2 -dot(a,b))

Real-world use

Where it is used

Object detection evaluation (IoU of bounding boxes vs. threshold like 0.5 or 0.75), semantic segmentation (per-class IoU), user-item basket overlap in recommendation, duplicate/near-duplicate document detection via MinHash, keyword set similarity in NLP, and audience overlap in advertising (shared users between two campaign exposures).

Problem it solves

Set-based similarity needs a scale-invariant measure that accounts for both shared elements and the "missed" elements on either side. A simple intersection count ∣A∩B∣|A \cap B| is not normalized: two large sets with a 50-element intersection look very similar, even if one has 1000 elements (missing 950). Jaccard normalizes by the union.

How it solves the problem

∣A∪B∣=∣A∣+∣B∣−∣A∩B∣|A \cup B| = |A| + |B| - |A \cap B|. Dividing the intersection by the union normalizes for set size: J=∣A∩B∣∣A∣+∣B∣−∣A∩B∣J = \frac{|A \cap B|}{|A| + |B| - |A \cap B|}. For binary vectors a,b∈{0,1}na, b \in \{0,1\}^n: ∣A∩B∣=aTb|A \cap B| = a^T b (count of positions where both are 1), ∣A∪B∣=∥a∥2+∥b∥2−aTb|A \cup B| = \|a\|^2 + \|b\|^2 - a^Tb (count of positions where at least one is 1).

Example in a business

A DS evaluates an object detection model. For each predicted bounding box and ground-truth box, they compute IoU=J=area of overlaparea of union\text{IoU} = J = \frac{\text{area of overlap}}{\text{area of union}}. A prediction is considered correct at the COCO benchmark if IoU ≥0.5\geq 0.5. The mean IoU (mIoU) across all classes is the primary evaluation metric for the model release decision.

The formula, piece by piece

  • A,BA, B: two sets (or binary vectors viewed as indicator functions of sets)
  • ∣A∩B∣|A \cap B|: number of elements in both AA and BB (intersection size)
  • ∣A∪B∣|A \cup B|: number of elements in AA or BB or both (union size)
  • J(A,B)∈[0,1]J(A,B) \in [0, 1]: 0 = no overlap, 1 = identical sets
  • Equivalent for binary vectors: J=sum(a∧b)sum(a∨b)=aTb∥a∥1+∥b∥1−aTbJ = \frac{\text{sum}(a \wedge b)}{\text{sum}(a \vee b)} = \frac{a^Tb}{\|a\|_1 + \|b\|_1 - a^Tb}
  • Jaccard distance: dJ(A,B)=1−J(A,B)d_J(A,B) = 1 - J(A,B) — IS a proper metric (satisfies triangle inequality)
  • For continuous (non-binary) data, use generalized Jaccard: J(x,y)=∑min⁡(xi,yi)∑max⁡(xi,yi)J(x,y) = \frac{\sum \min(x_i, y_i)}{\sum \max(x_i, y_i)}

The formula in words

"Count the elements shared by both sets. Count all elements in either set. Divide: how big is the overlap relative to everything involved? 1 = perfect overlap, 0 = no shared elements." Tiny example: A={1,2,3,4}A = \{1, 2, 3, 4\}, B={3,4,5,6}B = \{3, 4, 5, 6\}. ∣A∩B∣=∣{3,4}∣=2|A \cap B| = |\{3,4\}| = 2. ∣A∪B∣=∣{1,2,3,4,5,6}∣=6|A \cup B| = |\{1,2,3,4,5,6\}| = 6. J=2/6≈0.333J = 2/6 \approx 0.333. ✓\checkmark

Critical Concepts

  • Jaccard distance is a proper metric: dJ=1−Jd_J = 1 - J satisfies all four metric axioms including triangle inequality (unlike cosine distance). This makes it safe for metric-based algorithms.
  • MinHash approximation: for large-scale set similarity (e.g., comparing millions of documents), computing exact Jaccard is expensive. MinHash provides an unbiased estimator: Pr⁡[minhash(A)=minhash(B)]=J(A,B)\Pr[\text{minhash}(A) = \text{minhash}(B)] = J(A,B).
  • IoU threshold in object detection: COCO mAP is computed at IoU thresholds {0.5,0.55,...,0.95}\{0.5, 0.55, ..., 0.95\}. A higher threshold means stricter localization requirements.
  • Asymmetry of set sizes: if ∣A∣≪∣B∣|A| \ll |B| (e.g., a tiny predicted region vs. large ground truth), IoU is low even if the prediction is fully inside the ground truth. This is a known limitation for small object detection.
  • Zero-zero problem: if both ai=0a_i = 0 and bi=0b_i = 0 for feature ii, this contributes nothing to either numerator or denominator. Jaccard ignores shared absences — this is desirable for sparse data (zero means "no occurrence", not "similar").
  • **vs.

Dice coefficient:** Dice =2∣A∩B∣/(∣A∣+∣B∣)= 2|A \cap B|/(|A|+|B|) weights the intersection more heavily than Jaccard. Dice =2J/(1+J)= 2J/(1+J).

Step-by-step calculation (tiny example)

Step 1: Define the sets and compute intersection User A purchased: {1,3,5,7,9}\{1, 3, 5, 7, 9\}. User B purchased: {3,5,6,9,10}\{3, 5, 6, 9, 10\}. A∩B={3,5,9}A \cap B = \{3, 5, 9\}. ∣A∩B∣=3|A \cap B| = 3.

Step 2: Compute union A∪B={1,3,5,6,7,9,10}A \cup B = \{1, 3, 5, 6, 7, 9, 10\}. ∣A∪B∣=7|A \cup B| = 7. (Alternatively: ∣A∣+∣B∣−∣A∩B∣=5+5−3=7|A| + |B| - |A \cap B| = 5 + 5 - 3 = 7.)

Step 3: Compute Jaccard similarity and distance J=3/7≈0.429J = 3/7 \approx 0.429. dJ=1−0.429=0.571d_J = 1 - 0.429 = 0.571.

Code examples

Beginner example (Python)

python
def jaccard_similarity(set_a: set, set_b: set) -> float:
    """Jaccard similarity between two sets."""
    intersection = len(set_a & set_b)
    union = len(set_a | set_b)
    return intersection / union if union > 0 else 0.0

def jaccard_binary(a, b) -> float:
    """Jaccard similarity for binary (0/1) vectors."""
    import numpy as np
    a, b = np.asarray(a, dtype=bool), np.asarray(b, dtype=bool)
    intersection = int(np.sum(a & b))
    union = int(np.sum(a | b))
    return intersection / union if union > 0 else 0.0   # Set example
A = {1, 3, 5, 7, 9}
B = {3, 5, 6, 9, 10}
print(f"J(A, B) = {jaccard_similarity(A, B):.4f}")
print(f"d_J = {1 - jaccard_similarity(A, B):.4f}")   # Binary vector example
import numpy as np
a = np.array([1, 1, 0, 1, 0, 1, 0, 0, 1, 0])
b = np.array([0, 1, 0, 1, 1, 0, 0, 1, 1, 0])
print(f"\nBinary J = {jaccard_binary(a, b):.4f}")   # Verify with scipy
from scipy.spatial.distance import jaccard
print(f"scipy jaccard distance: {jaccard(a, b):.4f} (= 1 - J)")

Beginner example (R)

r
# -- Jaccard similarity ---
jaccard_sim <- function(a, b) {  # Works for numeric (binary) vectors
  intersection <- sum(a & b)
  union <- sum(a | b)
  if (union == 0)
  return(0) intersection / union
}
A <- c(1, 3, 5, 7, 9)
B <- c(3, 5, 6, 9, 10)
intersection <- length(intersect(A, B))
union_size <- length(union(A, B))
j_ab <- intersection / union_size
cat(sprintf("|A ∩ B| = %d, |A ∪ B| = %d\n", intersection, union_size))
cat(sprintf("J(A, B) = %.4f\n", j_ab))
cat(sprintf("d_J = %.4f\n", 1 - j_ab))  # Binary vector
a <- c(1, 1, 0, 1, 0, 1, 0, 0, 1, 0)
b <- c(0, 1, 0, 1, 1, 0, 0, 1, 1, 0)
cat(sprintf("\nBinary J = %.4f\n", jaccard_sim(a, b)))

Professional example (Python)

python
import numpy as np
import pandas as pd
np.random.seed(42)   # -- Business scenario: IoU evaluation for object detection bounding boxes ---

def bbox_iou(box1: np.ndarray, box2: np.ndarray) -> float:
    """ Compute IoU (Jaccard) between two bounding boxes. box format: [x_min, y_min, x_max, y_max] """
    x_inter1 = max(box1[0], box2[0])
    y_inter1 = max(box1[1], box2[1])
    x_inter2 = min(box1[2], box2[2])
    y_inter2 = min(box1[3], box2[3])
    inter_area = max(0, x_inter2 - x_inter1) * max(0, y_inter2 - y_inter1)
    area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
    area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
    union_area = area1 + area2 - inter_area
    return inter_area / union_area if union_area > 0 else 0.0   # Simulated ground-truth and predicted bounding boxes
N_BOXES = 12
gt_boxes = np.column_stack([ np.random.randint(0, 400, N_BOXES), np.random.randint(0, 300, N_BOXES),
]).astype(float)
gt_boxes = np.column_stack([gt_boxes, gt_boxes + np.random.randint(50, 150, (N_BOXES, 2))])   # Predictions: add small random offsets to ground truth
noise = np.random.randint(-20, 20, gt_boxes.shape).astype(float)
pred_boxes = np.clip(gt_boxes + noise, 0, None)
IOU_THRESHOLD_50 = 0.50
IOU_THRESHOLD_75 = 0.75
results = []
for i in range(N_BOXES):
    iou = bbox_iou(gt_boxes[i], pred_boxes[i])
results.append({ "box_id": i + 1, "iou": round(iou, 4), "tp_at_50": iou >= IOU_THRESHOLD_50, "tp_at_75": iou >= IOU_THRESHOLD_75, })
df = pd.DataFrame(results)
print("Object detection IoU evaluation:")
print(df.to_string(index=False))
print(f"\nmAP@50 (fraction correct): {df['tp_at_50'].mean():.4f}")
print(f"mAP@75 (fraction correct): {df['tp_at_75'].mean():.4f}")

Source: domain knowledge

#linear-algebra#spatial-distance-norms-and-analytic-geometry#advanced-distance-and-similarity-metrics

Keyboard shortcuts

Single keys, ignored while typing in a field or with Cmd, Ctrl or Alt held.

0
Home
1 2 3 4
Work, Writing, Tools, About
j / k
Next / previous section on this page
n / p
Next / previous project or essay
u
Up one level
l
Deutsch
t
Light or dark theme
?
This list
Esc
Close