テーマ
「期間Aと期間Bで単語の出現頻度の差を算出する」
- if-idf的なことをやるためのお試しといった感じです
- 出現頻度:その単語を含む記事数
方針
- terms aggregation → 単語毎の出現頻度を数える
- filter aggregation → 期間Aと期間Bに分割
- bucket script aggregation → 期間Aと期間Bを比較
設定
elasticsearch.yml
script.engine.groovy.inline.aggs: on
query
bucket-script-agg.json
GET _search?search_type=count
{
"query": {
"match_all": {}
},
"aggs": { // 1.terms aggregation
"term": {
"terms": {
"field": "text",
"size": 5
},
"aggs": { // 2.filter aggregation
"dateRange1": { // 期間A
"filter": {
"range": {
"datetime": {
"gte": "2014-04-01T00:00:00+0900",
"lte": "2014-04-10T23:59:59+0900"
}
}
}
},
"dateRange2": { // 期間B
"filter": {
"range": {
"datetime": {
"gte": "2014-04-11T00:00:00+0900",
"lte": "2014-04-20T23:59:59+0900"
}
}
}
},
"tf-idf": { // 語弊あり
"bucket_script": { // 3.bucket script aggregation
"buckets_path": {
"range1": "dateRange1._count", //doc_countを取り出す
"range2": "dateRange2._count"
},
"script": "range2 - range1" //簡単なscript
}
}
}
}
}
}
結果
response.json
{
"took": 133,
"timed_out": false,
"_shards": {
"total": 7,
"successful": 7,
"failed": 0
},
"hits": {
"total": 48762,
"max_score": 0,
"hits": []
},
"aggregations": {
"term": {
"doc_count_error_upper_bound": 6688,
"sum_other_doc_count": 904138,
"buckets": [
{
"key": "い", // term(analyzerの設定が適当なので変な感じ)
"doc_count": 23490, // hits全体での件数
"dateRange2": {
"doc_count": 7642 // 期間Aでの件数
},
"dateRange1": {
"doc_count": 8270 // 期間Bでの件数
},
"if-idf": {
"value": -628 // 期間B - 期間A の結果
}
}
...
]
}
}
}
まとめと感想
- かなり柔軟に処理を書けそう
- scriptで扱うbucketの数が特定できないと使えないような気が
- bucketからドキュメント件数を取り出すには
{backet_path}._count
を使う - ひとまず思ったとおりのことができた
- bucketを任意の条件でfilterするaggregationの構想もあるらしいので期待
- (20151112追記) bucket selector aggregationと言うのがそれらしい
- Senseが超便利
課題
- 期間Aと期間Bそれぞれの全ドキュメント数で正規化するような処理を入れたい
- terms aggregationとfilter aggregationの順序を逆に出来るだろうか