--- title: "Deduplication Module" description: "Entity deduplication v1/v2 — similarity scoring, blocking, merging, and cluster-based batch processing." icon: "copy" --- > Identify and merge duplicate entities across sources for a clean, single-source-of-truth knowledge graph. --- ## Overview The **Deduplication Module** detects and merges duplicate entities using similarity scoring, blocking strategies, and configurable merge policies. **v2 strategies** (`blocking_v2`, `hybrid_v2`, `semantic_v2`) are up to **7x faster** than v1 and support fine-grained result control. Pairwise and batch duplicate detection with similarity scoring. Merge duplicate groups using configurable strategies with provenance preservation. Multi-factor similarity: Levenshtein, Jaro-Winkler, cosine, Jaccard, embedding. Union-Find and hierarchical clustering for batch deduplication at scale. --- ## DuplicateDetector The primary class for finding duplicate entity pairs: ```python from semantica.deduplication import DuplicateDetector detector = DuplicateDetector(similarity_threshold=0.85) duplicates = detector.detect_duplicates(entities) for dup in duplicates: print(f"{dup.entity_a} ≈ {dup.entity_b} ({dup.similarity:.2f})") ``` Fine-grained control: ```python duplicates = detector.detect_duplicates( entities, strategy="semantic_v2", # see strategies below min_similarity=0.85, # minimum score to consider a match top_k_per_entity=3, # max candidates per entity max_results=100, # total result cap sort_by="similarity", # "similarity" | "entity_id" | "cluster_size" ) ``` Detection strategies: | Strategy | Algorithm | Speed | Accuracy | |----------|-----------|-------|----------| | `jaro_winkler` | String similarity (v1) | Fast | Medium | | `blocking_v2` | Blocking + Jaro-Winkler (v2) | Very fast | Medium | | `hybrid_v2` | Blocking + semantic + string (v2) | Fast | High | | `semantic_v2` | Embedding similarity (v2) | Medium | Highest | **v0.5.0 fix:** `DuplicateDetector` no longer produces duplicate definition errors when the same entity appears in multiple sources with identical definitions. --- ## EntityMerger Merges detected duplicate groups into canonical entities: ```python from semantica.deduplication import EntityMerger merger = EntityMerger() merged_entities = merger.merge_duplicates( entities, strategy="keep_most_complete", # see strategies below preserve_provenance=True, # keep source references after merge ) ``` Merge strategies: | Strategy | Behavior | |----------|----------| | `keep_first` | Keep the first entity in each duplicate group | | `keep_last` | Keep the most recently seen entity | | `keep_most_complete` | Keep the entity with the most non-null properties | | `union` | Merge all properties — non-conflicting fields combined | | `voting` | Most common property value wins | ```python # Fine-grained merge with custom property rules from semantica.deduplication import PropertyMergeRule merger = EntityMerger( property_rules={ "name": PropertyMergeRule.KEEP_FIRST, "aliases": PropertyMergeRule.UNION, "description": PropertyMergeRule.KEEP_LONGEST, } ) ``` --- ## SimilarityCalculator Compute multi-factor similarity between entity pairs: ```python from semantica.deduplication import SimilarityCalculator calc = SimilarityCalculator() score = calc.calculate_similarity(entity_a, entity_b) # → SimilarityResult(score=0.91, components={...}) print(score.score) # overall score 0.0–1.0 print(score.components["label"]) # label similarity print(score.components["embedding"]) # semantic similarity print(score.components["property"]) # property overlap ``` Individual metrics: ```python from semantica.deduplication import SimilarityCalculator calc = SimilarityCalculator() # String metrics lev = calc.levenshtein("Apple Inc.", "Apple Inc") jaro = calc.jaro_winkler("Steve Jobs", "Steven Jobs") cos = calc.cosine_similarity(embedding_a, embedding_b) jacc = calc.jaccard({"founded", "tech"}, {"founded", "technology"}) ``` --- ## ClusterBuilder Build clusters from detected duplicate groups for large-scale batch processing: ```python from semantica.deduplication import ClusterBuilder builder = ClusterBuilder(algorithm="union_find") # or "hierarchical" result = builder.build_clusters(entities, similarity_threshold=0.85) print(f"Clusters: {len(result.clusters)}") for cluster in result.clusters: print(f" [{cluster.id}] {cluster.members} — quality: {cluster.cohesion:.2f}") ``` --- ## Convenience Functions ```python from semantica.deduplication import detect_duplicates, merge_entities, calculate_similarity # Quick detection duplicates = detect_duplicates(entities, method="semantic_v2", similarity_threshold=0.85) # Quick merge merged = merge_entities(entities, duplicates, method="keep_most_complete") # Quick similarity score = calculate_similarity(entity_a, entity_b, method="hybrid_v2") ``` --- ## Blocking Strategies Blocking reduces the O(n²) pairwise comparison to a manageable subset: ```python from semantica.deduplication import DuplicateDetector detector = DuplicateDetector( blocking_strategy="token", # "token" | "phonetic" | "ngram" blocking_threshold=0.6, similarity_threshold=0.85 ) ``` --- ## Custom Similarity Functions ```python from semantica.deduplication import MethodRegistry, method_registry def domain_similarity(entity_a, entity_b): # e.g., match drug names by active compound return score # 0.0 to 1.0 method_registry.register("similarity", "drug_name", domain_similarity) detector = DuplicateDetector(similarity_method="drug_name") ``` --- ## See Also Detect value conflicts between non-duplicate entities. GraphBuilder uses deduplication during construction. Normalize entity names before deduplication. Track merged entity lineage.