saraNour commited on
Commit
d2fbfa0
·
verified ·
1 Parent(s): 26a4c83

Upload audit/phase4_audit_report.md with huggingface_hub

Browse files
Files changed (1) hide show
  1. audit/phase4_audit_report.md +361 -0
audit/phase4_audit_report.md ADDED
@@ -0,0 +1,361 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Phase 4 — Variant Assignment Audit Report
2
+
3
+ **Version:** 1.0.0
4
+ **Date:** 2026-07-30
5
+ **Status:** PASS
6
+
7
+ ---
8
+
9
+ ## 1. Input Traceability
10
+
11
+ ### Source of Truth -> Phase 1 -> Phase 2 -> Phase 3 -> Phase 4
12
+
13
+ | Step | Input | Output | Rows | Columns | Key Operations |
14
+ |------|-------|--------|------|---------|----------------|
15
+ | Source of Truth | `saraNour/compliments-brand/source_of_truth/products.parquet` | — | 4,440 | 16 | Voila product catalog |
16
+ | Phase 1 | products.parquet | phase1_output.parquet | 4,440 | 20 | Data quality, brand_clean, title_clean, dropped size_per_unit/size_total |
17
+ | Phase 2 | phase1_output.parquet | phase2_output.parquet | 4,440 | 38 | Identity extraction, brand_norm, product_line, variant_attributes, core_title, identity_hash |
18
+ | Phase 3 | phase2_output.parquet | product_group_mapping.csv | 4,440 | 14 | Product domain classification, taxonomy, grouping by identity_hash+domain+core_title |
19
+ | Phase 4 | product_group_mapping.csv + phase2_output.parquet | product_variant_mapping.parquet | 4,440 | 11 | Variant key generation, variant_id assignment |
20
+
21
+ ### Input Verification
22
+
23
+ | Check | Result |
24
+ |-------|--------|
25
+ | Phase 3 products -> Phase 4 products | 4,440 -> 4,440 (0 lost, 0 added) |
26
+ | Phase 3 groups -> Phase 4 groups | 3,691 -> 3,691 (0 lost, 0 added) |
27
+ | Phase 3 rows -> Phase 4 rows | 4,440 -> 4,440 (0 difference) |
28
+
29
+ ---
30
+
31
+ ## 2. What a "Variant" Means
32
+
33
+ ### Definitions
34
+
35
+ **Product:** A single purchasable item from the Voila catalog, identified by `external_id`.
36
+
37
+ **Product Group:** A set of products that share the same identity. Group membership is determined by:
38
+ - `identity_hash` (extracted from Phase 2): 9 boolean identity attributes
39
+ - `product_domain`: food / non_food / unknown
40
+ - `normalized_core_title`: tokenized, deduplicated, sorted title
41
+
42
+ **Variant:** A purchasable representation of the same reference product that differs in **size or package attributes**. Variants within the same group share all identity attributes but differ in size/quantity.
43
+
44
+ ### Relationship
45
+
46
+ ```
47
+ Product Group (identity)
48
+ Variant 1 (500g)
49
+ Product A (external_id: 506623EA)
50
+ Product B (external_id: 484129EA) [if same size, same variant]
51
+ Variant 2 (1kg)
52
+ Product C (external_id: 506661EA)
53
+ Variant 3 (2kg)
54
+ Product D (external_id: 250067EA)
55
+ ```
56
+
57
+ ### Why This Distinction?
58
+
59
+ The reference database serves a different purpose than a product catalog:
60
+ - **Product Group** = "What is this product?" (identity)
61
+ - **Variant** = "How can I buy it?" (purchasable form)
62
+ - **Product** = "Which specific SKU am I looking at?" (retailer SKU)
63
+
64
+ ### Concrete Example: Peanut Butter
65
+
66
+ **Smooth Peanut Butter 500g, 1kg, 2kg:**
67
+ - Same product group (same identity: smooth, non-organic, Compliments)
68
+ - Three size variants: 500g, 1kg, 2kg
69
+ - Different variant_keys: `amt500.0|unitg`, `amt1.0|unitkg`, `amt2.0|unitkg`
70
+
71
+ **Smooth vs Crunchy vs Organic:**
72
+ - Different product groups (different identity attributes)
73
+ - Smooth != Crunchy (formulation difference)
74
+ - Organic vs non-organic (is_organic difference)
75
+ - Each gets its own group_id
76
+
77
+ **Dog Treat Peanut Butter:**
78
+ - Different product group (different core_title, different identity)
79
+ - Not a variant of human peanut butter
80
+
81
+ ---
82
+
83
+ ## 3. Variant Statistics
84
+
85
+ ### Overview
86
+
87
+ | Metric | Value |
88
+ |--------|-------|
89
+ | Total products | 4,440 |
90
+ | Total product groups | 3,691 |
91
+ | Total unique variants | 4,299 |
92
+ | Unique variant keys | 631 |
93
+
94
+ ### Variants per Group
95
+
96
+ | Count | Groups | % of Total |
97
+ |-------|--------|------------|
98
+ | 1 variant | 3,235 | 87.6% |
99
+ | 2 variants | 357 | 9.7% |
100
+ | 3 variants | 67 | 1.8% |
101
+ | 4+ variants | 32 | 0.9% |
102
+ | **Max** | **10** | — |
103
+ | **Mean** | **1.16** | — |
104
+
105
+ ### Products per Variant
106
+
107
+ | Count | Variants | % of Total |
108
+ |-------|----------|------------|
109
+ | 1 product (single) | 4,168 | 97.0% |
110
+ | 2+ products (merged) | 131 | 3.0% |
111
+ | **Max** | **4** | — |
112
+ | **Mean** | **1.03** | — |
113
+
114
+ ### Size Information
115
+
116
+ | Metric | Value |
117
+ |--------|-------|
118
+ | Products with valid size | 3,197 (72.0%) |
119
+ | Products with nosize | 215 (4.8%) |
120
+ | Products with count-based variant | ~1,028 (23.2%) |
121
+
122
+ ### Top 10 Variant Keys
123
+
124
+ | Variant Key | Products | Description |
125
+ |-------------|----------|-------------|
126
+ | `nosize` | 215 | No size information |
127
+ | `amt400.0\|unitg` | 133 | 400g |
128
+ | `amt200.0\|unitg` | 117 | 200g |
129
+ | `amt500.0\|unitg` | 111 | 500g |
130
+ | `amt300.0\|unitg` | 92 | 300g |
131
+ | `amt454.0\|unitg` | 69 | 454g |
132
+ | `amt250.0\|unitg` | 67 | 250g |
133
+ | `amt1.0\|unitkg` | 65 | 1 kg |
134
+ | `amt450.0\|unitg` | 64 | 450g |
135
+ | `amt1.0\|unitl` | 61 | 1 L |
136
+
137
+ ---
138
+
139
+ ## 4. Variant Assignment Rules
140
+
141
+ ### Rule 1: Variant Key Generation
142
+
143
+ **Rule Name:** `build_variant_key`
144
+ **Input Columns:** `variant_attributes` (JSON from Phase 2)
145
+ **Output Column:** `variant_key`
146
+
147
+ **Logic:**
148
+
149
+ ```
150
+ 1. Parse variant_attributes JSON
151
+ 2. If "count" is present:
152
+ key = "count{count}" # e.g., "count20"
153
+ 3. Else if "amount" is present:
154
+ key = "amt{amount}"
155
+ If unit: append "|unit{unit}" # e.g., "amt500.0|unitg"
156
+ If qty > 1: append "|qty{qty}" # e.g., "amt12|unitml|qty12"
157
+ If multiplier > 1: append "|mult{mult}"
158
+ 4. Else if "raw" is present:
159
+ key = "raw{raw_string}" # fallback
160
+ 5. Else:
161
+ key = "nosize" # no size info
162
+ ```
163
+
164
+ **Examples:**
165
+
166
+ | variant_attributes | variant_key |
167
+ |--------------------|-------------|
168
+ | `{"amount": 500.0, "unit": "g"}` | `amt500.0\|unitg` |
169
+ | `{"amount": 1.0, "unit": "kg"}` | `amt1.0\|unitkg` |
170
+ | `{"count": 20}` | `count20` |
171
+ | `{"qty": 12, "amount": 29.583, "unit": "ml"}` | `amt29.583\|unitml\|qty12` |
172
+ | `{}` | `nosize` |
173
+
174
+ **Why:** Variant keys capture size/package differences. Products with the same variant_key are identical purchasable forms.
175
+
176
+ ### Rule 2: Variant ID Generation
177
+
178
+ **Rule Name:** `generate_variant_id`
179
+ **Input Columns:** `group_id`, `variant_key`
180
+ **Output Column:** `variant_id`
181
+
182
+ **Logic:**
183
+
184
+ ```python
185
+ variant_id = uuid5(NAMESPACE_DNS, f"{group_id}||{variant_key}")
186
+ ```
187
+
188
+ **Examples:**
189
+
190
+ | group_id | variant_key | variant_id |
191
+ |----------|-------------|------------|
192
+ | `b09ff83f-...` | `amt500.0\|unitg` | `a1b2c3d4-...` |
193
+ | `b09ff83f-...` | `amt1.0\|unitkg` | `e5f6a7b8-...` |
194
+ | `b09ff83f-...` | `amt2.0\|unitkg` | `c9d0e1f2-...` |
195
+
196
+ **Why:** Deterministic UUID5 ensures reproducibility. Same input always produces same ID.
197
+
198
+ ### Rule 3: Variant Assignment
199
+
200
+ **Rule Name:** `assign_variants`
201
+ **Input Columns:** `group_id`, `variant_key`
202
+ **Output Columns:** `variant_key`, `variant_id`
203
+
204
+ **Logic:**
205
+
206
+ ```
207
+ 1. For each product: compute variant_key from variant_attributes
208
+ 2. For each product: compute variant_id from group_id + variant_key
209
+ 3. Products in same group with same variant_key -> same variant_id
210
+ 4. Products in same group with different variant_keys -> different variant_ids
211
+ ```
212
+
213
+ **Why:** This is the core variant assignment logic. It groups products by their purchasable form within each reference product group.
214
+
215
+ ---
216
+
217
+ ## 5. Variant Attributes
218
+
219
+ ### Currently Represented
220
+
221
+ | Attribute | Source | Used for Variant? | Description |
222
+ |-----------|--------|-------------------|-------------|
223
+ | `size_amount` | Phase 2 | Yes (via variant_key) | Numeric size (500, 1, 2) |
224
+ | `size_unit` | Phase 2 | Yes (via variant_key) | Unit (g, kg, ml, L, count) |
225
+ | `size_qty` | Phase 2 | Yes (via variant_key) | Quantity multiplier (12-pack) |
226
+ | `variant_attributes` | Phase 2 | Yes (via variant_key) | JSON with amount/unit/qty/count |
227
+ | `size` | Phase 3 | No (metadata only) | Raw size string ("500g", "1 kg") |
228
+ | `brand` | Phase 2 | No (metadata only) | Normalized brand |
229
+ | `product_line` | Phase 2 | No (metadata only) | Product line |
230
+
231
+ ### Currently NOT Represented (Missing)
232
+
233
+ | Attribute | Impact | Notes |
234
+ |-----------|--------|-------|
235
+ | Package type (bottle, jar, can) | Cannot distinguish jar vs bottle variants | Would need Voila packaging data |
236
+ | Package material (glass, plastic) | Cannot distinguish material variants | Not available in source data |
237
+ | Multipack configuration | Limited multipack handling | Only qty is captured |
238
+ | Seasonal/limited editions | Cannot distinguish | Not in source data |
239
+ | Temperature variant (frozen vs shelf) | Cannot distinguish | Not in source data |
240
+
241
+ ### Identity Attributes (NOT Variant Attributes)
242
+
243
+ These attributes define the **product group**, not the variant:
244
+
245
+ | Attribute | Column | Description |
246
+ |-----------|--------|-------------|
247
+ | is_organic | Phase 2 | Organic flag |
248
+ | is_gluten_free | Phase 2 | Gluten-free flag |
249
+ | is_naturally_simple | Phase 2 | Naturally simple flag |
250
+ | is_sugar_free | Phase 2 | Sugar-free flag |
251
+ | is_unsalted | Phase 2 | Unsalted flag |
252
+ | is_lactose_free | Phase 2 | Lactose-free flag |
253
+ | is_peanut_free | Phase 2 | Peanut-free flag |
254
+ | is_plant_based | Phase 2 | Plant-based flag |
255
+ | is_reduced_sodium | Phase 2 | Reduced sodium flag |
256
+ | fat_level | Phase 2 | Fat level |
257
+ | fat_percentage | Phase 2 | Fat percentage |
258
+ | flavour | Phase 2 | Flavour keywords |
259
+ | formulation | Phase 2 | Formulation keywords |
260
+ | product_line | Phase 2 | Product line |
261
+ | product_domain | Phase 3 | food/non_food/unknown |
262
+ | taxonomy | Phase 3 | Taxonomy category |
263
+
264
+ ---
265
+
266
+ ## 6. Peanut Butter Example
267
+
268
+ See `peanut_butter_variant_example.parquet` for the full table.
269
+
270
+ ### Summary
271
+
272
+ | Group | Name | Products | Variants |
273
+ |-------|------|----------|----------|
274
+ | `b09ff83f-...` | Smooth Peanut Butter | 3 | 3 |
275
+ | `bc4d1309-...` | Peanut Butter Crunchy | 3 | 3 |
276
+ | `5bfec681-...` | Light Smooth Peanut Butter | 3 | 3 |
277
+ | `e9bc48d6-...` | Compliments 100% Natural Smooth PB | 2 | 2 |
278
+ | + 17 single-product groups | — | 17 | 17 |
279
+
280
+ ### Multi-Variant Groups
281
+
282
+ **Smooth Peanut Butter (Group b09ff83f):**
283
+ | Product | Size | Variant Key |
284
+ |---------|------|-------------|
285
+ | Smooth Peanut Butter | 500g | `amt500.0\|unitg` |
286
+ | Smooth Peanut Butter | 1kg | `amt1.0\|unitkg` |
287
+ | Peanut Butter Smooth | 2kg | `amt2.0\|unitkg` |
288
+
289
+ **Peanut Butter Crunchy (Group bc4d1309):**
290
+ | Product | Size | Variant Key |
291
+ |---------|------|-------------|
292
+ | Peanut Butter Crunchy | 500g | `amt500.0\|unitg` |
293
+ | Peanut Butter Crunchy | 1kg | `amt1.0\|unitkg` |
294
+ | Peanut Butter Crunchy | 2kg | `amt2.0\|unitkg` |
295
+
296
+ ---
297
+
298
+ ## 7. Validation Results
299
+
300
+ | Rule | Description | Result |
301
+ |------|-------------|--------|
302
+ | 1 | Every product has exactly one variant | PASS |
303
+ | 2 | Every variant belongs to exactly one group | PASS |
304
+ | 3 | No orphan variants | PASS |
305
+ | 4 | No duplicate product assignments | PASS |
306
+ | 5 | Summary matches variant table | PASS |
307
+ | 6 | All products have group_id and variant_id | PASS |
308
+ | **Overall** | | **PASS** |
309
+
310
+ ---
311
+
312
+ ## 8. Potential Issues
313
+
314
+ ### Issue 1: Products with nosize (215 products, 4.8%)
315
+
316
+ **Description:** 215 products have no size information in `variant_attributes`. They receive `variant_key = "nosize"` and are still assigned deterministic variant_ids.
317
+
318
+ **Impact:** Low. These products cannot be distinguished by size. If two products in the same group both have nosize, they will share the same variant_id.
319
+
320
+ **Recommendation:** This is expected behavior. Size information is not always available in the Voila catalog.
321
+
322
+ ### Issue 2: Limited variant-distinguishing attributes
323
+
324
+ **Description:** Variant assignment is based solely on size/package attributes. Products with identical size but different packaging (bottle vs jar) cannot be distinguished.
325
+
326
+ **Impact:** Low for current dataset. The Voila catalog does not contain packaging type information.
327
+
328
+ **Recommendation:** If packaging data becomes available in the future, it can be added to variant_key generation.
329
+
330
+ ### Issue 3: Products with merged variants
331
+
332
+ **Description:** 131 variants contain 2+ products (3.0%). These are products with identical variant_keys within the same group.
333
+
334
+ **Impact:** Low. These products have the same size and are in the same group. They are effectively the same purchasable form.
335
+
336
+ **Recommendation:** This is expected behavior. Multiple retailer SKUs can map to the same variant.
337
+
338
+ ---
339
+
340
+ ## 9. Recommendations
341
+
342
+ 1. **Phase 4 is safe to freeze.** All validation checks pass. The variant assignment logic is deterministic and reproducible.
343
+ 2. **Phase 5 (Nutrition Merge)** should join nutrition data on `external_id` via the `product_variant_mapping.parquet`.
344
+ 3. **Future enhancement:** If packaging type data becomes available, it can be added to variant_key without breaking the current structure.
345
+
346
+ ---
347
+
348
+ ## 10. Files
349
+
350
+ | File | Format | Description |
351
+ |------|--------|-------------|
352
+ | `phase4_audit_summary.parquet` | Parquet | Audit summary metrics |
353
+ | `phase4_audit_summary.csv` | CSV | Audit summary metrics |
354
+ | `peanut_butter_variant_example.parquet` | Parquet | Peanut Butter variant example |
355
+ | `peanut_butter_variant_example.csv` | CSV | Peanut Butter variant example |
356
+ | `phase4_audit_report.md` | Markdown | This report |
357
+
358
+ ---
359
+
360
+ *Generated by Phase 4 Audit Process*
361
+ *Date: 2026-07-30*