Schema — unsupervised (clustering / anomaly)¶
Tables an experiment of type=cluster or type=anomaly can publish. Clustering
and anomaly use different fact tables. Query the data-kb name (RULE 1).
Contents¶
- Clustering:
*__cluster_assignments+*__clusters - Anomaly:
*__anomalies - Example queries
Clustering¶
- Fact
*__cluster_assignments(PKrow_index):row_index, the input's declared key column(s) (catalog rolekey— the answer to "which customer is in which segment"),row_role(trainon every row: the fit learned from all of them),cluster_id(FK → clusters),distance?(to cluster centre). - Dimension
*__clusters(PKcluster_id):cluster_id,label,size,profile?,name_source,ai_name?,ai_description?. - Computed, approved, AI-written.
labelis the name a person approved when the run was released (name_source = 'approved'), else the id-derivedCluster 3— the same identity the chart axes use.profileis computed: the attributes that most separate the cluster from the dataset average, strongest first, each as<field> <signed z><unit>— for exampletenure +2.1sd, balance -1.4sd, Notes 1.8rel (best of 384 dims). sd= standard deviations of that raw column. Written only for an axis that is an affine image of the user's own column.rel= relative enrichment on a derived axis (a rank image, a 0/1 share, a frequency lookup, a text latent). Do not report arelnumber as standard deviations of the named column — nobody computed that.(best of N dims)marks a source spread over several axes (a text column's embedding): the number is its strongest axis and the sign of a latent axis means nothing, so the term is written unsigned.- It is descriptive, not a significance test: the clusters were chosen to maximise exactly this separation, so no p-value or q-value is attached.
- Empty
profilevs absentprofile. An empty string means the computation ran and no attribute distinguishes this cluster (it is below the support floor, or its members are spread like the rest of the data) — that is a finding. The whole column is absent when the fields could not be computed at all (a run fitted by an older engine). Never report "nothing distinguishes any cluster" off a table that has noprofilecolumn. ai_name/ai_descriptionare AI-written text, not measurements. The workspace's agent wrote them (the run'sclusters.namesannotation) from the cluster's statistics and a sample of its member rows, so they can be wrong or invented. Verify againstprofileand your own aggregates; never quote one as a finding, and never without saying it is AI-written.name_sourceis one ofapproved(a person approved the name inlabelat release — it wins over the others),ai(AI-written),no_evidence(the run's clusters were named and this one had nothing to ground a name on — leave it unnamed rather than naming it yourself) ornot_named(no names were written for the run: the workspace's profile does not write them automatically, or nobody asked; this says nothing whatever about the cluster). Bothai_*columns are absent entirely on runs with no names written.
Anomaly¶
- Fact
*__anomalies(PKrow_index):row_index, the input's declared key column(s),row_role(train),anomaly_score(higher = more anomalous),is_anomaly?(bool),rank?(1 = most anomalous),decision_threshold?,threshold_source?. anomaly_scoreis not a probability. It is a detector-specific, unnormalised quantity whose only guarantee is ordering within this run. No units, no calibration, not comparable across runs or detectors. Do not report it as a likelihood or a percentage, and do not average it across runs. Report ranks, percentiles or counts instead.- Run quality is
separation, notcount(is_anomaly).separation(the run'svalidation_scorein the catalog / experiment summary) is the gap between flagged and typical scores in unflagged-IQR units — dimensionless, so it compares across detectors. The flagged share does not: with amode='single'detector it restates the operator'scontaminationsetting, so "5% are anomalous" is a knob, not a finding. - Operating point.
is_anomalyis taken at the run's current cutoff, andthreshold_sourcesays whose cutoff that is:fitted(the detector's own) oroperator(a threshold committed on the Decision surface after the fit). Quote it when you report a flagged count — an operator can re-cut the same scores at any time, and the count moves with them. The comparison differs by source:fittedisanomaly_score > decision_threshold(the detector's own rule) andoperatorisanomaly_score >= decision_threshold. That is not pedantry — ties on the cut are routine forhbos/ecod/copod, where ~80% of scores can sit exactly on it, so re-deriving the flag with>=at a fitted cutoff over-counts by that many rows. Read theis_anomalycolumn; do not recompute it. - Which optional columns you get. Five shapes, so check before you query:
full population + operator override →
is_anomaly,decision_threshold,threshold_source; full population at the fitted cutoff → the same three; full population whose artefact recorded no cutoff → flag + source, nodecision_threshold(absent, never a fabricated0.0); top-K sidecar under an override → all three; top-K sidecar with the engine's own label → flag +threshold_source='fitted', withdecision_thresholdonly when the fitted artefact was still readable. A top-K sidecar with neither carriesanomaly_scoreandrankalone. is_anomalycan be NULL or missing. NULL = the run records no usable label for that row. The whole column is absent when the labelling could not be recovered (a top-K sidecar with nopredicted_label);anomaly_scoreandrankare still exact, so rank by score instead — do not treat "not flagged" as "normal" on that shape.- Scope: the catalog
scopeisfullorin_sample. Transductive detectors score only the in-sample fit set — don't claim scores for rows that weren't scored. A table built from the top-K sidecar holds only the toptop_k_anomaliesrows by score, soWHERE NOT is_anomalyis not a population query there either.
Standard joins¶
Fact ↔ input on the declared key column(s) (RULE 3); cluster_assignments
↔ clusters on cluster_id.
Example queries¶
Placeholders <input>, <cluster_assignments>, <clusters>, <anomalies> are
the data-kb names; <key> is the input's declared key column (catalog role
key).
Which customer is in which segment:
SELECT a.<key>, a.cluster_id, c.label
FROM "<cluster_assignments>" a LEFT JOIN "<clusters>" c USING (cluster_id)
ORDER BY a.cluster_id;
Cluster sizes + a profiled feature:
SELECT a.cluster_id, c.label, COUNT(*) AS n, AVG(s.tenure) AS avg_tenure
FROM "<cluster_assignments>" a
JOIN "<input>" s USING (<key>)
LEFT JOIN "<clusters>" c USING (cluster_id)
GROUP BY a.cluster_id, c.label ORDER BY n DESC;
Top-N anomalies with input context:
SELECT s.*, an.anomaly_score
FROM "<anomalies>" an JOIN "<input>" s USING (<key>)
WHERE an.is_anomaly ORDER BY an.anomaly_score DESC LIMIT 50;
Where do flagged anomalies concentrate:
SELECT s.region, COUNT(*) AS flagged
FROM "<anomalies>" an JOIN "<input>" s USING (<key>)
WHERE an.is_anomaly GROUP BY s.region ORDER BY flagged DESC;