ElasticsearchとPython使えば生活圏推定を簡単にできる話


# 概要
なんか書いてみようということで、Elasticsearchに行動履歴の位置情報を登録すれば、いい感じに利用できる上に、いい感じに可視化もできるという話をします。


# 前提知識
![logo-elastic.png](https://qiita-image-store.s3.amazonaws.com/0/19795/ae7c567b-fe57-32ec-fd6a-c6636b71ae9a.png)

今回Elasticsearchを利用するので、簡単に紹介。
ElasticsearchはApache Solrとよく比較される全文検索エンジンの一つです。スキーマフリーですべての入出力がREST＆JSONになっています。またJavaで実装されています。

- 詳しくは[Elasticsearchの紹介と特徴](https://speakerdeck.com/johtani/elasticsearchfalseshao-jie-tote-zheng-cross-2015)

インストールはyumでもbrewでも簡単に出来ます。利用したい環境に合わせて調べてみてください。
ちなみにElasticsearchのGUIプラグインのelasticsearch-headが便利なので合わせて入れておくと良いです。

# Elasticsearchの設定
Elasticsearchを起動できたら、利用するindex(データベースで言うテーブルのようなもの)の設定をしていきます。
その為にまずindexのマッピング方法をjsonで作成します。
今回は以下の様なデータセットのログがあることを想定します。


```json:sample_log
{
  "id":1,
  "uuid":"7ef82126c32c55f7272d5ca5dd5e40c6",
  "time":"2015-12-03T04:21:01.641Z",
  "lat":35.658546,
  "lng":139.729281,
  "accuracy":47.126048
}
```

この様なデータセットが上手くマッピングされるように、フィールド毎のタイプなどの設定をします。今回は以下の様なマッピングの設定にしました。geoというデータセットのタイプのマッピングの設定をしてあげているというイメージです。

```json:geo_mapping.json
{
  "geo" : {
    "properties" : {
      "id" : {
        "type" : "integer"
      },
      "uuid" : {
        "type" : "string",
        "index" : "not_analyzed"
      },
      "time" : {
        "type" : "date",
        "format" : "date_time"
      },
      "location" : {
        "type" : "geo_point"
      },
      "accuracy" : {
        "type" : "double"
      }
    }
  }
}
```

少し解説すると、uuidフィールドが```not_analyzed```と設定しているのは、ユニークな値で形態素解析されたくない値なのでこうしています。
また今回重要となるのがlocationフィールドのtypeです。[geo_point](https://www.elastic.co/guide/en/elasticsearch/reference/1.4/mapping-geo-point-type.html)はElasticsearchが提供しているtypeで、経度緯度をセットで登録することで利用できます。このフィールドのtypeにしておくことで便利な検索ができるようになったりします。詳しくは後で。

マッピングの設定が作成できたら、それを利用してindexを作成します。index名は今回```test_geo```にしてます。Elasticsearchを起動している状態で以下の様なcurlを投げてあげれば作成完了です。

```:indexの作成
curl -XPOST 'localhost:9200/test_geo' -d @geo_mapping.json
```

# データの登録
データはログファイルとして所持していることを想定し、ログファイルから作成したindexにデータを登録していきます。
今回公式のpythonのclientがあるのでそれを利用してみます。

- 公式: [elasticsearch-py]
(https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/index.html)

これはpipで簡単にインストールできます。

```:インストール
pip install elasticsearch
```

これを利用して登録していくプログラムは以下の用な感じです。

```py3:regist_es.py
import json
import sys
from elasticsearch import Elasticsearch

es = Elasticsearch()
index = "test_geo"
doc_type = "geo"

f = open('var/logs.json', 'r')

_line = f.readline()
while _line:
    data = json.loads(_line)
    _line = f.readline()
f.close()

for value in data:
    lat = value['lat']
    lon = value['lng']
    del value['lat']
    del value['lng']
    value.update({
        "location" : {
            "lat" : lat,
            "lon" : lon
        }
    })
    es.index(index=index, doc_type=doc_type, body=value, id=value['id'])

```

注意する点は```geo_point```に適した形にするために、```lat```と```lon```をまとめて```location```にしている点です。
このプログラムを実行すればlogs.jsonにあった行動履歴の情報がElasticsearchに登録されます。とても簡単ですね。

# Kibanaで可視化
データの登録が出来たので、あとは煮るなり焼くなりです。
[Kibana](https://www.elastic.co/products/kibana)はElasticsearchに登録されているデータを可視化してくれる、公式の出している可視化ツールです。
現在はKibana4が出ているので、せっかくなので最新版を取ってきてみると良いと思います。
取得したら```./bin/kibana```を実行するだけで5601番ポートでHTTPサーバが起動します。[詳しい設定の方法など](http://qiita.com/harukasan/items/3737a1cc0bed2facc14e)。
実際に起動したら、適当なブラウザでアクセスするとダッシュボードの設定ができます。
色々といじって遊んでみると、下みたいなヒートマップなどが簡単に作成できたりします。

![heatmap.png](https://qiita-image-store.s3.amazonaws.com/0/19795/c9bcba04-881f-51bd-9947-a285cd8da1d8.png)

![histgram.png](https://qiita-image-store.s3.amazonaws.com/0/19795/81ecd3b3-6ef6-b5d7-0fb8-bf28f2774483.png)



# 生活圏推定
せっかくデータを登録したので利用もしてみます。
今回はこの論文([モバイル端末向けの位置情報を利用した情報推薦システム](http://www.jc.u-aizu.ac.jp/11/141/thesis/msy2011/08.pdf))の生活圏推定なんかをやってみます。
位置情報を```geo_type```で登録したので以下の様な、特定の位置から何km以内のデータを取得するというようなクエリが投げれます。

```py3:
query = {
    "from":0,
    "query": {
        "filtered" : {
            "query" : {
                "simple_query_string" : {
                    "query" : uuid,
                    "fields" : ["uuid"],
                }
            },
            "filter" : {
                "geo_distance" : {
                    "distance" : 10 + 'km',
                    "geo.location" : {
                        "lat" : lat,
                        "lon" : lon
                    }
                }
            }
        }
    }
}
```

実際にこれを利用した生活圏推定とかした結果は以下みたいになりました。

```text:Sample結果
Stage1
35.653945 , 139.716692
半径(km): 5.90

Stage2
35.647367 , 139.709346
半径(km): 1.61

重心使う場合
35.691165 , 139.709840
半径(km): 8.22

昼: (104)
35.696822 , 139.708228
半径(km): 9.61
夜: (97)
35.685100 , 139.711568
半径(km): 6.77

最寄り駅(昼): 東新宿
最寄り駅(夜): 新宿御苑前

```

# 感想
Elasticsearchはとても簡単に設定できる上に、利用するのも簡単です。
Elasticsearch凄い。Kibana凄い。しかも新プロダクト([Beats](http://acro-engineer.hatenablog.com/entry/2015/12/15/234056))もあるらしい。
データの量にもよりますが、Elasticsearchにとりあえず登録してみると色々と便利です。
ログなんかは[fluentd](http://www.fluentd.org/)とかで自動に登録することもできるので、組み合わせれば色々出来そうです。

_記事書くのむずかしい…_