Retail Sales Index via SingStat Table Builder · latest month July 2026 · 4 of 6

July 2026 retail value growth was concentrated in watches, while volume weakness spanned six observed industries. Chained volume fell −1.3% YoY; current-price value rose +1.5%. Six of eleven industries declined in volume, representing 47.8% of total 2025-base weight; the subset covers 86.1%. Supermarkets and petrol supplied the largest approximate volume drags (−0.84 and −0.74 pp), partly offset by watches (+0.84 pp). On value, Watches & Jewellery contributed +1.21 pp against total growth of +1.46%. The +0.21 pp weight-swap result is for a renormalized 11-industry subset, not the rebase's effect on the published headline. Retail only; F&B services stay separate.

−1.3% vs +1.5%
July 2026 YoY — chained volume vs current-price sales value; SA MoM rose on both bases (+0.40% / +0.88%)
+1.21 of +1.46
watches' calculated value contribution (pp) vs total growth (%); six of eleven observed industries fell in volume, 47.8% of total weight
18.1% → 14.8%
Motor Vehicles' published weight change; +0.21 pp is the old/new-weight sensitivity of the observed subset, not a headline effect
35,271/35,271 · 13/13
July snapshot cells retained / staging checks, 0 exclusions; 19 release-comparable rows, three not recomputed; tolerance 0.05 + 0.001 pp
July 2026 retail sales: stat cards and history — chained volume vs current prices
YoY: volume fell while value rose. SA MoM: both rose (volume +0.40%, value +0.88%). Three years of history keep the one-month print in context.

More views

Ranked observed-industry split — volume vs current prices — with fixed-weight contribution estimates
The split — 11 observed industries ranked by their volume move, beside calculated value contributions. Covered sum +1.4404 pp, total growth +1.4578%, residual +0.0174 pp; not a complete identity.
Published Tableau dashboard. Explore retail value/volume history, fixed-weight industry contributions and the published total, covered estimate and residual. The industry picker changes only the contribution view. July 2026 snapshot; not an automatic live feed. Coverage and approximation limits still apply.

Open the interactive Tableau dashboard → · Dashboard notes and checks · Source extract.

Method

  1. Pull — 14 SingStat Table Builder tables (RSI & F&B: volume, prices, value, online); structure-validated before replacing files, release-stamped manifest. src/download.py
  2. Audit — profile, units, SA vs original; compare the dated July release transcription with raw Table Builder data. This remediation does not freshly authenticate the PDFs. docs/data_audit.md · src/audit.py
  3. Stage & check — raw JSON → tidy monthly + quarterly tables; 13 assertions (coverage, contiguity, ranges) run before the parquet is written. sql/01 · sql/05
  4. Split & quantify — latest-month YoY + SA MoM on both bases; fixed-weight value calculations, approximate volume contributions and explicit residuals; a renormalized subset weight swap under both weight sets. src/analysis.py → latest_split.csv · rebase_read.csv
  5. Draw & write — three figures as code, light and dark; PNG byte equality requires the same Python/dependencies/fonts/backend and manifest-derived date (src/figures.py); then the decision memo and dated release checks (0.05 pp rounding + 0.001 pp index-precision allowance, applied before rounding differences; three unavailable rows are not matches).

Reproduce

git clone https://github.com/faizsaifulnizam/retail-sales-split && cd retail-sales-split
uv venv .venv --python 3.12          # or: python -m venv .venv
source .venv/bin/activate            # Windows: .venv\Scripts\activate
uv pip install -r requirements.txt   # or: pip install -r requirements.txt

python src/download.py       # 14 Table Builder tables → data/raw/ (gitignored; --force to re-pull)
python src/build_dataset.py  # staging + 13 checks → data/processed/*.parquet
python src/audit.py          # dated release, level/share checks → outputs/
python src/analysis.py       # split + rebase + sensitivity + reconciliation history → outputs/
python src/figures.py        # reports/figures/ + docs/img/ (light + dark)

Then check outputs/latest_split.csv: Watches & Jewellery reads calculated value contribution +1.207 pp, Supermarkets & Hypermarkets −0.370 pp, and the Total row −1.28% (volume) / +1.46% (prices) for July 2026. July snapshot from the 2026-10-04 pull; these expectations apply to July, not an arbitrary latest row. A later re-pull can move the newest month.

Limits and residuals. July's +0.0174 pp residual is not a general identity: June's covered +3.3093 pp versus total growth +4.0182% leaves +0.7088 pp. Missing coverage, linked aggregation and rounding can enter the residual. Growth contributions are pp, not index-point changes; volume approximations have no universal ±0.1 pp error bound. The weight swap excludes 13.9% of new weight and does not reconstruct the old-base headline. These aggregates cannot measure customers, company sales or causes. The implied +2.77% value/volume deflator change is not CPI. F&B total is separately −3.26% volume / −1.86% value YoY.

Refresh and verification scope. A re-pull updates calculations, not the July release transcription or narrative. Transcribe the matching release-YYYY-MM-tables.csv, update audit selection and expected levels/share, review prose and snapshot locks, and run build_dataset → audit → analysis → figures. Stable LF CSVs require unchanged inputs; PNG bytes are only comparable within the same rendering environment. New execution/publication receipts are tracked in review-remediation.md; no fresh network/PDF check is claimed.

Read more