Fix cardinality judged against sample size instead of real event volume
classify_field_shape() received a deduplicated set of observed values with no real event-volume context, so cardinality was always measured against itself — trivially 100%, making any field with more than ~16 distinct values look "high cardinality" no matter how large the real traffic volume was. This misclassified genuinely low-cardinality fields (e.g. username: 82 distinct values across 1,601 events) as free text, which the new shape-veto work then surfaced as false-positive "remove this field" warnings in production. Fixed by passing real event coverage as an explicit total_events parameter; omitting it keeps prior behavior for other callers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,25 @@ class FieldShapesTests(unittest.TestCase):
|
||||
with_name = classify_field_shape(values, field_name="srcip")
|
||||
self.assertLessEqual(with_name.confidence - without_name.confidence, 15)
|
||||
|
||||
def test_deduplicated_sample_alone_misjudges_cardinality_without_total_events(self):
|
||||
# sample_values is commonly a deduplicated set (callers cap distinct values
|
||||
# remembered per field), so its own length can't stand in for real event
|
||||
# volume: 82 distinct plain usernames look "high cardinality" against
|
||||
# themselves even though they're a tiny fraction of a much larger stream.
|
||||
values = [f"user{n}" for n in range(82)]
|
||||
without_total = classify_field_shape(values, field_name="username")
|
||||
self.assertEqual(without_total.primary_shape, "free_text")
|
||||
|
||||
def test_total_events_corrects_cardinality_judgement_for_low_cardinality_field(self):
|
||||
values = [f"user{n}" for n in range(82)]
|
||||
result = classify_field_shape(values, field_name="username", total_events=880)
|
||||
self.assertEqual(result.primary_shape, "categorical_low_cardinality")
|
||||
|
||||
def test_total_events_does_not_rescue_genuinely_high_cardinality_field(self):
|
||||
values = [f"user{n}" for n in range(82)]
|
||||
result = classify_field_shape(values, field_name="username", total_events=95)
|
||||
self.assertEqual(result.primary_shape, "free_text")
|
||||
|
||||
def test_empty_values_return_zero_confidence(self):
|
||||
result = classify_field_shape([], field_name="whatever")
|
||||
self.assertEqual(result.confidence, 0)
|
||||
|
||||
Reference in New Issue
Block a user