はてだBlog(仮称)

私的なブログど真ん中のつもりでしたが、気づけばWebサイト系のアプリケーション開発周りで感じたこと寄りの自分メモなどをつれづれ述べています。2020年6月現在、Elasticsearch、pandas、CMSなどに関する話題が多めです。...ですが、だんだんとより私的なプログラムのスニペット置き場になりつつあります。ブログで述べている内容は所属組織で販売している製品などに関するものではなく、また所属する組織の見解を代表するものではありません。

Term Vectors、Multi termvectorsでドキュメント中の単語出現回数を確認(Elasticsearch)

Elasticsearchには、本分である検索もさることながらドキュメント(テキスト)に関する調査も可能です。

調査も...というか、Elasticsearchのような検索エンジンテキストマイニングの情報技術の結集かと思いますので、テキストマイニングで出てくるような話題に関連するような数値等の確認ができそうな気がします。

確かにその手の基本APIがいくつかありそうでして、そのうちの一つが、Term Vectorsと呼ばれるものです。

Term Vectorsを使うと指定のドキュメント中の(ANALYZE後の)単語出現回数の分布を確認できます。

f:id:azotar:20181225002557p:plain

www.elastic.co

また、Multi termvectorsは、ドキュメントを複数指定できるバージョンです。

これらTerm Vectorsについて、通常のワードカウンターと違って、私が面白いなと思うところは次のとおりです。

  • ANALYZE後の件数カウンターになっている。もちろんmapping設定次第なものの、形態素解析や原形化後のカウントになっている。この流れでstopワード等はカウント対象外にできるので、出現回数にノイズが混ざりにくい。
  • 転置インデックスの生ダンプに相当するデータが取得できる。
  • Elasticsearch(lucene)に付属(当たり前ですが...)。
  • Elasticsearchに付属なので、Elasticsearchを扱っているアプリの中で使いやすいかもしれない。 上記などの延長線上で、いろいろ遊べる(かもしれない)。(ややイレギュラーな使い方かもしれないので性能等の考慮は必要かもしれませんが。)

ということでなかなか面白いでしょう?ということで、ここでは基本的な使い方のマイリマインダです。

Term Vector使い方(要点)

具体的なコールの仕方の一例は、

GET インデックス名/_doc/対象ドキュメントのID/_termvectors?fields=対象フィールド&filter_path=*.*.*.*.tokens.position&field_statistics=false
{
  "filter":{
     "min_term_freq" : 5
  }
}

※ Elasticsearch 6.4 で確認

ここでは応答イメージは割愛しますが、各ワードごとに、term_freqというフィールドが返ってきて、これがこのワードの出現回数です。

なお、上記のコール例でのクエリパラメータは、私のオススメのものにして、値を絞り込んでいます。

が、正確にはちょいと冗長です。ここではクエリパラーメーターでどんなものが指定できるかを暗示する意味でそのようにしています。

また、ここでは、filterに、min_term_ferqを5に設定し、出現回数が5回以上のものに絞っています。 (max_term_freqもあります。)

他にも絞り込み条件として、

  • max_num_terms: 何件まで回答を返すか(デフォルトは25)
  • max_doc_freq : 他ドキュメントも含めてこの単語が出現するドキュメントの数がこれを超えるようなものは回答に含めない。
  • max_word_length : 何文字までのワードを回答に含めるか。

があるものの、データの様子を見るのに使うのは1つめぐらいで、他はスクロールで流れてしまう時に、文字通り回答件数を絞りたい時ぐらいかなと思いました。

また、上記は、「max_XXX」としていますが、対になる「min_XXX」も指定可能です。

Tipsなど

artificialなtextをインプットにできる

実際に登録されているドキュメント(ドキュメントID)ではなく、Term Vectorsのクエリにdocフィールドでテキストを指定すると、このテキストに対してワードの出現回数を解析してくれます。

GET /twitter/_doc/_termvectors?field_statistics=false&term_statistics=false
{
  "doc" : {
    "content_ja" : "となりの客はよく柿食う客だ。隣の客は良く柿を食べる客だ。トナリのお客様はよーく柿を食った客だった。"
  }
}

//  ※ content_jaで指定したテキストに対してワードの出現頻度が返ってくる。

なお、上記では、content_jaがtwitterインデックスに実際に存在するフィールド名となっています。

この方式では、インデックスには存在しない、便宜上のテキストを食わせることができますが、ぶつけるインデックスには、この例でいうと、content_jaフィールドに値を持つ実際のデータが存在している必要があります。 (公式Rでもそれっぽいことが書いてあるのですが、おそらく後述の「統計情報」との兼ね合いの仕様ではないかと思います。)

Multi termvectors

複数のドキュメントを一括で指定できます。

クエリのJSONボディ中のdocsプロパティに、「_id」や「doc」プロパティで指定するとこれらのtermvectorが返ってきます。

docsプロパティは配列なので、ここに必要分だけ、ドキュメントを指定することになります。

GET /twitter/_doc/_mtermvectors
{
   "docs": [
      {
         "_id": "1",
           "fields" : ["content_ja"]
      },
      {
         "doc" : {
            "fullname_ja" : "部屋とTシャツとたわし"
         }
      }
   ]
}
# mappingsでの「store:true」設定

これにより、payloadの値が取得できるようになる。

# Per-field analyzer

省略

追伸:Term statics、Field statistics(自信がないので追伸に記述)

上記では省略しましたが(その一方でオススメのクエリパラメーターではそれとなく示唆しましたが)、Term statics、Field statistics という出現回数の統計情報も取得できます。

おおよそのイメージとしては次のようになっています。

f:id:azotar:20181225002802p:plain

Term information(再掲)

統計情報の前に少しだけ寄り道します。

本編では、出現回数に絞って示しましたが、単語のリストとともに出現回数が返ってきます。 他にも、このワード(term)についての数値が示されています。次のようなものです。

  • term frequency(term_freq) : 出現回数(A)
  • term positions : 出現場所
  • start and end offsets: オフセット
  • term payloads : ペイロード

統計値関連

出現回数にまつわる関連統計数値(インデックス全体での串刺しなどでの集計)も取得できます。(クエリのパラメータを設定すれば、取得されます。)

非常に面白い情報なのですが、公式Rにもそれとなく書いてありますが、「The term and field statistics are not accurate.」です。

おそらく、次↓の図のような各種値が取得できるように思われるのですが、

f:id:azotar:20181225002802p:plain

以下、公式Rの読み違えもあったらごめんなさいですが、公式Rの説明と、termvectorsの返り値の値の文字面を見比べておそらくこういうことかなということでメモってみています。

Term statics

このドキュメントが格納されているインデックス全体の次のような統計数値が返ってくるようです。

  • total term frequency(ttf) : ドキュメント全体のこの単語の出現回数 : (A) の該当ドキュメント分の合計
  • document frequency(doc_freq) : ドキュメント全体のこの単語を含むドキュメント数 (B)

Field statistics

検索対象のフィールドに関する、このドキュメントが格納されているインデックス全体の統計数値が返ってきます。

  • document count(doc_count) : このフィールドを持つドキュメント数
  • sum of document frequencies(sum_doc_freq) : (B)のなんらかのドキュメント件数にまつわるなんらかの合計
  • sum of total term frequencies(sum_ttf) : 全ワードに関する(2)の合計