fix: partition metrics_anomaly_score training stats by dimension - #1042
Conversation
|
👋 @sravankumarkunadi |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe anomaly score model now calculates training statistics and window boundaries separately for each combination of metric, table, column, dimension, and dimension value. ChangesAnomaly training partitioning
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The window functions in metrics_anomaly_score partition only by metric_name, full_table_name and column_name. For dimension-based metrics this pools every dimension value into a single training set, so training_avg / training_stddev / training_set_size describe the whole table rather than the individual dimension value. The effect is worst when dimension values differ in magnitude. With values X~1000/day, Y~200/day and Z~10/day, the pooled average is ~400, so X sits far above its own historical mean and is scored anomalous on every bucket while it has in fact been perfectly stable. Add dimension and dimension_value to all five window partitions, so each dimension value is scored against its own history. This mirrors the partition keys already used by get_anomaly_scores_query, which computes the same statistics for the test path. Non-dimension metrics are unaffected: dimension and dimension_value are null for them, and partition by groups nulls together, leaving a single partition as before. Fixes elementary-data/elementary#1729
e94c119 to
db1d98a
Compare
|
@arbiv kindly review the PR and please let me know if anything should be adjusted. |
Fixes elementary-data/elementary#1729, and addresses the
training_avghalf of elementary-data/elementary#2172.(Both issues are filed on the
elementaryrepo, but the code they describe lives here.)Problem
In
models/edr/data_monitoring/anomaly_detection/metrics_anomaly_score.sql, all five window functions partition bymetric_name, full_table_name, column_nameonly:dimensionanddimension_valueare selected in the CTE and in thegroup by, but they're missing from the partitions. For dimension-based metrics that pools every dimension value into one training set, sotraining_avg,training_stddev,training_set_size,training_startandtraining_enddescribe the table as a whole rather than the individual dimension value.The distortion scales with how uneven the dimension values are. Using the example from #2172 — daily counts per
row_type:Before: every row is scored against a pooled average of ~400. X, which has been flat at ~1000 for its entire history, sits far above that mean and reads as anomalous on every bucket. The genuine Y spike at D4 is diluted by the same pooling.
After: X is scored against X's history, Y against Y's, Z against Z's. X reads as stable, and Y's D4 spike stands out against its own ~200 baseline.
This matches the reported symptom in #2172 —
latest_metric_valuecorrect when grouped by the dimension, buttraining_avgfar below that dimension's real historical values.Change
Added
dimension, dimension_valueto thepartition byof all five window functions.This brings the model in line with
macros/edr/data_monitoring/anomaly_detection/get_anomaly_scores_query.sql, which already partitions the equivalent statistics bymetric_name, full_table_name, column_name, dimension, dimension_value(seepartition_by_keys, line 62). The model was simply never updated to match.Including
dimensionas well asdimension_valuealso handles the case @wbarth11 raised in #1729, where the same value string appears under two different dimensions.Scope
dimensionanddimension_valueare null for them, andpartition bytreats nulls as a single group, so those rows stay in one partition exactly as before.get_anomaly_scores_queryalready partitioned correctly; this only changes themetrics_anomaly_scoreview, which is what users query directly and what feedsanomaly_threshold_sensitivity.sqlfmt(v0.29.0, matching.pre-commit-config.yaml) passes on the changed file.Testing
Verified by inspection and
sqlfmt. I don't have a warehouse to run the integration suite against — the existingintegration_tests/tests/test_dimension_anomalies.pycases exercise the test-time path viaget_anomaly_scores_query, so they don't cover this view. Happy to add coverage formetrics_anomaly_scoreif you can point me at the right place for it.Summary by CodeRabbit