More than 3 years have passed since last update.
delika のデータを Elastic Stack で分析じゃ
データの Github になる!を目指す delika 。お恥ずかしながら今まで存じ上げず、ふと Qiita で何か書こうかな、と眺めていたら Qiita で delika の記事投稿キャンペーンをやっていたので、やってみました!
この記事で利用している Elastic Stack は ver 8.1.0 です。
まずはデータを取り込み!
delika には creative commons などでライセンスされたパブリックなデータセットが公開されています。さっそくいくつか取り込んでみましょう。
品川区避難所
CC 2.1 JP ライセンスで最初に見つけたデータが 品川区避難所 のデータ。delika 上でプレビュー確認すると、緯度経度を持っている。ってことは Kibana Map でいけるよね?と、試してみました。
緯度、経度と二つのカラムになっているので、一つの geo_point フィールドに変換するために ingest pipeline を作成。
汎用的に呼べるように、テスト後のスクリプトを登録しておきます。
PUT _scripts/geo-point
{
"script": {
"lang": "painless",
"source": """
Map p = new HashMap();
p.put('lat', ctx[params.lat]);
p.put('lon', ctx[params.lon]);
ctx[params.output] = p;
"""
}
}
すると、色々な ingest pipeline から、 ID 指定で呼び出せるようになります。
{"script":{"id":"geo-point","params":{"output":"location","lat":"緯度","lon":"経度"}}}で、 Machine Learning の Data Visualizer で、 delika からダウンロードした CSV ファイルをどーん、とインポート。 さきほど作った geo-point スクリプトを使って location フィールドを作成します。(同様のデータ編集は Add combined field からもできるのかな??知らなかった)
取り込むと、各フィールドの統計情報がわかりやすく表示されるのは、知らないデータを扱う際にとても便利ですよね。
Kibana Map で表示
Map で Document のレイヤーを追加し、ツールチップで各種フィールドの値を表示する設定を加えてっと。
こんな感じになりました!
『データに関する記事を書こう!』の記事投稿キャンペーンのダミーデータ
お次はキャンペーンのダミーデータです。こちらは
CC BY-NC-SA 4.0 となっています。
- qiita-dummy-article-tags
- qiita-dummy-articles
- qiita-dummy-tags
の三つのファイルに分かれていて、 JOIN する系です。
JOIN 後のリッチな JSON ドキュメントとしてインポートしたいので、 enrich の元となる CSV を取り込み、 enrich の定義をガシガシ作っていきます。
# 記事とタグの JOIN 用
PUT _enrich/policy/delika-qiita-dummy-article-tags
{
"match": {
"indices": "delika-qiita-dummy-article-tags",
"match_field": "article_id",
"enrich_fields": ["tag_id"]
}
}
POST _enrich/policy/delika-qiita-dummy-article-tags/_execute
# タグID と タグ名の JOIN 用
PUT _enrich/policy/delika-qiita-dummy-tags
{
"match": {
"indices": "delika-qiita-dummy-tags",
"match_field": "id",
"enrich_fields": ["name"]
}
}
POST _enrich/policy/delika-qiita-dummy-tags/_execute
タグ名エンリッチのテスト
実際にインポートする前にテストしておきます。まずはタグ ID からタグ名への変換。
# 1段階
POST _ingest/pipeline/_simulate
{
"docs": [ { "_source": { "tags": [ 1, 2 ] } } ],
"pipeline": {
"processors": [
{
"enrich": {
"policy_name": "delika-qiita-dummy-tags",
"field": "tags",
"target_field": "tag_names",
"max_matches": 10
}
}
]
}
}
結果はこんな感じ、ちゃんと引っ張れてますね。
{"docs":[{"doc":{"_index":"_index","_id":"_id","_source":{"tag_names":[{"name":"Python","id":1},{"name":"JavaScript","id":2}],"tags":[1,2]},"_ingest":{"timestamp":"2022-04-16T15:07:11.48173318Z"}}}]}記事からタグ一覧を JOIN して、さらにタグ名に変換
foreach プロセッサを使うのがミソですね。不要なフィールドは削除して綺麗にしておきます。
# 2段階 enrich して、不要なフィールドは削除
POST _ingest/pipeline/_simulate
{
"docs": [ { "_source": { "id": 1 } } ],
"pipeline": {
"processors": [
{
"enrich": {
"policy_name": "delika-qiita-dummy-article-tags",
"field": "id",
"target_field": "tags",
"max_matches": 10
}
},
{
"foreach": {
"field": "tags",
"processor": {
"enrich": {
"policy_name": "delika-qiita-dummy-tags",
"field": "_ingest._value.tag_id",
"target_field": "_ingest._value.tag_name",
"max_matches": 1
}
}
}
},
{
"foreach": {
"field": "tags",
"processor": {
"set": {
"field": "_ingest._value",
"value": "{{_ingest._value.tag_name.name}}"
}
}
}
}
]
}
}
結果はこんな感じ
{"docs":[{"doc":{"_index":"_index","_id":"_id","_source":{"id":1,"tags":["Perl","初心者","Elasticsearch"]},"_ingest":{"_value":null,"timestamp":"2022-04-16T15:11:51.259629479Z"}}}]}Data Visualizer で取り込んで、可視化
テスト済みのプロセッサを加えて、取り込みます。
すると、こんな感じに。ふむふむ 2013 年からのデータなんですね。
Kibana Lens を使って、タグのついた投稿の数から、「プログラミング言語トレンドの推移」的なチャートを作ってみました。ダミーデータですからね、現実とは異なります的な。
酒だ酒だ! sakenowa!
飲んだ日本酒を記録して、好みの日本酒を見つける、 sakenowa のデータも CC BY 4.0 で公開されていました。sakenowa-data。
このデータセット、面白いです。そしてそれなりに複雑で取り込みがいがありますね!
- sakenowa-area
- sakenowa-brand-flavor-tags
- sakenowa-brands
- sakenowa-breweries
- sakenowa-flavor-charts
- sakenowa-flavor-tags
今回は使いませんでしたが、ランキングのデータもあります:
- sakenowa-rankings-area
- sakenowa-rankings-overall
Enrich の定義して、取り込み用のプロセッサを記述
データの種類が多いので、 enrich を書くのも一苦労です。とはいえ、形式的なものなのでそれほどでも。
PUT _enrich/policy/delika-sakenowa-area
{
"match": {
"indices": "delika-sakenowa-area",
"match_field": "id",
"enrich_fields": ["name"]
}
}
POST _enrich/policy/delika-sakenowa-area/_execute
PUT _enrich/policy/delika-sakenowa-brand-flavor-tags
{
"match": {
"indices": "delika-sakenowa-brand-flavor-tags",
"match_field": "brandId",
"enrich_fields": ["tagId"]
}
}
POST _enrich/policy/delika-sakenowa-brand-flavor-tags/_execute
PUT _enrich/policy/delika-sakenowa-breweries
{
"match": {
"indices": "delika-sakenowa-breweries",
"match_field": "id",
"enrich_fields": ["name", "areaId"]
}
}
POST _enrich/policy/delika-sakenowa-breweries/_execute
PUT _enrich/policy/delika-sakenowa-flavor-charts
{
"match": {
"indices": "delika-sakenowa-flavor-charts",
"match_field": "brandId",
"enrich_fields": ["f1", "f2", "f3", "f4", "f5"]
}
}
POST _enrich/policy/delika-sakenowa-flavor-charts/_execute
PUT _enrich/policy/delika-sakenowa-flavor-tags
{
"match": {
"indices": "delika-sakenowa-flavor-tags",
"match_field": "id",
"enrich_fields": ["tag"]
}
}
POST _enrich/policy/delika-sakenowa-flavor-tags/_execute
プロセッサではフィールドの有無をしっかり確認しましょう
今回の取り組みで一番ハマったのがここ。 Ingest Pipeline で色々と加工する時に、例外が発生すると、データ取り込みの際にエラーになるんです。そして Data Visualizer 経由で取り込むとエラーの原因をデバッグするのが大変。。紆余曲折あり、最終系が次の形、 null チェック、しっかりしたら大丈夫!
先ほどのキャンペーンダミーデータの発展系ですね。
POST _ingest/pipeline/_simulate
{
"docs": [
{
"_source": {
"breweryId": 1,
"name": "新十津川",
"id": 1
}
},
{
"_source": {
"breweryId": 2,
"name": "男山",
"id": 2
}
},
{
"_source": {
"breweryId": 14,
"name": "蔵物語",
"id": 16
}
}
],
"pipeline": {
"processors": [
{
"enrich": {
"policy_name": "delika-sakenowa-breweries",
"field": "breweryId",
"target_field": "brewery"
}
},
{
"enrich": {
"policy_name": "delika-sakenowa-area",
"field": "brewery.areaId",
"target_field": "brewery.area"
}
},
{
"enrich": {
"policy_name": "delika-sakenowa-flavor-charts",
"field": "id",
"target_field": "flavor"
}
},
{
"enrich": {
"policy_name": "delika-sakenowa-brand-flavor-tags",
"field": "id",
"target_field": "tags",
"max_matches": 30
}
},
{
"foreach": {
"field": "tags",
"processor": {
"enrich": {
"policy_name": "delika-sakenowa-flavor-tags",
"field": "_ingest._value.tagId",
"target_field": "_ingest._value.tag_name",
"max_matches": 1
}
},
"if": "ctx.containsKey('tags')"
}
},
{
"remove": {
"field": "brewery.area.id",
"if": "ctx.containsKey('brewery')"
}
},
{
"remove": {
"field": "brewery.areaId",
"if": "ctx.containsKey('brewery')"
}
},
{
"remove": {
"field": "brewery.id",
"if": "ctx.containsKey('brewery')"
}
},
{
"remove": {
"field": "flavor.brandId",
"if": "ctx.containsKey('flavor')"
}
},
{
"foreach": {
"field": "tags",
"processor": {
"set": {
"field": "_ingest._value",
"value": "{{_ingest._value.tag_name.tag}}"
}
},
"if": "ctx.containsKey('tags')"
}
}
]
}
}
そして無事取り込まれましたの図。よっしゃ!各フィールドの分布も本物データっぽい。
Sakenowa データの可視化も多少追加。 Sakenowa はまだ色々とできそうです。
酒造で作っている銘柄一覧を表示するドリルダウンを設定
もう一つダッシュボードを作って、ドリルダウンの設定を加えるのは簡単。ドリルダウン先のダッシュボードで、銘柄の一覧を出すのも簡単。今回悩んだのは、酒造名をわかりやすくすっきりと表示する方法。銘柄一覧のデータテーブルに酒造名出すと、そもそもその酒造でフィルタリングかけているので、全行で同じデータが表示されて冗長なんですよね。
こういうのは、避けたい。
そこで、 Tree Map を使った小技を考案!
Tree Map でパーセント表示隠すと、見出しみたいに使える
デフォルトの Tree Map 表示だと、その項目しかなくても 100% って出ちゃうんですよね。
そこで、 Labels オプションから、パーセント表示をオフにしてあげれば ...
こんなにすっきり!
いざ日本酒探訪!
出来上がったダッシュボードで日本酒をさがしてみましょう。酒造の一覧では、地域、銘柄数、タグなどでフィルタリングできるようにしてみました。
その時の気分で選んで、っと。おー、なんか甘くてとろけて美味しそう。
酒造名からドリルダウンで銘柄を見てみます!
飲んでみたい!
おまけ、 8.x の新機能、KNN で似たような味の日本酒を探す
Sakenowa のデータセットには、各銘柄の f1 から f5 までの数値がついていました。それぞれの特徴がなんなのかは分かりませんが、これをベクトルとして扱えば、 KNN で似たようなテイストの銘柄が探せそうです。
Ingest pipeline にこんな感じの script プロセッサでコードを追加して、配列に変換。
if (ctx.containsKey('flavor')) {
ctx['flavor_vector'] = [
ctx['flavor']['f1'],
ctx['flavor']['f2'],
ctx['flavor']['f3'],
ctx['flavor']['f4'],
ctx['flavor']['f5']
];
}
マッピングには dense_vector の定義を追加しました。
"flavor_vector":{"type":"dense_vector","dims":5,"index":true,"similarity":"dot_product"}そして、先ほどの「中島屋」っぽいテイストを検索してみます。
GET delika-sakenowa-brands/_knn_search
{
"knn": {
"field": "flavor_vector",
"query_vector": [0.423, 0.484, 0.617, 0.295, 0.12],
"k": 3,
"num_candidates": 100
},
"_source": ["name", "brewery.name", "id", "tags", "flavor_vector"]
}
結果がこちら。「中島屋」自身がトップに来ていますね、確かに flavor_vector の傾向が近そうな銘柄が検索できました!
{"took":1,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":100,"relation":"eq"},"max_score":0.9999994,"hits":[{"_index":"delika-sakenowa-brands","_id":"PPG4MoABatE64WYgEKiF","_score":0.9999994,"_source":{"flavor_vector":[0.422742747668764,0.483615331307015,0.616651032566715,0.29511402062313,0.119561104037828],"name":"中島屋","brewery":{"name":"中島屋酒造場"},"id":1974,"tags":["ハチミツ","ナッツ","レーズン","果物","ガス","蜜","甘酸っぱい","若い","ジューシー","ミルク"]}},{"_index":"delika-sakenowa-brands","_id":"0vG4MoABatE64WYgEqk0","_score":0.99249893,"_source":{"flavor_vector":[0.450562124136379,0.517157794317278,0.571616530946918,0.235612366670008,0.109413827194259],"name":"玉風味","brewery":{"name":"玉川酒造"},"id":2599,"tags":["ワイン","冷酒","燗酒","スパイス","ジュース","甘酸っぱい","酸味","グレープフルーツ","じっくり"]}},{"_index":"delika-sakenowa-brands","_id":"pPG4MoABatE64WYgD6e1","_score":0.9875737,"_source":{"flavor_vector":[0.487210520058936,0.455750958521687,0.570253559820013,0.367022332284362,0.102992910932017],"name":"伊根満開","brewery":{"name":"向井酒造"},"id":1766,"tags":["ワイン","甘酸っぱい","紹興酒","熱燗","酸味","チョコレート","桜","コーヒー","綺麗","フルーティ","ジュース","ブドウ","甘味","とろみ","冷酒","カラメル","上品","いちご","ガス","淡い"]}}]}}まとめ
いかがでしたでしょうか。Elastic Stack のようなデータ分析の仕組みを色々と試したい時に delika のパブリックなデータセットはとても便利ですね!プランによっては、さまざまなデータセットも利用できるようになるとか。データを共有する場所、仕組みがどんどん充実して、色々なことに利用できるようになってきて素晴らしいですね :)
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme
