Significant terms 집계
Significant terms 집계
significant_terms 집계는 더 넓은 참조 집합(background set)에 비해 문서 부분 집합(foreground set)에서 비정상적으로 자주 나타나는 term을 식별해요. 기본적으로 background set은 대상 인덱스의 모든 문서를 대상으로 해요. background_filter로 좁힐 수 있어요. 이 집계를 사용해 가장 과대대표된 값을 검색해요. 가장 흔한 값만 보여주는 일반 terms 집계로는 충분하지 않은 경우에 유용해요.
각 결과 버킷에는 다음이 포함돼요:
- key: term 값.
- doc_count: term을 포함하는 foreground 문서 수.
- bg_count: term을 포함하는 background 문서 수.
- score: term이 background 대비 foreground에서 얼마나 두드러지는지를 나타냄. 자세한 내용은 Heuristics and scoring을 참고하세요.
집계가 버킷을 반환하지 않으면, 보통 foreground가 필터링되지 않았거나(예: match_all 쿼리 사용) foreground의 term 분포가 background와 동일하다는 뜻이에요.
출처: 문서
본문
기본 예제: 전자상거래 애플리케이션의 고가 반품에서 독특한 term 식별 (Basic example: Identify distinctive terms in high-value returns for an e-commerce application)
고객 주문을 담은 인덱스를 만들어요:
PUT /retail_orders
{
"mappings": {
"properties": {
"status": { "type": "keyword" },
"order_total": { "type": "double" },
"payment_method": { "type": "keyword" }
}
}
}
인덱스에 샘플 문서를 수집해요:
POST _bulk
{ "index": { "_index": "retail_orders" } }
{ "status":"RETURNED", "order_total": 950, "payment_method":"gift_card" }
{ "index": { "_index": "retail_orders" } }
{ "status":"RETURNED", "order_total": 720, "payment_method":"gift_card" }
{ "index": { "_index": "retail_orders" } }
{ "status":"RETURNED", "order_total": 540, "payment_method":"gift_card" }
{ "index": { "_index": "retail_orders" } }
{ "status":"RETURNED", "order_total": 820, "payment_method":"credit_card" }
{ "index": { "_index": "retail_orders" } }
{ "status":"RETURNED", "order_total": 500, "payment_method":"paypal" }
{ "index": { "_index": "retail_orders" } }
{ "status":"DELIVERED", "order_total": 130, "payment_method":"credit_card" }
{ "index": { "_index": "retail_orders" } }
{ "status":"DELIVERED", "order_total": 75, "payment_method":"paypal" }
{ "index": { "_index": "retail_orders" } }
{ "status":"DELIVERED", "order_total": 260, "payment_method":"paypal" }
{ "index": { "_index": "retail_orders" } }
{ "status":"DELIVERED", "order_total": 45, "payment_method":"credit_card" }
{ "index": { "_index": "retail_orders" } }
{ "status":"DELIVERED", "order_total": 310, "payment_method":"credit_card" }
{ "index": { "_index": "retail_orders" } }
{ "status":"DELIVERED", "order_total": 220, "payment_method":"credit_card" }
{ "index": { "_index": "retail_orders" } }
{ "status":"DELIVERED", "order_total": 410, "payment_method":"paypal" }
다음 쿼리를 실행해 전체 인덱스와 비교할 때 반품되었고 $500을 초과하는 주문에서 비정상적으로 흔한 payment_method 값을 식별해요:
GET /retail_orders/_search
{
"size": 0,
"query": {
"bool": {
"filter": [
{ "term": { "status": "RETURNED" } },
{ "range": { "order_total": { "gte": 500 } } }
]
}
},
"aggs": {
"payment_signals": {
"significant_terms": {
"field": "payment_method"
}
}
}
}
반환된 집계는 다섯 건의 고가 반품 중 gift_card가 3번(60%) 나타나는 반면, 전체 인덱스에서는 12번 중 3번(25%)임을 보여줘요. 결과적으로 gift_card는 가장 과대대표된 결제 수단으로 표시돼요:
{
...
"hits": {
"total": {
"value": 5,
"relation": "eq"
},
"max_score": null,
"hits": []
},
"aggregations": {
"payment_signals": {
"doc_count": 5,
"bg_count": 12,
"buckets": [
{
"key": "gift_card",
"doc_count": 3,
"score": 0.84,
"bg_count": 3
}
]
}
}
}
다중 집합 분석 (Multi-set analysis)
먼저 문서를 버킷으로 그룹화한 다음 각 버킷 안에서 significant_terms 집계를 실행하면 카테고리별 비정상 값을 확인할 수 있어요.
예제: 지역별 비정상 cancel_reason (Example: Unusual cancel_reason per region)
다음 예제는 terms 집계로 지역별로 그룹화하고 각 버킷 안에서 significant_terms를 실행해 해당 지역에서 불균형적으로 흔한 취소 사유를 식별해요:
GET /rides/_search
{
"size": 0,
"aggs": {
"by_region": {
"terms": { "field": "region.keyword", "size": 5 },
"aggs": {
"odd_cancellations": {
"significant_terms": { "field": "cancel_reason.keyword" }
}
}
}
}
}
예제: 지도 위의 핫스팟 (Example: Hotspots on a map)
전국의 현장 사이트에서 현장 사고 데이터셋이 있다고 가정해보세요. 각 문서에는 geo_point 타입의 포인트 위치 site.location과 범주형 필드 issue.keyword(예: POWER_OUTAGE, FIBER_CUT, VANDALISM)가 있어요. 더 넓은 참조 집합과 비교해 특정 지도 타일 안에서 과대대표된 사고 유형을 식별하려고 해요. geotile_grid를 사용해 지도를 줌 레벨 타일로 나눌 수 있어요. 높은 precision은 거리나 도시 블록 같은 더 작은 타일을 만들고, 낮은 precision은 도시나 지역 같은 더 큰 타일을 만들어요. 각 타일 안에서 significant_terms 집계를 실행해 지역 이상값을 식별해요.
지도 타일별로 데이터를 세분화하고 해당 타일에서 비정상적으로 빈번한 issue.keyword 값을 식별해요:
GET field_ops/_search
{
"size": 0,
"aggs": {
"tiles": {
"geotile_grid": { "field": "site.location", "precision": 6 },
"aggs": {
"odd_issues": {
"significant_terms": { "field": "issue.keyword" }
}
}
}
}
}
background_filter로 background 집합 좁히기 (Use a background_filter to narrow the background set)
기본적으로 background는 전체 인덱스예요. background_filter를 사용해 더 정확한 결과를 위해 background 문서를 제한해요.
예제: 토론토를 캐나다 나머지와 비교 (Example: Compare Toronto to the rest of Canada)
다음 예제는 foreground를 "Toronto"로 필터링하고 background_filter를 "Canada"로 설정해요. significant_terms는 다른 캐나다 도시에 비해 토론토에서 비정상적으로 자주 나타나는 주제를 강조해요:
GET /news/_search
{
"size": 0,
"query": { "term": { "city.keyword": "Toronto" } },
"aggs": {
"unusual_topics": {
"significant_terms": {
"field": "topic.keyword",
"background_filter": {
"term": { "country.keyword": "Canada" }
}
}
}
}
}
커스텀 background를 사용하면 필터를 적용해 각 후보 term의 background 빈도를 계산해야 하므로 추가 처리가 필요해요. 이는 기본 인덱스 전체 개수를 사용하는 것보다 느릴 수 있어요.
필드 타입 고려 사항 (Field type considerations)
significant_terms 집계는 정확한 값 필드(예: keyword 또는 숫자)에서 가장 잘 동작해요. 심하게 토큰화된 텍스트에 significant_terms 집계를 실행하면 메모리를 많이 소비할 수 있어요. 분석된 텍스트의 경우 full-text 필드를 위해 설계되고 동일한 중요도 휴리스틱을 지원하는 significant_text 집계를 사용하는 것을 고려해요.
휴리스틱과 점수 매기기 (Heuristics and scoring)
score는 term의 foreground 빈도가 background 빈도와 얼마나 다른지에 따라 term의 순위를 매겨요. 단위가 없으며 같은 요청과 휴리스틱 안에서만 비교하는 데 의미가 있어요.
요청당 하나의 휴리스틱을 significant_terms 아래에 지정해 선택할 수 있어요. 다음 휴리스틱이 지원돼요.
JLH
Jensen-Shannon Lift Heuristic(JLH)은 대부분의 범용 시나리오에 적합해요. term의 절대 빈도와 background 집합 대비 상대적 과대대표를 모두 균형 있게 고려하여, 절대적으로도 상대적으로도 모두 증가하는 term을 선호해요.
"significant_terms": {
"field": "payment_method.keyword",
"jlh": {}
}
JLH 점수 매기기 (JLH scoring)
JLH 점수는 다음과 같이 계산돼요:
fg_pct = doc_count / foreground_total 그리고 bg_pct = bg_count / background_total. JLH ≈ (fg_pct − bg_pct) * (fg_pct / bg_pct).
큰 기준선에서 약간 증가하는 term은 매우 작은 background 비중에서 같은 절대 증가를 보이는 term보다 점수가 높아요.
JLH를 사용한 점수 계산 예제 (Score example calculation using JLH)
foreground 집합(고가 반품)에 주문 2,000건이 있고 background 집합(모든 주문)에 주문 120,000건이 있다고 가정해요. significant_terms 집계에서 다음 개수를 가진 단일 term을 고려해보세요:
- doc_count = 160
- bg_count = 3,200
term을 포함하는 문서의 백분율은 다음과 같이 계산돼요:
- fg_pct = 160 / 2000 = 0.08
- bg_pct = 3200 / 120000 ≈ 0.026666…
JLH ≈ (0.08 − 0.026666…) * (0.08 / 0.026666…) ≈ 0.053333… * 3 ≈ 0.16
이 양수 점수는 검색된 term이 전체에서보다 고가 반품에서 눈에 띄게 더 보편적이라는 뜻이에요. 점수는 상대적이에요: 절대 확률이 아니라 term 순위를 매기는 데 사용하세요.
상호 정보 (Mutual information)
상호 정보(Mutual information, MI)는 빈번한 term을 선호하고 인기 있지만 여전히 독특한 term을 식별해요. include_negatives: false를 설정해 foreground에서 background보다 덜 흔한 term을 무시해요. background가 foreground의 상위 집합이 아니면 background_is_superset: false를 설정해요:
"significant_terms": {
"field": "product.keyword",
"mutual_information": {
"include_negatives": false,
"background_is_superset": true
}
}
카이제곱 (Chi-square)
카이제곱(Chi-square)은 부분 집합(foreground)에서 term의 관측 빈도가 참조 집합(background)을 기반으로 한 기대 빈도와 얼마나 편차가 있는지 측정하는 통계적 검정이에요. MI와 마찬가지로 chi-square는 include_negatives와 background_is_superset을 지원해요:
"significant_terms": {
"field": "error.keyword",
"chi_square": { "include_negatives": false }
}
Google 정규화 거리 (Google Normalized Distance)
Google Normalized Distance(GND)는 강한 동시발생(co-occurrence)을 선호해요. 동의어 발견이나 함께 나타나는 경향이 있는 항목에 유용해요:
"significant_terms": {
"field": "tag.keyword",
"gnd": {}
}
백분율 (Percentage)
Percentage는 doc_count/bg_count 비율로 term을 정렬하고 term이 background 히트에 비해 상대적으로 가진 foreground 히트 수를 식별해요. 두 집합의 전체 크기는 고려하지 않으므로 매우 희귀한 term이 지배할 수 있어요:
"significant_terms": {
"field": "sku.keyword",
"percentage": {}
}
스크립트 휴리스틱 (Scripted heuristic)
커스텀 휴리스틱 공식을 제공하려면 다음 변수를 사용해요:
- _subset_freq: foreground 집합에서 term을 포함하는 문서 수.
- _superset_freq: background 집합에서 term을 포함하는 문서 수.
- _subset_size: foreground 집합의 총 문서 수.
- _superset_size: background 집합의 총 문서 수.
다음 요청은 커스텀 스크립트 휴리스틱을 사용해 foreground의 term 빈도를 background와 상대적으로 평가해 field.keyword에 대한 significant_terms 집계를 실행해요:
"significant_terms": {
"field": "field.keyword",
"script_heuristic": {
"script": {
"lang": "painless",
"source": "params._subset_freq / (params._superset_freq - params._subset_freq + 1)"
}
}
}