Skip to content

Retail

An omnichannel retail warehouse — customers, orders, returns, loyalty. Customers shop across physical stores and digital channels; loyalty tier evolves via SCD2; orders are variable-grain with child line items (parent/child); returns reference prior orders (cross-fact FK); promotions are eligible per-customer via an M:N bridge. Multi-locale faker output reflects international operations. Q4 holiday, summer slowdown, January dip drive the seasonal channel.

What it produces

Table Type Grain Rows (default) Notes
dim_date dim per_period 24 24 monthly periods, Jan 2023 – Dec 2024
dim_customer dim per_entity (+ SCD2 + geo) 100–400 100 customers, expanded by SCD2 loyalty-tier moves
dim_product dim per_reference 8 widget, gadget, gizmo, ..., whatsit
dim_promotion dim per_reference 10 seasonal_sale, bogo, clearance, ..., first_order_discount
fct_customer_activity fact per_entity_per_period 2,400 six metrics + per-segment review narrative
fct_orders fact variable (proportional, CDC) ~order_vol×1.2 one row per order; CDC for refund processing
fct_order_items fact per_parent_row 1–5× parent child items per order
fct_returns fact variable (proportional) ~0.6× return_rate cross-fact FK to fct_orders
evt_session event variable (proportional) ~25,000 scaled 12× conversion_rate
evt_return event variable (proportional) ~400–800 scaled 2× return_rate
bridge_customer_promotion bridge static M:N 100–400 each customer eligible for 1–4 promotions

100 entities across six segments: loyal_repeat (22), holiday_shopper (18), bargain_hunter (22), churning (16), new_customer (14), vip (8).

Features exercised

  • Multi-locale fakerlocale: [en_US, en_GB, fr_FR, de_DE]. Customer names, emails, and faker-driven columns rotate across the four locales.
  • Noise presetslightly_messy (sigma 0.03, outliers 0.01, MCAR 0.005).
  • Seasonality — Q4 holiday lift Nov–Dec (+0.45), summer slowdown Jun–Aug (-0.10), post-holiday January dip (-0.25).
  • Correlationsorder_volume driven_by loyalty_score, cart_value related conversion_rate, plus explicit loyalty_score 0.55 repeat_purchase_rate, nps 0.55 loyalty_score, return_rate opposes loyalty_score.
  • SCD2loyalty_tier on dim_customer tracking loyalty_score with thresholds [0.25, 0.55, 0.80] (bronze / silver / gold / platinum).
  • Parent / child + cross-fact FKfct_orders is variable-grain driven by order_volume; fct_order_items is per_parent_row (1–5 items per order); fct_returns references orders via ref.fct_orders cross-fact FK and is driven by return_rate.
  • CDCfct_orders carries audit columns for refund-processing amendments.
  • Bridgebridge_customer_promotion (M:N), trajectory-driven by loyalty_score so loyal customers are eligible for more promotions.
  • Geo bundlehome_country, home_country_code, home_region, home_city on dim_customer.
  • Narrative columnreview_text on fct_customer_activity with per-segment lexicons.
  • Pool-driven columnspayment_method, order_channel, return_reason are pool.<attr> columns sourcing from the segment's attribute lists.
  • Multi-phase archetypesflat > decline @ 10, flat > growth @ 4, plus seasonal and accelerating.
  • Lifecycle stagesbrowser / first_purchase / returning / loyal on loyalty_score.
  • Quality injection — null injection on conversion_rate (3%), duplicate rows on sessions (1.5%), late arrivals on orders (2%).

Use it

from plotsim import load_template, generate_tables, write_tables

config = load_template("retail")
tables = generate_tables(config)
write_tables(tables, config, output_dir="./output")
plotsim template retail -o retail.yaml
plotsim run retail.yaml -o ./output

Common customizations

Switch to a single locale

Default rotates across four locales. To produce all-US data:

config = create(
    # ... other fields ...
    locale="en_US",
)
locale: en_US

Increase the line-item fan-out

Default is 1–5 items per order. Bump to 1–12 for high-volume warehouse modeling:

facts=[
    # ... fct_orders unchanged ...
    {
        "name":             "fct_order_items",
        "parent_table":     "fct_orders",
        "children_per_row": (1, 12),       # changed from (1, 5)
        "metrics":          [],
        "columns":          [...],         # same as template
    },
]
facts:
  - name: fct_order_items
    parent_table: fct_orders
    children_per_row: [1, 12]

Add a struct column for device info on sessions

Native nested cells survive Parquet and JSONL writes:

events=[
    {
        "name":    "evt_session",
        "trigger": "proportional",
        "driver":  "conversion_rate",
        "scale":   12.0,
        "columns": [
            {"name": "event_id",    "type": "id"},
            {"name": "date_key",    "type": "ref.dim_date"},
            {"name": "customer_id", "type": "ref.dim_customer"},
            {"name": "event_ts",    "type": "timestamp"},
            {
                "name":          "device",
                "type":          "struct",
                "nested_schema": {"os": "string", "version": "string", "screen_height": "int"},
            },
        ],
    },
]
events:
  - name: evt_session
    trigger: proportional
    driver: conversion_rate
    scale: 12.0
    columns:
      - { name: event_id,    type: id }
      - { name: date_key,    type: ref.dim_date }
      - { name: customer_id, type: ref.dim_customer }
      - { name: event_ts,    type: timestamp }
      - name: device
        type: struct
        nested_schema:
          os:            string
          version:       string
          screen_height: int

Then change output.format to parquet or jsonl to preserve the nested cells natively.

Where it shines

  • E-commerce schema teaching — the order / order_item parent/child relationship plus the fct_returns cross-fact FK is the canonical retail star pattern; students join across three fact tables with different grains.
  • Promotion-attribution exercises — bridge_customer_promotion plus dim_promotion's promo_type and discount_band columns let students practice promotion-eligibility joins.
  • Loyalty-program SCD2 — loyalty_tier versioning produces a textbook SCD Type 2 pattern with dim_row_id resolution on facts that FK into the dim.