SaaS¶
A B2B SaaS customer-success and revenue warehouse. Companies subscribe, onboard users, engage with the product, generate revenue, open support tickets, and eventually churn or expand. Plan tier is tracked with SCD2 versioning; revenue gets CDC audit columns; MRR carries heteroscedastic noise so large accounts vary more than small ones.
What it produces¶
| Table | Type | Grain | Rows (default) | Notes |
|---|---|---|---|---|
dim_date |
dim | per_period | 24 | 24 monthly periods, Jan 2023 – Dec 2024 |
dim_company |
dim | per_entity (+ SCD2) | 100–400 | 100 companies, expanded by SCD2 plan-tier moves |
dim_user |
dim | per_entity (sub-entity) | 400 | 4 users per company |
dim_support_category |
dim | per_reference | 8 | 8 ticket categories |
fct_engagement |
fact | per_entity_per_period | 2,400 | engagement, feature_adoption, active_seats |
fct_revenue |
fact | per_entity_per_period | 2,400 | MRR, expansion, discount_pct (CDC audit) |
fct_support |
fact | per_entity_per_period | 2,400 | tickets, NPS, churn_risk (FK to category) |
evt_login |
event | variable (proportional) | ~24,000 | scaled 10× engagement |
evt_churn |
event | variable (threshold) | 15–30 | churn_risk above 0.7 for 2 consecutive periods |
<fct>_wide |
fact | denormalized companion | — | one per fact (FK'd dims left-joined) |
100 entities across six segments: rapid_adopter (18), steady_grower
(24), enterprise_steady (14), churning (18), expansion_play (12),
at_risk (14). Approximate total row count: 40,000 (excluding wide
companions).
Features exercised¶
- Seasonality — Q4 budget flush (Nov–Dec, strength +0.30) and summer slowdown (Jun–Aug, strength -0.12).
- Causal lag —
expansionfollowsengagementwith delay 2. - Correlations —
mrr driven_by engagement,expansion related feature_adoption,churn_risk opposes engagement, plus explicitsupport_tickets -0.40 engagementandnps 0.55 engagement. - Multi-phase archetypes —
flat > decline @ 10,flat > growth @ 8,growth > spike_then_crash > flat @ 6 @ 12as cohort stories. - Per-segment baselines —
high/mid/lowper metric so enterprise_steady and churning sit in different bands of the same range. - Lifecycle stages —
trial/active/engaged/power_userthresholds on engagement. - SCD2 —
plan_tierondim_companyversioned by MRR thresholds (free/starter/growth/enterpriseat 0.25, 0.55, 0.80). - Sub-entity dim —
dim_userwithcount: 4(four users per company). - Reference dim —
dim_support_categorywith eight static categories. - CDC —
fct_revenuecarries_inserted_at,_updated_at,_opaudit columns. - Heteroscedastic noise —
scale_with_trajectory: trueso large trajectories receive proportionally larger gaussian jitter. - Denormalized output —
<fct>_widecompanion alongside each normalized fact. - Quality injection — null injection on engagement (3%), duplicate rows on logins (1.5%), late arrivals on revenue (2%).
- Events — one proportional (
evt_login) and one threshold (evt_churn).
Use it¶
Common customizations¶
Switch off heteroscedastic noise¶
Default behavior scales gaussian jitter with each entity's trajectory position. Disable for uniform noise across the population:
Add an A/B treatment on one segment¶
Carve steady_grower into a 50/50 treatment / control split:
# Re-author with create() instead of load_template — segments are immutable on the loaded config.
from plotsim import create
config = create(
# ... same other fields ...
segments=[
# ... other segments unchanged ...
{
"name": "steady_grower",
"count": 24,
"archetype": "accelerating",
"treatment": {
"fraction": 0.5,
"lift_log_odds": 0.40,
"start_period": 6,
"target_metric": "expansion",
},
},
],
)
Switch output to partitioned Parquet¶
Write per-year partitions for tables that carry the year column:
Where it shines¶
- Customer-success dashboards —
fct_engagementjoined tofct_supportandfct_revenuereproduces the "engagement-up-MRR-up-tickets-down" story. - Churn-prediction training data —
evt_churnas the label, all other facts as features. Use the manifest'streatment_cohortssection as the answer key when you add an A/B split. - Revenue-restatement pipelines — the CDC
_opflips to"U"for any quality-corrupted row onfct_revenue.