Sandbox — LSD is in active testing. All systems operational·payments are test-mode (no real charges).
Overview Use Cases News Docs Tool Store Support Community Get LSD
Use cases/ Machine Learning/ A churn model whose features you can actually see
Full pipeline run — events and snapshots becoming windowed features, the leak fix, and the scored holdout replace with clip · 1600 × 900

A churn model whose features you can actually see

Wire Postgres events and Parquet snapshots into windowed churn features where every intermediate table is cached and previewable. A leaky recency feature shows up as a too-clean histogram before training, not as a production incident. The same canvas exports the time-based split and scores the holdout with ONNX.

▶ Video walkthrough Intermediate ⏱ 35 min churnfeature-engineeringonnxpostgresparquet

Where churn models actually break#

Every churn post-mortem has the same shape. The model looked great in the notebook — AUC deep in the 0.90s — and useless in production. Along the way the features were rebuilt three times (pandas for exploration, SQL for the warehouse, a serving job in Go), and one of the copies quietly measured the future.

Ours did exactly that. The training set was assembled months after the snapshots it described, and one recency feature was computed relative to export day. Every churned account looked long-dormant. Perfect signal, completely fake.

The dataset#

Two sources, both realistic and both messy. An events table in Postgres — 38M rows of logins, page views, support tickets, and payment failures — and twelve monthly account snapshots in Parquet, one row per account per month, each carrying a churned_within_90d label.

Why a canvas instead of a notebook#

The pipeline in this walkthrough is one artifact. The join, the windows, the split — each is a live node with a cached intermediate you can open and sort. That's what makes the leak findable in seconds. You don't audit code, you look at the data between the steps. And because the ONNX scoring node runs in the same pipeline, the features the model sees at inference are, by construction, the ones it trained on.

Step by step

How it's built

  1. 01

    Connect the events table

    Drop a PostgreSQL node on the canvas and point it at the `events` table. The @load step previews the first rows instantly — 38M rows of user_id, event_type, and timestamp, already typed. No SELECT written yet.

    Postgres @load node with live row preview replace with screenshot · 1200 × 750
  2. #008 · Load 02

    Drop the snapshot files

    Drag twelve monthly `snapshot_*.parquet` files onto the canvas together. LSD unions them into one entity, derives a `snapshot_month` column from the filenames, and caches the result — 2.1M account rows with a `churned_within_90d` label.

    Twelve Parquet files merging into one snapshot entity replace with screenshot · 1200 × 750
  3. 03

    Window the events into features

    Add a @shape join on `user_id`, then window steps for 7-, 30-, and 90-day activity — login counts, support tickets, failed payments. Each window is its own live node, and clicking any of them opens the cached intermediate table it produced.

    Feature-engineering nodes with cached previews replace with screenshot · 1200 × 750
  4. #009 · Shape 04

    Catch the leaky feature

    Sort the feature preview by correlation with the label and `days_since_last_login` glows at 0.96, its histogram split into two clean islands. Click the node and the cause is right there in the expression — the window was anchored to the export date, not the snapshot date. Re-anchor it and the histogram relaxes into honest overlap.

    The too-clean histogram, before and after re-anchoring replace with screenshot · 1200 × 750
  5. 05

    Split by time, not at random

    Add a split step with the boundary on `snapshot_month` — train on the first nine months, test on the last three. Both splits are cached intermediates, so you inspect class balance in each before anything leaves the canvas.

    Time-based split step with both outputs previewed replace with screenshot · 1200 × 750
  6. #010 · Deliver 06

    Export, train, and score in place

    A @deliver step writes both splits to Parquet for your trainer. When training finishes, drop the exported `churn.onnx` back on the canvas, wire it to the test split, and the confusion matrix fills in right next to the pipeline that built the data.

    ONNX scoring node with confusion matrix output replace with screenshot · 1200 × 750
The pipeline

Every step used, at a glance

Load

Events & snapshots

Pull the raw events table from Postgres and union twelve monthly account snapshots from Parquet.

PostgreSQLParquet
Shape

Windowed features

Join, window, and normalize into 42 features with every intermediate cached and inspectable.

Deliver

Split, export & score

Export a time-based train/test split to Parquet and score the holdout with the trained ONNX model.

ParquetONNX
0.94 → 0.81
holdout AUC before and after the leak fix — the honest number
42 features
every one previewable at every step
One canvas
from raw events to a scored holdout

More use cases