Platform specifics
What a price-matching policy does to a price series
These are the reasons a Colruyt dataset needs its own handling rather than a shared retail schema.
A movement you cannot attribute is worse than no movement
Standard grocery price analysis treats a change as a decision: the retailer moved, and the question is why.
Where a retailer has publicly committed to matching competitors, some proportion of its movements are responses to
someone else's decision. Those look identical in the data.
- A price drop may be Colruyt competing, or it may be Colruyt matching a competitor who dropped first.
- The two mean opposite things for a supplier or a rival reading the series.
- Nothing in a single-retailer feed distinguishes them.
What we do and do not do
We record the change with a precise timestamp and we do not attribute a cause. There is no
reason_for_change field, because we would be guessing at one.
price_change_attribution is a constant not_determined, stated so that nothing downstream
treats a movement as an initiative.
What makes it analysable
Collecting the competitor set on the same schedule. If a competitor moved first and Colruyt followed within
the observation window, the sequence is visible — and the sequence is the evidence, not our label on it.
That is why a Colruyt engagement is usually scoped with two or three Belgian competitors rather than alone. Colruyt
data by itself is the least informative version of this dataset.
Which means observation frequency is the design decision
On most grocers, refresh frequency is a cost trade-off: prices move slowly, so a lower cadence loses little.
Here the timing is the analysis. If you want to see whether Colruyt led or followed, your observation window
has to be shorter than the reaction time.
- A weekly cadence tells you prices differ. It cannot tell you who moved first.
- A daily cadence across the competitor set resolves most sequences.
- The competitor schedule must match, or the comparison is between two different observation times.
We record observed_at to the second and hold the schedule identical across the retailers in scope. This
is one of the few grocery cases where we would recommend a higher refresh than a client's instinct.
Compare that to our DMart page, where we recommend lower. The right cadence
is a property of the retailer's pricing behaviour, not a default.
Belgian specifics: two languages and a deposit scheme
Dutch and French
Belgium is bilingual and product listings appear in Dutch and French, sometimes both on one listing. We retain both
as published, record the dominant language, and do not machine-translate into the name field.
Matching runs on identifiers, normalised attributes and image hashes rather than title similarity, which performs
badly across two languages describing the same product differently.
Deposit
Belgium operates a container deposit on part of the beverage range. As with German Pfand and Dutch statiegeld, it
is refundable, differs by container, and is captured as its own field rather than folded into the shelf price.
Own label
Significant, and it does not cross-match to other retailers. Flagged, matched within the banner, marked unmatched
across — the same position as everywhere else, and it matters here because own-label lines are not covered by a
price-matching policy in the same way branded lines are.