| # Phase 2 — Semantic Normalization / Identity Extraction |
|
|
| ## Compliments Reference DB Pipeline |
|
|
| **Version:** 2.0.0 |
| **Date:** 2026-07-30 |
| **Status:** PASS |
|
|
| --- |
|
|
| ## Purpose |
|
|
| Phase 2 reads the validated Phase 1 output and performs **semantic normalization** (brand → brand_norm + product_line) and **identity attribute extraction** from the original title. Phase 2 is purely semantic — it does NOT duplicate basic string cleaning now handled by Phase 1. |
|
|
| --- |
|
|
| ## Input |
|
|
| | Property | Value | |
| |----------|-------| |
| | Source | `phase1/outputs/phase1_output.parquet` | |
| | Row Count | 4,440 | |
| | Column Count | 20 | |
|
|
| --- |
|
|
| ## What Phase 2 Does |
|
|
| ### 1. Brand Semantic Normalization |
| - Uses `brand_clean` from Phase 1 (already whitespace/case normalized) |
| - Maps 8 cleaned brand variants to `brand_norm` (Compliments/Sensations) + `product_line` (Core/Organic/Balance/Naturally Simple/Green/Little Ones/Sensations) |
| - **Does NOT** duplicate whitespace/case cleaning (Phase 1 handles this) |
|
|
| ### 2. Identity Attribute Extraction (from ORIGINAL title) |
| - 9 boolean flags from ORIGINAL title: is_organic, is_gluten_free, is_naturally_simple, is_sugar_free, is_unsalted, is_lactose_free, is_peanut_free, is_plant_based, is_reduced_sodium |
| - Fat-level extraction: fat_free / reduced_fat / regular |
| - Fat-percentage extraction: Numeric % from title |
| - Flavour extraction: 32 keyword list |
| - Formulation extraction: 14 keyword list |
|
|
| ### 3. Title Processing |
| - Core-title extraction: Strip brand prefix + size + bracket content (uses `title_clean` from Phase 1) |
| - Variant attributes parsing: Size string → structured JSON |
|
|
| ### 4. Identity Hash |
| - Deterministic 14-field hash (for reference; grouping in Phase 3) |
|
|
| --- |
|
|
| ## Phase 2 Does NOT |
|
|
| - Perform product grouping |
| - Perform food/non-food classification |
| - Use taxonomy |
| - Use nutrition data |
| - Use external data sources |
| - Modify original `title`, `upc`, `external_id`, or other source identifiers |
| - **Duplicate string cleaning already done in Phase 1** |
|
|
| --- |
|
|
| ## Output Schema |
|
|
| The Phase 2 output (`phase2_output.parquet`) contains 38 columns: |
|
|
| ### Original Columns (14, preserved from Phase 1) |
|
|
| | # | Column | Type | |
| |---|--------|------| |
| | 1 | upc | str | |
| | 2 | external_id | str | |
| | 3 | brand | str | |
| | 4 | title | str | |
| | 5 | price | float64 | |
| | 6 | price_currency | str | |
| | 7 | size | str | |
| | 8 | size_amount | float64 | |
| | 9 | size_unit | str | |
| | 10 | size_qty | int64 | |
| | 11 | size_unit_norm | str | |
| | 12 | image_url | str | |
| | 13 | source | str | |
| | 14 | source_url | str | |
| |
| ### Phase 1 Cleaned Columns (6, preserved from Phase 1) |
| |
| | # | Column | Type | Description | |
| |---|--------|------|-------------| |
| | 15 | brand_clean | str | Cleaned brand (from Phase 1) | |
| | 16 | brand_raw | str | Original brand (from Phase 1) | |
| | 17 | title_clean | str | Cleaned title (from Phase 1) | |
| | 18 | title_raw | str | Original title (from Phase 1) | |
| | 19 | upc_raw | str | Original UPC (from Phase 1) | |
| | 20 | external_id_raw | str | Original external_id (from Phase 1) | |
| |
| ### New Columns (18, added by Phase 2) |
| |
| | # | Column | Type | Description | |
| |---|--------|------|-------------| |
| | 21 | brand_norm | str | Normalized brand (Compliments/Sensations) | |
| | 22 | product_line | str | Product line (Core/Organic/Balance/Naturally Simple/Green/Little Ones/Sensations) | |
| | 23 | is_organic | bool | Organic flag | |
| | 24 | is_gluten_free | bool | Gluten-free flag | |
| | 25 | is_naturally_simple | bool | Naturally Simple flag | |
| | 26 | is_sugar_free | bool | Sugar-free flag | |
| | 27 | is_unsalted | bool | Unsalted flag | |
| | 28 | is_lactose_free | bool | Lactose-free flag | |
| | 29 | is_peanut_free | bool | Peanut-free flag | |
| | 30 | is_plant_based | bool | Plant-based flag | |
| | 31 | is_reduced_sodium | bool | Reduced sodium flag | |
| | 32 | fat_level | str | Fat level (fat_free/reduced_fat/regular) | |
| | 33 | fat_percentage | float64 | Fat percentage from title (nullable) | |
| | 34 | flavour | list | Extracted flavour keywords | |
| | 35 | formulation | list | Extracted formulation keywords | |
| | 36 | variant_attributes | str | Parsed size as JSON | |
| | 37 | core_title | str | Title with brand prefix + size stripped | |
| | 38 | identity_hash | str | Deterministic 14-field identity hash | |
|
|
| --- |
|
|
| ## Validation Results |
|
|
| | Check | Result | |
| |-------|--------| |
| | Row count preserved (4,440) | PASS | |
| | All original columns preserved | PASS | |
| | All 18 new columns added | PASS | |
| | Brand normalization (2 values) | PASS | |
| | Product line (7 values) | PASS | |
| | Fat level values valid | PASS | |
| | Identity flags are boolean | PASS | |
| | Core titles not empty | PASS | |
| | Identity hashes not empty | PASS | |
| | No duplicate external_ids | PASS | |
| | **Overall** | **PASS** | |
| |
| --- |
| |
| ## Files |
| |
| ``` |
| phase2/ |
| README.md |
| src/phase2.py |
| notebooks/phase2.ipynb |
| outputs/ |
| phase2_output.parquet |
| phase2_executed.ipynb |
| validation/ |
| phase2_validation.json |
| statistics/ |
| phase2_statistics.json |
| ``` |
| |
| --- |
|
|
| ## How to Run |
|
|
| ```bash |
| # Using the Python script |
| cd /home/sara/gsoc/compliments-reference-db |
| python -m phase2.src.phase2 |
| |
| # Using the notebook |
| jupyter notebook phase2/notebooks/phase2.ipynb |
| ``` |
|
|
| --- |
|
|
| ## Handoff to Phase 3 |
|
|
| Phase 3 must read: |
| - `phase2/outputs/phase2_output.parquet` |
|
|
| Phase 3 must NOT: |
| - Re-download from HuggingFace |
| - Read the original products.parquet directly |
| - Read Phase 1 output directly |
| - Use any other data source |
|
|