File size: 7,476 Bytes
bf2fcf4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# Phase 4 β€” Variant Assignment / Variant Modeling

## Compliments Reference DB Pipeline

**Version:** 1.0.0
**Date:** 2026-07-30
**Status:** PASS

---

## Purpose

Phase 4 transforms the Phase 3 product-group representation into a structured product variant model. Products belonging to the same reference product group can have different purchasable variants such as size, package quantity, formulation, flavour, fat level, etc.

---

## Key Concepts

### Reference Product Group (from Phase 3)

A reference product group represents the same underlying product identity. All products in a group share the same identity attributes (organic, gluten-free, fat level, flavour, formulation, product_line).

### Variant

A **variant** is a purchasable representation of the same reference product that differs in attributes such as:

- **Size**: 500 g, 1 kg, 2 kg
- **Package quantity**: 1 Γ— 500 g, 2 Γ— 500 g
- **Pack configuration**: single, multipack, case

### Identifier Hierarchy

| Identifier | Level | Description |
|------------|-------|-------------|
| `external_id` | Source product | Voila retailer product ID (unique per product) |
| `upc` | Source product | Barcode (may be reused across variants) |
| `group_id` | Reference group | Deterministic UUID5 from Phase 3 |
| `variant_id` | Variant | Deterministic UUID5 from group_id + variant_key |

---

## Input Datasets

| Dataset | Source | Description |
|---------|--------|-------------|
| Phase 3 mapping | `phase3/outputs/product_group_mapping.csv` | Product-to-group mapping |
| Phase 2 output | `phase2/outputs/phase2_output.parquet` | Variant-relevant attributes |

---

## What Phase 4 Does

1. **Loads Phase 3 outputs** β€” product_group_mapping.csv
2. **Loads Phase 2 output** β€” for variant_attributes, flavour, formulation, fat_level, fat_percentage
3. **Merges data** β€” combines group assignments with variant attributes
4. **Builds variant keys** β€” deterministic keys from size/package attributes
5. **Generates variant IDs** β€” deterministic UUID5 from group_id + variant_key
6. **Creates variant tables** β€” main variant table, mapping, summary
7. **Validates** β€” checks all consistency rules
8. **Generates audit** β€” Peanut Butter example

---

## Variant Key Logic

The variant key captures size/package information:

```
If count-based: "count{count}"
If size-based: "amt{amount}|unit{unit}[|qty{qty}][|mult{multiplier}]"
If raw: "raw{raw_string}"
If missing: "nosize"
```

Examples:
- `"amt500.0|unitg"` β†’ 500 g
- `"amt1.0|unitkg"` β†’ 1 kg
- `"amt12|unitml|qty12"` β†’ 12 Γ— 12 ml
- `"count20"` β†’ 20 per pack
- `"nosize"` β†’ size missing

---

## Variant ID Generation

```python
variant_id = uuid5(NAMESPACE_DNS, f"{group_id}||{variant_key}")
```

This is:
- **Deterministic**: Same input β†’ same output
- **Reproducible**: Runs produce identical IDs
- **Collision-resistant**: UUID5 namespace-based

---

## Output Files

### reference_product_variants.parquet

One row per unique variant.

| Column | Type | Description |
|--------|------|-------------|
| variant_id | str | Deterministic UUID5 |
| group_id | str | Reference group UUID |
| group_name | str | Most frequent core_title |
| brand | str | Normalized brand |
| product_line | str | Product line |
| product_domain | str | food/non_food/unknown |
| taxonomy | str | Taxonomy category |
| core_title | str | Core product title |
| variant_key | str | Deterministic variant key |
| size | str | Size string |
| size_amount | float | Numeric size amount |
| size_unit | str | Size unit |
| size_qty | int | Quantity multiplier |
| flavour | list | Flavour keywords |
| formulation | list | Formulation keywords |
| fat_level | str | Fat level |
| fat_percentage | float | Fat percentage |
| product_count | int | Products in this variant |
| unique_upcs | int | Unique UPCs in this variant |
| source | str | Data source |
| source_url | str | Source URL |

### product_variant_mapping.parquet

Product-to-variant mapping.

| Column | Type | Description |
|--------|------|-------------|
| external_id | str | Source product ID |
| upc | str | Barcode |
| group_id | str | Reference group UUID |
| variant_id | str | Variant UUID |
| core_title | str | Core title |
| original_title | str | Raw title |
| brand | str | Normalized brand |
| size | str | Size string |
| variant_key | str | Deterministic variant key |
| source | str | Data source |
| source_url | str | Source URL |

### reference_product_variant_summary.parquet

Group-level variant summary.

| Column | Type | Description |
|--------|------|-------------|
| group_id | str | Reference group UUID |
| group_name | str | Most frequent core_title |
| brand | str | Normalized brand |
| product_domain | str | food/non_food/unknown |
| taxonomy | str | Taxonomy category |
| variant_count | int | Number of variants |
| variant_ids | str | Comma-separated variant IDs |
| product_count | int | Total products in group |

---

## Validation Rules

| Rule | Description | Status |
|------|-------------|--------|
| 1 | Every product has exactly one variant | PASS |
| 2 | Every variant belongs to exactly one group | PASS |
| 3 | No product maps to multiple variants | PASS |
| 4 | Variant count matches across tables | PASS |
| 5 | All products have group_id | PASS |
| 6 | All products have variant_id | PASS |

---

## Handling Missing Size

- Products with missing size receive `variant_key = "nosize"`
- They still get deterministic variant_ids
- They are NOT merged together just because size is missing
- Phase 3 group identity remains authoritative

---

## Files

```
phase4/
β”œβ”€β”€ README.md
β”œβ”€β”€ src/
β”‚   └── phase4.py
β”œβ”€β”€ notebooks/
β”‚   β”œβ”€β”€ phase4.ipynb
β”‚   └── phase4_executed.ipynb
β”œβ”€β”€ outputs/
β”‚   β”œβ”€β”€ reference_product_variants.parquet
β”‚   β”œβ”€β”€ reference_product_variants.csv
β”‚   β”œβ”€β”€ product_variant_mapping.parquet
β”‚   β”œβ”€β”€ product_variant_mapping.csv
β”‚   β”œβ”€β”€ reference_product_variant_summary.parquet
β”‚   └── reference_product_variant_summary.csv
β”œβ”€β”€ validation/
β”‚   └── phase4_validation.json
β”œβ”€β”€ statistics/
β”‚   └── phase4_statistics.json
└── audit/
    └── phase4_peanut_butter_example.parquet
```

---

## How to Run

```bash
cd /home/sara/gsoc/compliments-reference-db
python -m phase4.src.phase4
```

---

## LLM Usage

None. All variant assignment is deterministic.

## Nutrition Usage

None. This phase does NOT load or use nutrition data.

## External Datasets

None. This phase does NOT use GPC, Agribalyse, or any external taxonomy.

---

## Traceability

```
source_of_truth/products.parquet
        ↓
Phase 1 output (phase1_output.parquet)
        ↓
Phase 2 output (phase2_output.parquet)
        ↓
Phase 3 product_group_mapping.csv
        ↓
Phase 4 product_variant_mapping.parquet
        ↓
Phase 4 reference_product_variants.parquet
```

---

## Known Limitations

- Variant assignment is based solely on size/package attributes
- Products with identical size but different packaging (e.g., bottle vs jar) cannot be distinguished
- The current dataset does not contain packaging type information

---

## Recommendation for Phase 5

Phase 5 (Nutrition Merge) should:
1. Load `nutrition.parquet` from the authoritative source
2. Join with `product_variant_mapping.parquet` on `external_id`
3. Create a merged product-nutrition table
4. Preserve all variant and group information