Elasticsearch Kibana でデータの可視化

Elasticsearch と Kibana で実現するデータの可視化とその設定例

Kunihiko Kido
May 2, 2014 · 19 min read

Kibana でデータの可視化ということで、ググればたくさん出てきますが、私なりにまとめたいと思います。今回可視化するのはツィート情報です。ツィート情報を Elasticsearch にインデックスして、Kibanaで、可視化してみたいと思います。

作成するダッシュボード

こんな感じのダッシュボード作ります。


ツィート情報のインデックスには、Twitter River Plugin for Elasticsearch を使用します。まずは、Elasticsearch をここからダウンロードして解凍し、Twitter River プラグインをインストールして起動します。

# Install Elasticsearch.
$ tar zvxf elasticsearch-1.1.1.tar.gz
# Install Twitter River Plugin.
$ cd elasticsearch-1.1.1
$ bin/plugin -install elasticsearch/elasticsearch-river-twitter/2.0.0
# Run Elasticsearch.
$ bin/elasticsearch

Kibana をここからダウンロードして解凍します。Kibana はHTMLとJavaScript だけなので、一般的なHTTPサーバーであれば起動できます。ここでは、お手軽に Python のSimpleHTTPServer を使用して Kibana を起動します。

今回は、Elasticsearch も Kibanaも同じローカル内で起動するのでKibanaの設定は特に何も変更せずそのまま起動するだけでOKです。

# Install Kibana.
$ tar zvxf kibana-3.0.1.tar.gz
# Run Kibana.
$ cd kibana-3.0.1
$ python -m SimpleHTTPServer

上記コマンドでHTTPサーバーを起動後、ブラウザで http://localhost:8000/ にアクセスしてKibanaのWelcomページが表示されればOKです。

SimpleHTTPServerは起動するポート番号を変更することも出来ます。

$ python -m SimpleHTTPServer 8080

コマンドのエイリアス作っておくとちょっと便利。

alias simplehttpd=’python -m SimpleHTTPServer’
Welcome to Kibana.

参考:Kibana を ES プラグインとしてインストールする

https://twitter.com/johtani/status/462624729577189378

見つけたので勝手に掲載させてもらいました。確かにこの方が簡単ですね。

手順を調べたのでメモ。

# Kibana プラグインのインストール
$ bin/plugin -url http://download.elasticsearch.org/kibana/kibana/kibana-latest.zip -install elasticsearch/kibana3
# ブラウザアクセスURL
http://localhost:9200/_plugin/kibana3/

NOTE:古いバージョンがインストールされてしまうので、インストールするとき、-url で最新版を指定した方が良さそうです。


日本語を含むツィート情報をインデックス、グラフ化したいのでTwitter River プラグインで作成されるマッピング定義をちょっとカスタマイズしました。下記の内容を twitter.json と名前を付けてファイルに保存してあとで使います。

{
"settings": {
"analysis": {
"tokenizer": {
"ngram_tokenizer": {
"type": "nGram",
"min_gram": 3,
"max_gram": 3,
"token_chars": ["letter", "digit"]
}
},
"analyzer": {
"facet_analyzer": {
"type": "custom",
"char_filter": ["html_strip"],
"tokenizer": "keyword",
"filter": ["cjk_width", "lowercase"]
},
"ngram_analyzer": {
"type": "custom",
"char_filter": ["html_strip"],
"tokenizer": "ngram_tokenizer",
"filter": ["cjk_width", "lowercase"]
}
}
}
},
"mappings": {
"status" : {
"_all": {
"enabled": true,
"analyzer": "ngram_analyzer"
},
"properties" : {
"created_at" : {
"type" : "date",
"format" : "dateOptionalTime"
},
"hashtag" : {
"properties" : {
"end" : {
"type" : "long"
},
"start" : {
"type" : "long"
},
"text" : {
"type" : "string",
"index": "analyzed",
"analyzer": "facet_analyzer"
}
}
},
"in_reply" : {
"properties" : {
"status" : {
"type" : "long"
},
"user_id" : {
"type" : "long"
},
"user_screen_name" : {
"type" : "string",
"index": "analyzed",
"analyzer": "facet_analyzer"
}
}
},
"language" : {
"type" : "string",
"index": "analyzed",
"analyzer": "facet_analyzer"
},
"link" : {
"properties" : {
"display_url" : {
"type" : "string"
},
"end" : {
"type" : "long"
},
"expand_url" : {
"type" : "string"
},
"start" : {
"type" : "long"
},
"url" : {
"type" : "string"
}
}
},
"location" : {
"type" : "geo_point"
},
"mention" : {
"properties" : {
"end" : {
"type" : "long"
},
"id" : {
"type" : "long"
},
"name" : {
"type" : "string",
"index": "not_analyzed"
},
"screen_name" : {
"type" : "string",
"index" : "not_analyzed"
},
"start" : {
"type" : "long"
}
}
},
"place" : {
"properties" : {
"country" : {
"type" : "string"
},
"country_code" : {
"type" : "string"
},
"full_name" : {
"type" : "string"
},
"id" : {
"type" : "string"
},
"name" : {
"type" : "string"
},
"type" : {
"type" : "string"
},
"url" : {
"type" : "string"
}
}
},
"retweet" : {
"properties" : {
"id" : {
"type" : "long"
},
"retweet_count" : {
"type" : "long"
},
"user_id" : {
"type" : "long"
},
"user_screen_name" : {
"type" : "string"
}
}
},
"retweet_count" : {
"type" : "long"
},
"source" : {
"type" : "string",
"index": "analyzed",
"analyzer": "facet_analyzer"
},
"text" : {
"type" : "string",
"index": "analyzed",
"analyzer": "ngram_analyzer"
},
"truncated" : {
"type" : "boolean"
},
"user" : {
"properties" : {
"description" : {
"type" : "string",
"index": "analyzed",
"analyzer": "ngram_analyzer"
},
"id" : {
"type" : "long"
},
"location" : {
"type" : "string",
"index": "analyzed",
"analyzer": "facet_analyzer"
},
"name" : {
"type" : "string",
"index": "not_analyzed"
},
"profile_image_url" : {
"type" : "string"
},
"profile_image_url_https" : {
"type" : "string"
},
"screen_name" : {
"type" : "string",
"index" : "not_analyzed"
}
}
}
}
}
}
}

ちょっと長くなってしまいましたが、ポイントは3つ。

  1. グラフ化に使用する属性で値をそのまま使う属性
    Twitter ユーザー名(user.name)など String 型で、属性の値をそのままを使用してグラフ化したい属性は、index オプションを “not_analyzed” に設定します。
  2. グラフ化に使用する属性で値を正規化する属性
    ハッシュタグ (hashtag.text) など String 型で、属性の値を正規化してグラフ化に使用したい属性は、analyzer をカスタマイズします。上記の設定では、”facet_analyzer” というカスタムアナライザーを用意しましたのでこれを使用します。ハッシュタグなどのユーザーが入力する値は、多少の違っていてもなるべく正規化して、同じデータとして集計したいので、1とは別にしています。
  3. 絞り込み条件で日本語全文検索に使用する属性
    ツィートの内容(text)などの日本語を含む String 型の文章で、日本語全文検索したい属性は、analyzer をカスタマイズします。上記の設定では、Nグラムで日本語全文検索可能な “ngram_analyzer” を用意しましたのでこれを使用します。

なぜこのような設定が必要かというと、1、2についてはグラフ化にファセットの結果を使用するため、デフォルトのアナライザーで解析された属性のファセット結果を使用すると、解析後の最小単語単位の文字列毎に集計されてしまうからです。ん〜何を言っているかわかりづらいなぁ。

たとえば...デフォルトのアナライザーで解析すると次のようになります。

Original: "news sports"
Analyzed: "news", "sports"

この場合、"news sports"で集計されず、"news"、"sports"それぞれで集計されます。さらに日本語は1文字づつに分解されてしまうため使い物になりません。

3については日本語でデータを絞り込みたいから。

それでは、準備も整ったのでインデックスを作成してツィート情報をElasticsearchに流し込みます。

まずは、twitter インデックスに先ほど twitter.json ファイルに保存したマッピング情報を使用して、status ドキュメントタイプを作成します。

# Create Index & Mapping.
$ curl -XPUT 'localhost:9200/twitter' -d @twitter.json

つぎに、Twitter River プラグインを使ってツィートフィード用の River を作成します。アクセストークンなどの認証キーは事前に準備してください。次の例では、”news”または、”ニュース”を含み言語が日本語のツィートを対象に、先ほど作成したstatus ドキュメントタイプにデータをフィードする例です。

curl -X PUT 'localhost:9200/_river/twitter/_meta' -d '{
"type" : "twitter",
"twitter" : {
"oauth": {
"consumer_key" : "*** YOUR Consumer key HERE ***",
"consumer_secret" : "*** YOUR Consumer secret HERE ***",
"access_token" : "*** YOUR Access token HERE ***",
"access_token_secret" : "*** YOUR Access token secret HERE ***"
},
"filter" : {
"tracks" : "news, ニュース",
"language": "ja"
}
},
"index": {
"index" : "twitter",
"type": "status",
"bulk_size": 100
}
}'

コマンドを実行するとすぐにフィードが開始されますので、少し待ってください。

NOTE: language を指定したい場合は、tracks または、follow、locations のいずれかの条件が必要です。

また、フィードを停止するには次のコマンドを実行します。インデックスは削除されませんのでご安心を。

$ curl -XDELETE 'localhost:9200/_river/twitter

ではさっそく、ダッシュボードを作成して行きます。まずはブラウザーで、http://localhost:8000/ にアクセスしてください。せっかくなのでブランクから作成してみたいと思います。

ブランクらか作成するには、表示された画面下にある、”Blank Dashboard” リンクをクリックします。

Blank Dashboard Link

“Blank Dashboard” リンクをクリックすると、下の図のような殺風景な画面が表示されます。

New Dashboard

ダッシュボードの基本設定

まずは、ダッシュボードの名前など基本的な情報を設定するために画面右上の歯車アイコンをクリックして設定画面を表示します。

Dashboard Settings

ダッシュボードの設定には、General、Index、Rows、Controls、Timepicker がありますので、それぞれ設定してきます。

Dashboard Settings : General
主にダッシュボードの名前と見た目のスタイルを設定します。

Dashboard Settings: General

Dashboard Settings : Index
主にデフォルトのインデックスの設定をします。twitter という名前のインデックスを作成しましたので、ここに設定します。

Dashboard Settings: Index

Dashboard Settings: Rows
グラフなどを配置する為のスペース作成します。1つのRow には、複数の Panel (グラフなど)を配置できます。とりあえず、ツィートグラフを配置する為の Graph Row と ツィート情報一覧を表示する為の Status Row を作成しました。

Dashboard Settings: Rows

Dashboard Settings: Controls
とりあえずデフォルトのままで良いかと。ここでは、ダッシュボードの設定の保存先やロード先など設定できます。

Dashboard Settings: Controls

Dashboard Settings: Timepicker
Time Field をインデックス情報にあわせて、created_at に設定します。ここで設定したフィールドの値を使用して、日付や日時でデータを絞り込んだり出来るようになります。

Dashboard Settings: Timepicker

最後にSaveボタンをクリックして反映してください。

ツィート情報一覧パネル

ダッシュボードのRowsの設定で作成した Status Row にツィート情報の詳細な内容を表示します。画面下の領域の”Add panel to empty row”ボタンをクリックしてパネル作成画面を表示します。

Add panel to empty row

パネル作成画面では、Select Panel Type に “table” を選択し、”Title” と “Span” を設定します。Span はパネルの横幅の設定です。12を設定すると横幅いっぱいに表示されます。

Add table Panel

保存してダッシュボード画面に戻ると、ツィート情報の一覧が表示されます。デフォルトでは、_source フィールドの内容がそのまま表示されますので、画面左の “Fields” から表示したい内容を選択してください。

ツィート情報一覧パネル

ツィート数ヒストグラムパネル

次に、ツィート数を時系列に棒グラフで表したツィート数ヒストグラムを作成します。Graph Row に作成してください。Select Panel Type に ”histogram” を選択し、Title、Span、Time Field を以下の図のように設定します。

Add histogram panel

保存してダッシュボードに戻ると時系列のグラフが表示されます。インターバルを ”1s” に設定するとそれっぽく表示されます。

ツィート数ヒストグラムパネル

ハッシュタグ円グラフパネル
ハッシュタグを円グラフ化したパネルを追加します。Select Panel Type に “terms” を選択し、以下の図のように設定します。

Add terms panel

保存すると下記の図ような円グラフが表示されます。ちなみに円グラフに表示するアイテム数を変更するには、画面中央の “length” の値を変更します。

ハッシュタグ円グラフパネル

メンションユーザー円グラフパネル

メンションの多いユーザーの円グラフを作成します。作成手順は、ハッシュタグ円グラフパネルを参考に作成してください。対象フィールドは、”mention.name” または、”mention.screen_name”です。

メンションユーザー円グラフパネル

ツィートユーザー円グラフパネル

ツィート数の多いユーザーの円グラフを作成します。作成手順は、ハッシュタグ円グラフパネルを参考に作成してください。対象フィールドは、”user.name” または、”user.screen_name” です。

ツィートユーザー円グラフパネル

完成!

以上の手順で以下の図のようなダッシュボードが完成したかと思います。最後に画面右上の保存アイコンをクリックしてダッシュボードの設定を保存すれば完成です。

NOTE: 保存せずに画面をリロードしたりしてしまうと、設定内容は元に戻ってしまいますので、こまめに保存した方が良さそうです。

完成

画面上部のQueryで、データを絞り込んだり、各種グラフをクリックして絞り込んだり、期間で絞ったりして分析できるようになりました。

また、画面上部の “Time filter” にある “Auto-Refresh” を設定すると、インデックスされた情報をリアルタイムに更新してダッシュボードで確認することが出来ます。


マーケティング担当者など情報を分析する担当者が、直接 Kibana の画面を使用してダッシュボードを作成するのは難しそうな印象です。実際にはKibanaに精通した管理者が分析担当者の要望などをヒアリングして設定したものを使ってもらうと言った運用になりそうな気がします。

Hello! Elasticsearch.

オープンソースのサーチエンジン Elasticsearch に関連する技術ノート

    Kunihiko Kido

    Written by

    木戸 国彦

    Hello! Elasticsearch.

    オープンソースのサーチエンジン Elasticsearch に関連する技術ノート

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade