はてだBlog(仮称)

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

2018年のマイブックマークリストの棚卸し

2018年にマイWebブラウザでブックマークしたページ(かつブックマークした際に有効そうだと思って、時間がたっても依然として有効だとおもいつつ、一方で、まだしっかり読み込めていないもの*1のおさらいです。

分野は雑食ですが、やや実用テック系多め。

また、12月はアドベントカレンダーが盛りだくさんなので、来年分送りとして、ここには含めていません。 つまり、2018年といいつつ(2017年12月〜2018年11月ぐらい)のものが対象です。

なにやらコンテンツファーマー系と見間違えられてしまいそうな顔つきになってしまいましたが、実際そんな感じのところはありますネ。ググった当たりどころの結果ここに来られた方、思うようなものでなかった場合はごめんなさい。

見てのとおり雑まとめで、グルーピングもなんとなくしかしていません。一言コメントも上についたりしたについたり。

おおよその一覧↓

Statechart

scrapbox.io

フロントエンドの命名や設計の基本と自分の現在の設計

qiita.com

GraphQLとRESTfulについて今日考えてたこと Backend for Usecase/Resourceについて

lacolaco.hatenablog.com

「おむつを買った人はビールも買う」から何を想像するか

anond.hatelabo.jp

Linkedin/WhereHows

※データマネジメントに参考になるかなと思ってBM。

国内における地域SNSの事例数の推移とその背景

www.slideshare.net

生態系とか進化の過程とか、あるいは人の歴史の栄枯盛衰の縮図感を感じさせる。

サブスクリプションビジネスの「ターボ」:コミュニティとカスタマーサクセス

stilldayone.hatenablog.jp

ITIL、CASBも検討――マルチクラウド時代のITサービスマネジメント東京海上日動の選択 (2/2)

www.itmedia.co.jp

水は高いところから低いところに流れるとするなら、この辺は次はこういう流れになるよねという話。

AWSしかやったことない人向け】AWSGCPのネットワークの違いを理解してみよう

www.mpon.me

CDNに動的コンテンツを安全に通すにはどうするべきか

vector.hateblo.jp

Elasticsearch(そのものではないものの)の検索サイトからの連想

「難読住所」という観点

tech.dely.jp

フレーズバンク

地図情報

国土数値情報ダウンロードサービス

ハイフンなのかマイナスなのか

まぎらわしい「アレ」という観点

第35回 社内横断データセット検索システム「Goods」(パート1) (中井悦司)

https://www.school.ctc-g.co.jp/columns/nakai2/nakai235.html

Do people say it

Testing in Production の 3つのフェーズの図 (Ebookから抽出した図)Keizo Tatsumiさんが追加

e5rijun.hatenablog.com

意図的にプログラムの動きをランダムにしてバグを早期発見するテクニックについて

note.mu

結局、いろんなものにおいて、乱択アルゴリズム(randomized algorithm)が最強なのではないかという気持ちをあらたにさせてくれる話。(注:リンク先でそのような主旨でのべてあるわけではありません。私が勝手に連想した所感です。)

結合テストと呼ぶのをやめた話

ソフトウェアの「テスト」ってなんだろうなという問い。

akito0107.hatenablog.com

yoshitake-1201.hatenablog.com

taxa-program.hatenablog.com

Chromeの開発者ツールを使って特定のDOM要素だけスクリーンショットする方法

qiita.com

「TensorFlow.js」公開、Webブラウザ上で機械学習の開発、学習、実行が可能に。WebGL経由でGPUも活用

https://www.publickey1.jp/blog/18/tensorflowjswebwebglgpu.html

こういうのが好きなんです。

よく使うpsqlの便利技10選

masahikosawada.github.io

イマドキのJavaScriptの書き方2018

qiita.com

ざっくり影の練習~~影にも名前あるのしらなかった

名前をつけづらいものにも、どうにか良い名前をつけていくというマイトレンド。

ドラクエで学ぶ音色の時間変化】

これは"教養"としてスバラシイと思う次第。

【Day-8】絶望的なデータを前処理で何とかする。(pandas/sklearn)

【Day-8】絶望的なデータを前処理で何とかする。(pandas/sklearn) - プロクラシスト

これからの、AdWordsのコンバージョン計測方法 3つ

これからの、AdWordsのコンバージョン計測方法 3つ | SEM従事者のためのコミュニティ|SEMカフェ

ソフトウェアアーキテクチャとサッカーにおけるプレーモデル

mattun.hatenablog.com

アジャイル開発にはモデリングや要件定義の工程はあるのか、という問題とその周辺

forza.cocolog-nifty.com

Why Managers Believe Multitasking Works: Long Decision Wait Times

www.jrothman.com

タイム・コンサルタントの日誌から

プロジェクト管理は技術なのかアートなのか。

brevis.exblog.jp

ゲームモデル公開のすゝめ」 | footballista

その他Twitterのつぶやきから

oreno-yuigon.hatenablog.com

proc面白いよね、という話。UNIXの「ファイル哲学」みたいなのも滲み出てるし。

*1:上から目線ですね。スミマセン...

Elasticsearchを少々気の利いたgrepとして使ってみる冬休みの実験

はじめに

具体的にどうという話ではないのですが、一部の界隈では、貧弱な環境でgrepコマンドのみで様々なテキスト調査、下手をすれば人間をAIに見立てたNLPを実施しなければならんということが無いでしょうか。

貧弱な環境の解決にはなりませんが、もし近くに多少自由になる検索エンジンがあるなら、便利な調査アプローチをとることができる、こんな使い方もあるのではということで、書き綴ってみます。

確認はElasticsearch6.4で実施していますが、実験というほど深掘りはしてないので、タイトル負けしているかもしれません。ご容赦ください。

世のgrep調査ニーズの実態は検索エンジンでの検索行為に近いのではないかという仮説

もう少し補足します。

検索エンジンでは、次のような、grepに比べてある種のあいまいな検索に対応しています。

  • 全文検索寄りのマッチング
  • 形態素解析等の日本語処理
  • 統計的手法が織り込まれた特殊なクエリの存在

また、次のように、クエリのシンタックスそのものや検索条件の作り方が、冒頭のような「多少幅のある探し物ニーズ」そのものを表しているため、grepで黒魔術な正規表現等を繰り出す必要がありません。 (もちろん、grepの世界観はそれはそれで素晴らしいのですが...)

  • クエリーのシンタックスが(SQLに慣れた人からすると少し分かりにくい面もあるが)全文検索寄りの用途として見ると分かりやすい*1
  • 複数インデックスの串刺し、複数フィールドを対象に同じ検索条件で検索しやすい
  • 検索条件無しでも検索しやすい

実際にこれらの用途のためにElasticsearchにデータを入れ込むかはともかく、サイト内検索に対応している場合などは、それを利用しても良いのではないでしょうか。

... というところを(実際もっと高度にやっている方達もいるでしょうが)声に出して言ってみたくなったので荒削りながら以下に例としてあげてみました。

丸囲み文字などできれば使って欲しく無い表記をマルッと調査

Webサイトの記事などでは、厳密には規定していないものの、ライティングの都合やもろもろの理由で使用してほしくない文字を後から調査するというような場合もあると思います。*2

例えば、こちらの文字などです。

https://matome.naver.jp/odai/2134260789846270701/2134261371246625403

grepで対応している正規表現ではこれらの飛び地になっている文字を指定するとなると「OR」型の表現*3となりやたらめんどくさいのですが、検索エンジンでの検索クエリだと、ANALAYZERが良くある設定になっていることが前提ですが、

POST car_and_animal/_search
{
    "query" : {
        "match": { "title_ja": "㋐ ㋑ ㋒ ㋓ ㋔ ㋕ ㋖ ㋗ ㋘ ㋙ ㋚ ㋛ ㋜ ㋝ ㋞ ㋟ ㋠ ㋡ ㋢ ㋣ ㋤ ㋥ ㋦ ㋧ ㋨ ㋩ ㋪ ㋫ ㋬ ㋭ ㋮ ㋯ ㋰ ㋱ ㋲ ㋳ ㋴ ㋵ ㋶ ㋷ ㋸ ㋹ ㋺ ㋻ ㋼ ㋽ ㋾" }
    }
}

などとすれば、ここにあげた文字のいずれかを含むドキュメントを検知できます。

同じく、ここでは詳しく書きませんが、Elasticsearchの「should」の「minimum_should_match」も全文検索らしい用法だと思いますし、世のgrep調査で本当はこんな加減で調査できれば良かったのにという例に当てはまるような気がします。

www.elastic.co

www.elastic.co

誤記の可能性があるものを探す

やや変な例ですが、チームに英語ネイティブの人が多く、「ライオン」と表記してほしいところ、「ライアン」としてテキスト登録する人が後を絶ちません。

このような誤記を探します。誤記には稀にですが「ライエン」というものもぼちぼちあり、これも対象としたいです。他にも似たようなものがあるかもしれません。

Elasticsearchで言うと、fuzzyを使います。

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-fuzzy-query.html

POST car_and_animal/_search
{
  "query": {
    "fuzzy":{ "title_ja":{"value":"ライオン"}}},
}

↓
”ライオン”そのものもだが、"ライエン"や"ライアン"などが存在するか確認できる。


fuzziness、prefix_length、max_expansions、transpositionsといった曖昧さをコントロールするパラメータもあります。

パラメータで加減できるところも含めて、grepなどで正規表現で同じことをしようとするとかなり難しいですね。 (その分ヒットしてほしくないものも引き連れてヒットするトレードオフはありますが...。)

Term suggesterでも(インデックスでsuggest対象になっているかの手間はありますが)似たような利用方法が可能だと思います。

同じ意味を表す用語が複数の表記にバラついているものがないか

前項のfuzzyでも炙り出せる場合がありますが、ここでは別の方法です。

「渡邊」と「渡部」は、「渡辺」に寄せたいが、これをし損ねているものがあるか...というのが一番端的な例です。

アナライザー(kuromoji)の設定がそれなりになされているとして、その上で、

例えば、

POST car_and_animal/_search
{
    "query" : {
      "bool": {
        "must": {
          "match": { "content.my_analyzed": "ターゲットとしているワード(これに統一したいもの)" }
        },
        "must_not": {
          "match": { "content.my_not_analyzed": "ターゲットとしているワード(これに統一したいもの)" }
        }
      }
    }
}

というクエリで検索します。

ここで、content.my_analyzedとcontent.my_not_analyzedは、それぞれ、データソースのドキュメントに対し、前者が規定のアナライズがされているもの、後者がされていないものになります。

上記に書けた範囲の仕込みだと少し無理がある例かもしれませんが、ちょっとごまかし気味にアイディアの意図を説明すると、標準化すると同じものになるが、実際のドキュメント中は意図したどおりに統一した表記になっていない...というものを検索して探しているといるというイメージになります。

もっと単純に、次のような素直に調査する案も当然あります。

POST car_and_animal/_search
{
    "query" : {
        "match": { "title_ja": "ライオン" }
    },
    "highlight" : {
        "fields" : {
            "title_ja" : {"type" : "plain"}
        }
    }
}


↓
hits(一部)

        "title_ja": [
            "<em>ライオン</em>は百獣の王で..."
          ]

でドキュメントが帰ってくるか、また、具体的にマッチした部分をhighlightで識別しやすいようにするというやり方がありそうです。

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html

※highlightはもともとは検索結果をユーザー向けに見やすくするものかと思いますが、この類の調査やデバッグに利用するという使い方も実は便利そうです。 独自のタグで囲んでおけば、結果をそのまま後続の別の調査で取り扱いやすいと思います。

ドキュメントがkuromoji辞書に無い言葉を使っていないか(≒できるだけ標準的・平易で無難な言い回しをしているか/ミーハーにならないように新語は控えめか。)

kuromojiの辞書のありようを逆手に取って、ライティングが一定の範囲に収まっているかを発見的に確認するという発想です。

次のような、未知語と名詞関連以外を除去するようなアナライザの設定を行います。

      "my_kuromoji": ....
        (中略)   
       "filter":[
            "my_filter"
       ]
       (中略)
      "filter":{
        "my_filter":{
          "type":"kuromoji_part_of_speech",
          "stoptags" :[
                未知語と名詞関連以外は除去
       ※名詞関連は多少取捨選択した方が良いかもしれないが、ここでは説明を簡単にするため名詞は全般的に残すこととした。
          ]
      }}

作成した文書をアナライズします。

POST grep/_analyze
{
  "analyzer": "my_kuromoji",
    "text": "どこべろずんちょは大人しいが怒らせると襲いかかってくる"
 }

↓ トークンの一覧が返ってきます。上述の設定からいうとドキュメント中の未知語と名詞の一覧が返ってきます。

{
  "tokens": [
    {
      "token": "どこ",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "べろ",
      "start_offset": 2,
      "end_offset": 4,
      "type": "word",
      "position": 1
    },
    {
      "token": "ちょ",
      "start_offset": 6,
      "end_offset": 8,
      "type": "word",
      "position": 3
    }
  ]
}

返ってきたトークンの件数やワードに対して仮の閾値と比較して、数が多いようであればアラームをあげるというような仕組みが考えられると思います。

他にもありそう...

以下は、ちゃんと考えていませんが、こんなものもうまくやれば生のgrepよりいろいろ気の利いたことができるかも(ただし、ANALYZERの多少トリッキーな助けも必要かも...)な例としてありそうかなというところをキーワードだけですがあげてみました。ちょっと盛ってるかもですが...

表記ゆれ日付の記述を探す

全半角混在などにもある程度強いため。

てにおはがおかしい(かもしれない)ものを探す/文体のおおよその統一

フレーズ検索系のクエリの邪道(?)な使い方や、char_filterで大胆な名寄せ相当および先述のkuromoji_part_of_speechでの特定の品詞除外を組み合わせると文章の骨格が抜き出せる、あるいは外骨格の方を残すことで、てにおはがおかしいものを探したり、文体が叙述式にある程度統一されているかの確認。

なんとなくこんな表現の範囲におさまってそうかのチェック

略。前項と発想は同じ。この記事のアウトラインを考えた時には別にしようとして分けてみたものの、事例を忘れてしまったので、無理に分けなくても良かったかも...

無駄なコピペコード、類似コードの検索

More Like Thisか、フレーズ検索をぶち込むかというところ。

腰を据えてやるなら、リファクタリングツールを使った方が良いでしょうが...

プログラムっぽい記述

いわゆるググラビリティが低いプログラムの記述の横並び調査など。

エラーログの本格分析前の下調べ

定型フォーマットでしかも絶えず流れ続けるようなログは、近いところで言うと例えばkibanaで楽しく分析やML的な手法で検知できるのだと思います。

ただ、javaスタックトレースなど、明らかに異常が発生して今まで出なかったようなエラーログが1GB吐き出された...のような場合に、膨大なログテキストの中から、まずは注目すべき箇所をあぶり出すにあたって、マッチングのクエリを駆使するとログを読み解く手がかりが得られるのではと考えています。

時系列分析の前のPoCoC

PoCoCはこの場限りの造語です。PoC活動自体のProofです。

前項で言っていることと近い話で、例えばkibanaで時系列分析(のPoC)をする前に、雑にデータをインデックスに取り込んでおき、ここまであげたような検索エンジンの使い方等を組み合わせて、そもそもどんなログフォーマットやデータ内容が多数決的にありそうなのか、kibanaへの取り込み設計の方向性を見定める...というようなところに使えないかという考えでここにあげてみました。

four-letter wordのチェック

他と毛色がやや違いますが、作成した文書でNGワードを使用していないかのチェック...というようなケースにおいて、RDBでの対応表みたいなものの厳格な管理をしなくとも、NGワードNGワードの表記揺れのワードを一つのドキュメントに気まぐれにつっこんでやり、このドキュメントに対して、OR検索をしたり、More Like Thisクエリーで検索をして、ヒットするかどうかで、ほどほどのNGワードチェックができると考えています。

さいごに

実のところ、この類の調査がどこまでリアルな例かはともかく、例えばとある検索サイトやサイト内検索では(仕様のシンプル化の方針などにより)あえて使用しなかったElasticsearchのリッチな検索クエリの使い方を掘り下げる良い思考実験となりました。 (あくまで私にとっては...ですが。)

また、上記の例では例のための例ということと私のスキルの都合で、アナライザーのプラグインを自作する領域までは踏み込みませんでしたが、軽微な文字変換のプラグインを自作すると、文字どおりプラグイン的に、少々気の利いたgrepパターンを積み上げていくこともできそうです。

このブログの片隅でもまとめることで、Elasticsearchの素晴らしい機能をしゃぶりつくせる小さなきっかけとなるようなものがあれば、改めて書き出して見たいとおもいます。

以上

*1:分かりやすいかどうかの議論はあると思いますが、各種検索条件に対応させるためにパースできるようなシンタックスとして成り立たせる必要があるので現在の複雑さ(?)は最低限必要な範囲だと思います。

*2:このブログは表記揺れがめちゃくちゃ多いのは自覚しております。

*3: ㋐ |㋑|...みたいなやつ。

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)の合計

kuromojiプラグインのkuromoji_part_of_speechのspeechの一覧

私はどちらかといえば、Elasticsearchのkuromojiプラグインのkuromoji_part_of_speech についてはデフォルトのままで(つまり、stoptagsの設定は特に行わない)で良いのではないかと考えている派です。

が、当然ですが、案件ややりたいことによる訳で、なにかとコンパクトにするために名詞だけでいいやといった場合には、除外の設定を行うことになると思います。

この時、品詞の除外の設定は、除外するものを全て並べる必要があるのと、設定上は、名詞でいうと30種類サブ分類にあたるものがあるので、最初の一発とはいえ、多少試行錯誤が必要な場合は少し煩わしいです。

ということで、

github.com

から抜き出した、kuromojiのデフォルトのstoptagsの設定を俯瞰しやすいように表にまとめました。

また、ついでに、名詞を残すとしたら、動詞・形容詞・副詞といった助詞等のデフォルト除外および名詞を除外したものを残すとしたらでチェックをつけました。

上記のgithubの「#△」以外、「#」を含まない行を抜き出せばすぐ抜き出せる範囲ですが、Elasticsearchのsettings.analysis.filter.your_filter_name.stoptagsに貼り付ける手間を少し軽減できるかなと思って書き出してみました。

コピペ間違いなどありましたらごめんなさい。

項番 大分類 (A)デフォルト除外 (B)名詞のみ (C)デフォルト除外と名詞除外 フィルター
1 その他 x x x "その他-間投",
2 その他 "その他",
3 その他 x x x "フィラー",
4 その他 x "感動詞",
5 記号 "記号-アルファベット",
6 記号 x x x "記号-一般",
7 記号 x x x "記号-括弧開",
8 記号 x x x "記号-括弧閉",
9 記号 x x x "記号-句点",
10 記号 x x x "記号-空白",
11 記号 x x x "記号-読点",
12 記号 x x x "記号",
13 形容詞 x "形容詞-自立",
14 形容詞 x "形容詞-接尾",
15 形容詞 x "形容詞-非自立",
16 形容詞 x "形容詞",
17 語断片 x "語断片",
18 助詞 x x x "助詞-格助詞-一般",
19 助詞 x x x "助詞-格助詞-引用",
20 助詞 x x x "助詞-格助詞-連語",
項番 大分類 デフォルト除外 名詞のみ デフォルト除外と名詞除外 フィルター
21 助詞 x x x "助詞-格助詞",
22 助詞 x x x "助詞-間投助詞",
23 助詞 x x x "助詞-係助詞",
24 助詞 x x x "助詞-終助詞",
25 助詞 x x x "助詞-接続助詞",
26 助詞 x x x "助詞-特殊",
27 助詞 x x x "助詞-副詞化",
28 助詞 x x x "助詞-副助詞",
29 助詞 x x x "助詞-副助詞/並立助詞/終助詞",
30 助詞 x x x "助詞-並立助詞",
31 助詞 x x x "助詞-連体化",
32 助詞 x x x "助詞",
33 助動詞 x x x "助動詞",
34 接続詞 x x x "接続詞",
35 接頭詞 x "接頭詞-形容詞接続",
36 接頭詞 x "接頭詞-数接続",
37 接頭詞 x "接頭詞-動詞接続",
38 接頭詞 x "接頭詞-名詞接続",
39 接頭詞 x "接頭詞",
40 動詞 x "動詞-自立",
項番 大分類 デフォルト除外 名詞のみ デフォルト除外と名詞除外 フィルター
41 動詞 x "動詞-接尾",
42 動詞 x "動詞-非自立",
43 動詞 x "動詞",
44 非言語音 x x x "非言語音",
45 副詞 x "副詞-一般",
46 副詞 x "副詞-助詞類接続",
47 副詞 x "副詞",
48 未知語 "未知語",
49 名詞 x "名詞-サ変接続",
50 名詞 x "名詞-ナイ形容詞語幹",
51 名詞 x "名詞-一般",
52 名詞 x "名詞-引用文字列",
53 名詞 x "名詞-形容動詞語幹",
54 名詞 x "名詞-固有名詞-一般",
55 名詞 x "名詞-固有名詞-人名-一般",
56 名詞 x "名詞-固有名詞-人名-姓",
57 名詞 x "名詞-固有名詞-人名-名",
58 名詞 x "名詞-固有名詞-人名",
59 名詞 x "名詞-固有名詞-組織",
60 名詞 x "名詞-固有名詞-地域-一般",
項番 大分類 デフォルト除外 名詞のみ デフォルト除外と名詞除外 フィルター
61 名詞 x "名詞-固有名詞-地域-国",
62 名詞 x "名詞-固有名詞-地域",
63 名詞 x "名詞-固有名詞",
64 名詞 x "名詞-数",
65 名詞 x "名詞-接続詞的",
66 名詞 x "名詞-接尾-サ変接続",
67 名詞 x "名詞-接尾-一般",
68 名詞 x "名詞-接尾-形容動詞語幹",
69 名詞 x "名詞-接尾-助数詞",
70 名詞 x "名詞-接尾-助動詞語幹",
71 名詞 x "名詞-接尾-人名",
72 名詞 x "名詞-接尾-地域",
73 名詞 x "名詞-接尾-特殊",
74 名詞 x "名詞-接尾-副詞可能",
75 名詞 x "名詞-接尾",
76 名詞 x "名詞-代名詞-一般",
77 名詞 x "名詞-代名詞-縮約",
78 名詞 x "名詞-代名詞",
79 名詞 x "名詞-動詞非自立的",
80 名詞 x "名詞-特殊-助動詞語幹",
81 名詞 x "名詞-特殊",
82 名詞 x "名詞-非自立-一般",
83 名詞 x "名詞-非自立-形容動詞語幹",
84 名詞 x "名詞-非自立-助動詞語幹",
85 名詞 x "名詞-非自立-副詞可能",
86 名詞 x "名詞-非自立",
87 名詞 x "名詞-副詞可能",
88 名詞 x "名詞",
89 連体詞 x x "連体詞",

(B) 名詞のみ(コピペ用あえての1行バージョン)

"stoptags": [
"その他-間投", "フィラー", "感動詞",    "記号-一般",    "記号-括弧開", "記号-括弧閉", "記号-句点",    "記号-空白",    "記号-読点",    "記号",   "形容詞-自立", "形容詞-接尾", "形容詞-非自立",  "形容詞",    "語断片",    "助詞-格助詞-一般",  "助詞-格助詞-引用",  "助詞-格助詞-連語",  "助詞-格助詞", "助詞-間投助詞",  "助詞-係助詞", "助詞-終助詞", "助詞-接続助詞",  "助詞-特殊",    "助詞-副詞化", "助詞-副助詞", "助詞-副助詞/並立助詞/終助詞",  "助詞-並立助詞",  "助詞-連体化", "助詞",   "助動詞",    "接続詞",    "接頭詞-形容詞接続",    "接頭詞-数接続",  "接頭詞-動詞接続",   "接頭詞-名詞接続",   "接頭詞",    "動詞-自立",    "動詞-接尾",    "動詞-非自立", "動詞",   "非言語音", "副詞-一般",    "副詞-助詞類接続",   "副詞",   "連体詞"
]

(C)デフォルト除外と名詞除外

"stoptags":[
"その他-間投", "フィラー", "記号-一般",    "記号-括弧開", "記号-括弧閉", "記号-句点",    "記号-空白",    "記号-読点",    "記号",   "助詞-格助詞-一般",  "助詞-格助詞-引用",  "助詞-格助詞-連語",  "助詞-格助詞", "助詞-間投助詞",  "助詞-係助詞", "助詞-終助詞", "助詞-接続助詞",  "助詞-特殊",    "助詞-副詞化", "助詞-副助詞", "助詞-副助詞/並立助詞/終助詞",  "助詞-並立助詞",  "助詞-連体化", "助詞",   "助動詞",    "接続詞",    "非言語音", "名詞-サ変接続",  "名詞-ナイ形容詞語幹", "名詞-一般",    "名詞-引用文字列",   "名詞-形容動詞語幹",    "名詞-固有名詞-一般",   "名詞-固有名詞-人名-一般",    "名詞-固有名詞-人名-姓",   "名詞-固有名詞-人名-名",   "名詞-固有名詞-人名",   "名詞-固有名詞-組織",   "名詞-固有名詞-地域-一般",    "名詞-固有名詞-地域-国",   "名詞-固有名詞-地域",   "名詞-固有名詞",  "名詞-数",   "名詞-接続詞的",  "名詞-接尾-サ変接続",   "名詞-接尾-一般", "名詞-接尾-形容動詞語幹", "名詞-接尾-助数詞",  "名詞-接尾-助動詞語幹",    "名詞-接尾-人名", "名詞-接尾-地域", "名詞-接尾-特殊", "名詞-接尾-副詞可能",   "名詞-接尾",    "名詞-代名詞-一般",  "名詞-代名詞-縮約",  "名詞-代名詞", "名詞-動詞非自立的",    "名詞-特殊-助動詞語幹",    "名詞-特殊",    "名詞-非自立-一般",  "名詞-非自立-形容動詞語幹",  "名詞-非自立-助動詞語幹", "名詞-非自立-副詞可能",    "名詞-非自立", "名詞-副詞可能",  "名詞",   "連体詞"
]

参考リンク

参考にさせていただきました。

qiita.com

びじフレ

MBA的な世界で出てくる様々なビジネスフレームワークですが、どうもすっきりしません。

私が成功した経営者や経営者でなくともMBAを修めた立場でであれば良いのですが、残念ながらそうではないので小声で言うと、多分本来の経営においてはさほど役に立たなそうな気がします。

ただ、本当に役立たないかというとそうでもなくて、これまた多分ですが、事後分析や、逆に形式は問わずももともと確固たる信念や言語化するのが難しいアイディアやビジネスプラン、戦略の案がある場合に、それを「点検」する際に参考にするというのが良い使い方のような気がします。

また、これらのビジネスフレームワークですが、ことをややこしくしているのが使い所がはっきりしないことのように思います。

何かの試験問題や演習ならともかく、現場で、さて3C分析をしてみましょうと言われて、3つの空欄を埋めようとして手が止まりませんか。

手が止まるのはいろいろ理由があると思いますが、ひとつにはシンプルに目の付け所とそれを可視化する方法として使おうと思っているフレームワークが適していない、またひとつはそのフレームワークを使うにあたって事前に調査しておいた方が良い事項や素材が揃っていないというようなところがあると思います。

ということで、自分の中での咀嚼もかねて、有名フレームワークの使うタイミングを整理してみました。

ただただ一覧が出て来るのも辛いし自分でもおもしろくないので、(怪しい)業務改善コンサルタントとして召喚され、主要なフレームワークを並び立てるだけで現状の問題やビジネス上の課題をあきらかにするとともに、新たな(ただし、根拠に足る)事業戦略とともに実際に機能するプランを立案する... という夢のようなストーリーでフレームワーク間の順々に適用するとしてみました。

f:id:azotar:20181220010010p:plain f:id:azotar:20181220010015p:plain f:id:azotar:20181220010023p:plain

多少無理があるところもあると思いますが、適用順序とストーリーがあると、白紙だと記憶しづらいものも記憶に定着するのではという期待をこめております。

1. 財務三表

財務三表は普通にながめて見ましょう。

ここでは深掘りしていませんが、数字で大きさを図る・感じるのは重要だと思います。 (私がこれらの情報をもとに目利きができる・得意だとはひとことも申しておりません。)

他にも、生産性や効率性、収益性、安全性といったことを確認しましょう。

確認の際は、同業他社のデータがあると望ましい... というか同業他社と比べるしか、素人にはこの値がどれほどのものか分からないので注意しましょう。

2. バリューチェーン

バリューチェーンを確認しましょう。

価値を生み出しているのはどの工程か。貢献しているのはどこか。

主活動に目がいきがちですが、支援活動も(分析中毒にならない範囲で)見逃さないで。

チェーンのつながりこそが価値の源泉になっているかもしれません。

というかバリューチェーン分析の目的は本来そちらかな。 (業務フローなどを描いて分析すると、バリューがそっちのけになる場合があるように思います。分析として使うといいながら、あなたの会社は(うちの会社は)このバリューチェーンこそがポイントです!と言い切るための挿絵としての使い方がバリューチェーンの本質のような気がしてきました。)

ちなみに、「支援活動」の方が明らかにボリュームが大きいような営みになっている場合は大企業病になっている可能性があります。

3. ビジネスモデル

ビジネスモデルがどのタイプか確認しましょう。

あえて付け焼き刃ですが、ビジネスモデルの詳細な分析もさることながら、あえてカタログ的にものを見て、どの勝ちパターンを志向しているのかを言語化するというのが効率的な場合があるように思います。

https://www.amazon.co.jp/dp/4798146889 ビジネスモデルナビゲーター

4. 3C

3Cを可視化しましょう。

なお、どこかで見たことの受け売りですが、過去と現在の3Cを書き出して「変化」をみることで、3Cフレームで本来あぶり出したかったことが見える化されます。

逆に言うと、よくある3つの丸のベン図みたいな単品の3Cだと、それが正しい分類だったとしても、これは本当にCompanyの分類で良いのかねみたいなズレた話になるような気がしますので、3Cの本来の目的にそうように「変化」を示すのが有効だと思います。

3Cをまとめていると自ずと、その情報収集や考察の流れで、VRIOらしきものがおぼろげながら見えてきます(かもしれません)。

5. SWOT

ここではそれを横目に、SWOTの強み弱み機会脅威を埋めてみます。

戦略立案というよりは、まず現状分析です。

強みに関連するのはVRIOですね。

6. PEST

SWOTの機会・脅威に関するところでは、PESTの観点でものを見てみると良いと思います。

7. クロスSWOT

SWOTを戦略が噛み合うように、クロスSWOTに組み直します。

ここで、先の3Cをふりかえりつつ、次のような古典をあたまに浮かべてみましょう。

整合性のチェックやうまくいきそうなクロスSWOTになっているか確認するのが良いと思います。

8. ポーターの3つの基本戦略

コストリーダーシップ、差別化、集中に照らし合わせてみる

9. コトラーの競争地位戦略

リーダー、チャレンジャー、ニッチャー、フォロワーそれぞれと、 フルライン戦略、差別化、隙間製品の徹底、リーダーのマネ と言われる定石との兼ね合い

10. PPM

続いて、具体的な事業の展開を考えます。

現在の事業や製品を棚卸しして、どのポジションなのか確認します。

拡大か撤退か、はたまた重要な空白領域に新たな製品を投入するのか思考します。

11. イノベーター理論、プロダクトライフサイクル

PPMで市場成長率とシェアのどの位置に位置するかは、感覚的にもわかるかもしれませんが、イノベーター理論やプロダクトライフサイクルのフレームを知っていると定性的なりに多少客観視できるかもしれません。

12. アンゾフの事業マトリクス

事業の拡大については、製品の市場開拓なのか多角化なのか、市場において顧客への浸透なのか新商品開発なのか、これらの組み合わせなのか相性を考えて行動しましょう。

なお、図では特にふれていませんが、ブルーオーシャン戦略を念頭におきましょう。

13. STP分析

具体的な戦術に落とし込んでいきます。

が、ここでもまた分析です(笑)。

S: セグメンテーション

T: ターゲッティング

P: ポジショニング

14. 顧客グリッド

前項のターゲッティングは、顧客グリッドを使って分類するとともに、定石にしたがってアプローチを決めると良いでしょう。

顧客グリッドは、顧客収益性とロイヤリティの軸で顧客タイプを6種に分類したものです。

https://www.emotion-tech.co.jp/resource/2015/the-three-sacred-treasures

15. 再びコトラーとポーターの2大巨頭 〜コストリーダーシップ

市場開拓や新商品開発の方向性ではなく、既存製品を重視する場合、ポーターの3つの基本戦略のコストリーダーシップに注目します。

ここでは、効率化の方向性になるので、最近だと7つの無駄(加工、在庫、作りすぎ、手持ち、動作、運搬、不良のムダ)に注目することになりそうですが、少しレイヤが低くなるかもしれません。

16. 差別化 →4C(買い手)、4P(売り手)

差別化戦略を掘り下げる場合は、4C、4P分析を行います。

歴史的には、4Pから4Cという流れのようですが、分析が目的ではなくて、差別化戦略の点検という意味では、両方使えば良いような気がします。

また、VRIOが成立しているか/成立しうるか確認しましょう。

この時、競争優位の源泉(差別化、コスト優位、イノベーション)が確保できそうかも点検した方が良いと思います。

17. AIDMA・AISAS

4P、4Cの流通(Place)、販促(Promotion)には具体的なPR活動もさることながら、AIDMA、AISASを意識しましょう。

せまいところで戦っていませんか?

Promotionという意味では、顧客グリッドを振り返るのも有効でしょう。

つかれてきたのでここまで

続きとしては、

ビジネスモデルキャンバス、リーンキャンバスのフレームワーク

経営資源のヒトモノカネ、時間、情報。

経営視点に話を戻すなら、バランススコアカード(BSC)での財務、顧客、業務プロセス、学習と成長... と話は続きます。

が、疲れてきましたのでここまでにします*1

*1:実際は、この辺から先にストーリーづけるのは無理が出てきたというのもあります。

続:ナレッジインデックスの有効性のゆるい確認、 続:検索練習用のサンプルデータのインポート(ElasticsearchのSignificant text )

はじめに

この記事は、

itdepends.hateblo.jp

のおおよその続編。

また、

itdepends.hateblo.jp

itdepends.hateblo.jp

の親戚記事です。

一言で言うと、「ナレッジインデックス(仮称)」というものを考えて、こいつの有効性を(実証は難しいものの)いくつか試してみるというものになります。

使用したElasticsearchのバージョンは6.4です。

アンサイクロペディアwikipediaから「東京関係」の解説記事を抜き出した上、Elasticsearchにまるっと放り込んで(これが別記事でナレッジインデックスと呼んでいるもの)、東京の地理に関する検索語(例: 上野)に関連するワードがが得られるかやって見るというものです。 (ここだけ見ると、Significant t... の当たり前の機能じゃん...ですが。)

wikipediaのデータをElasticsearchにインポートする正攻法は他のブログで有用な記事を皆さんが作成されています。この記事はそれらと少し趣がことなりますので、Google検索等で流れついた方は、ご容赦ください。また、自分の主張を卑下するわけではありませんが、この記事で言うナレッジインデックス(仮称)は少々名前負けしています。

アンサイクロペディアから東京エリアナレッジインデックス(仮称)生成

1) mapping設定します。

PUT your_index5
{
  "mappings": {
    "_doc": {
      "dynamic_templates": [
        {
          "dtmpl": {
            "match_mapping_type": "*",
            "match": "*_ja",
            "mapping": {
              "analyzer": "my_kuromoji",
              "fielddata":true
            }
          }
        }
      ]
    }
  },
  "settings": {
    "analysis": {
      "analyzer": {
        "my_kuromoji": {
          "type": "custom",
          "tokenizer": "kuromoji_tokenizer",
          "mode": "search",
          "char_filter": [
            "icu_normalizer",
            "kuromoji_iteration_mark"
          ],
          "filter": [
            "kuromoji_baseform",
            "kuromoji_part_of_speech",
            "ja_stop",
            "lowercase",
            "kuromoji_number",
            "kuromoji_stemmer"
          ]
        }
      }
    }
  }
}


2) アンサイクロペディアのデータをダウンロードします。

http://download.uncyclomedia.org/

http://download.uncyclomedia.org/ja-wiki.xml.gz

展開すると、12Gぐらいありました。

3) indexに入れるための、東京に関するデータを抽出します。

データを抜き出すためのツールを作成しました。

この、

# encoding: utf-8

# wiki_simple_ext.rb : 

#   アンサイクロペディアのデータの地域の説明はおおよそ次のようなXMLのスキーマになっているようだ。
#   なので目的の情報を持ってそうな、*印部分の要素を1行ずつ処理する。
#
# * page
# * title
# *   revision
# *      {東京} もしくは {XXX}
#     revision
#        {東京} もしくは {XXX}
#     ...

entry = ""
tokyo = false
revision = 0
STDIN.each_line do |l|
  if l.match(/<page>/)  then
    puts entry + "</page>" if tokyo == true
    entry = ""
    tokyo = false
    revision = 0
  end

  revision = revision + 1 if l.match(/<revision>/)
  entry = entry + l if revision <= 1
  tokyo = true if l.match(/\{\{(東京|東京都区部|東京都の鉄道駅|東京都の自治体|大阪)\}\}/)
end

puts entry + "</page>" if tokyo == true

を wiki_simple_ext.rbとして、

cat ja-wiki.xml | ruby wiki_simple_ext.rb  > ja-wiki-tokyo.xml

で、ja-wiki-tokyo.xmlを出力して*1

 export META='{"index":{}}'; cat ja-wiki-tokyo.xml | tr '"' ' ' | tr '\n' ' ' | tr '{' ' ' | tr '}' ' ' | sed 's/<\/page>/<\/page>"\'$'\n/g' |  sed 's/<title>/"title_ja":"/' | sed 's/<\/title>/","content_kw":"/g' | sed 's/<[^>]*>//g' | sed '/^ *$/d' |  ruby -nle 'puts ENV["META"] + "\n{" + $_ + "}"'   > ja_wiki_tokyo.json

で、バルクロードに向いた次の形式のJSONファイルにしてやります。

{"index":{}}
{       "title_ja":"葛飾区", "content_ja":"XXXXXXXXXX" }

4) バルクロード

ja_wiki_tokyo.json を次のコマンドでバルクロードします。 (今気づきましたが、アドホック手順とはいえ、ファイル名に規則性がなくて分かりづらいですね。)

 curl -H "Content-type: application/x-ndjson" -X POST localhost:9200/your_index5/_doc/_bulk?refresh --data-binary  @ja_wiki_tokyo.json

取り漏れが何件かあったようですが、ひとまずインポートできました。

5) significant_text*2で検索してみます。


POST /your_index5/_search
{
  "size": 0, 
  "query": {
    "match": {
      "content_ja": {
        "query": "上野",
        "operator": "and"
      }
    }
  },
  "aggs": {
    "titlegroup": {
      "terms": {
        "field": "title_ja"
      },
      "aggs": {
        "tokuchougo": {
          "significant_text": {
            "field": "content_ja"
          }
        }
      }
    }
  }
}

返ってきました↓。

          "key": "上野",   
          "bg_count": 14    
          "key": "東北",  
          "bg_count": 7 
          "key": "ターミナル", 
          "bg_count": 5 
          "key": "東北新幹線", 
          "bg_count": 4 
          "key": "東北本線",    
          "bg_count": 4 
          "key": "新幹線",   
          "bg_count": 4 
          "key": "常磐線",   
          "bg_count": 5 
          "key": "常磐",  
          "bg_count": 5 
          "key": "止める",   
          "bg_count": 3 
          "key": "起点",  
          "bg_count": 3 
          "key": "京成電鉄",    
          "bg_count": 3 
          "key": "何者",  
          "bg_count": 3 
          "key": "運転",  
          "bg_count": 3 
          "key": "京浜東北",    
          "bg_count": 3 
          "key": "本線",  
          "bg_count": 8 
          "key": "列車",  
          "bg_count": 6 
          "key": "京浜",  
          "bg_count": 4 
          "key": "御徒",  
          "bg_count": 4 
          "key": "始発駅",   
          "bg_count": 4 
          "key": "更に",  
          "bg_count": 4 

気持ちは分からんでもないが.... というところです。

ちなみに、アンサイクロペディアをチョイスしたのは、「危険」や「眠らない街」でSignificant_textで検索して「新宿」がヒットしたりしないかな〜、だと面白いな〜と考えたからですが、そこまで都合はよくありませんでした。

一応aggsをネストしたものもやってみましたが、結果は略します。(このaggsのネストに深い意図はないです。)

POST /your_index5/_search
{
  "size": 0, 
  "query": {
    "match": {
      "content_ja": {
        "query": "上野",
        "operator": "and"
      }
    }
  },
  "aggs": {
    "titlegroup": {
      "terms": {
        "field": "title_ja"
      },
      "aggs": {
        "tokuchougo": {
          "significant_text": {
            "field": "content_ja"
          }
        }
      }
    }
  }
}

wikipediaから東京エリアナレッジインデックス(仮称)生成

なんとなくとっつきやすかったのでアンサイクロペディアの方を先にやりましたが、最初から、より体系的に記述されているであろうwikipediaでよかったような気がしてきました...

多分同じようなフォーマットと思われるので(実際、今回やりたいことの範囲ではおおよそ同じスキーマ形式でした)同じ手順でやります。

1) wikipediaデータのダウンロード

https://dumps.wikimedia.org/jawiki/

https://dumps.wikimedia.org/jawiki/20181201/jawiki-20181201-pages-meta-current.xml.bz2

展開すると16GBぐらいでした。

2) wikipediaのデータの東京関連データ抽出

冒頭の絞り込みrubyツールをwikipedia用に見直しました。こいつにwikipediaxmlを食らわせて、 jawiki-mini.xmlというファイルを作ります。

# encoding: utf-8

# * page
# * title
# *   revision
# *      [Category:東京都の特別区...
#     revision
#        [Category:東京都の特別区...
#     ...

entry = ""
tokyo = false
revision = 0
STDIN.each_line do |l|
  if l.match(/<page>/)  then
    puts entry + "</page>" if tokyo == true
    entry = ""
    tokyo = false
    revision = 0
  end

  revision = revision + 1 if l.match(/<revision>/)
  entry = entry + l if revision <= 1
  tokyo = true if l.match(/\[Category:(東京都の特別区|東京都の市町村|東京都交通局の鉄道駅|..区の鉄道駅|...区の鉄道駅)\]/)
end

puts entry + "</page>" if tokyo == true

中途半端に同じようなコードを量産してしまいました。これなら有志の方が作成されているwikipediaXMLをパースするツール等を利用させてもらえばよかったようにも思いました。が、ひとまず続けます。

バルクロード用のJSONファイルを作ります。

export META='{"_____":{}}'; cat jawiki-mini.xml | tr '"' ' ' | tr '\n' ' ' | tr '{' ' ' | tr '}' ' ' | sed 's/<\/page>/<\/page>"\'$'\n/g' |  sed 's/<title>/"@@@":"/' | sed 's/<\/title>/","%%%":"/g' | sed 's/<[^>]*>//g' | sed '/^ *$/d' |  ruby -nle 'puts ENV["META"] + "\n{" + $_ + "}"' | sed 's/Category:/...:/g' | tr -d '[0-9A-Za-z]' | sed 's/@@@/title_ja/g' | sed 's/%%%/content_ja/g' | sed 's/_____/index/g'

ここまで書いて、ひょっとして簡易なXMLぐらいなら、サクッと読み込んでくれるkibanaとかlogstashとかの仕組みがあるのではと思いましたが、実のところよく知らないので、この方法に徹しました。

つづきます。

4) バルクロードします

JSONファイルをバルクロードします。

curl -H "Content-type: application/x-ndjson" -X POST localhost:9200/your_index_spot/your_type_spot/_bulk?refresh --data-binary  @jawiki-mini.json 

5) significant_textで検索してみます。

アンサイクロペディアの例と同じで、「上野」での検索です。

返ってきたのはコレ↓

          "key": "上野",
          "bg_count": 130
          "key": "京成",
          "bg_count": 102
          "key": "台東",
          "bg_count": 47
          "key": "御徒",
          "bg_count": 32
          "key": "御徒町",
          "bg_count": 29
          "key": "成田",
          "bg_count": 82
          "key": "千住",
          "bg_count": 93
          "key": "都営",
          "bg_count": 363
          "key": "上野公園",
          "bg_count": 23
          "key": "成田空港",
          "bg_count": 67
          "key": "秋葉原",
          "bg_count": 51
          "key": "集計",
          "bg_count": 64
          "key": "浅草",
          "bg_count": 94
          "key": "区内",
          "bg_count": 104
          "key": "葉",
          "bg_count": 60
          "key": "日間",
          "bg_count": 55
          "key": "江戸",
          "bg_count": 86
          "key": "恩賜",
          "bg_count": 23
          "key": "松坂屋",
          "bg_count": 21
          "key": "千葉",
          "bg_count": 46

悪くない気がする。

結構「上野」感の雰囲がでているかもしれません。

significant .... はそういうものだから当たり前のところもありますが、一文字のものは取り除くとしたらそのまま使えるのでは?と思わせるところがありますね。

linuxコマンドラインスクリプトの参考にしたリンク

http://taichiw.hatenablog.com/entry/2012/04/06/102650

https://qiita.com/kkdd/items/725e53572bc69e4b51b7

https://orebibou.com/2015/07/sedコマンドで覚えておきたい使い方12個/

アンサイクロペディア

https://ansaikuropedia.org/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8

wikipedia

https://dumps.wikimedia.org/backup-index.html

http://koiroha.blogspot.com/2017/04/how-to-get-wikipedia-dump-as-plaintext.html

http://nonbiri-tereka.hatenablog.com/entry/2015/10/12/104800

参考文献リンク

http://komaken.me/blog/2018/01/16/elasticsearch-aggregationに任意の値を含める/ [Elasticsearch] aggregationに任意の値を含めるhttps://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns

*1:※ ja-wiki.xmlアンサイクロペディアの全件データです。

*2:significant_termsと言いましたが、実際はtextにしました

別立てのナレッジインデックス(検索UX向上のためのオレオレ考察 (Elasticsearch))

はじめに

弱者のエンジニアリング、細腕エンジニアとして、検索サイトのUX向上に向けて、せっかくある記事ページをナレッジインデックスとして検索に活かそうという論を主張します。 (Elasticsearchカテゴリとしたのは、Elasticsearchの機能にインスパイアされたからです。キーワード的には、Significant terms、Significant textあたりです。ただ、このページには厳密なテック情報はありませぬ。ごめんなさい。)

主張の要約(ナレッジインデックスを用いたプレ検索による検索語選定)

2018年現在においては、検索サイトはシンプルな検索UXを提供するべきかと思われます。

それはそれとしながらも、多くの検索サイトに当てはまる特徴を活用して、検索の前にもう一捻りしてみる「プレ検索による検索語選定(仮称)」を提唱したいと思います。

着目点

提唱(?)メソッドの説明の前に、私が「多くの検索サイトに当てはまる特徴」と考えたものを述べてみます。

その特徴とは、検索サイトは検索サイトでありながらコンバージョン対象のコンテンツ(ECなら商品ページ)と比類できるぐらい記事ページがしめる割合が増えているということです。

と言うのも、LPOやSEOSEM的な面ももちろんとして、そもそも最近の検索サイトでは、集客のために記事コンテンツやガイダンスコンテンツを用意して、自サイトの価値をアップしています。

このような記事ページはその目的からすると当たり前ですが、あるドメインのあるテーマに関して、周辺知識を含めて洗練された知識が集約されたページになっている傾向があります。

アウトドア製品ECサイトの商品ページコンテンツに対して、レジャーや新スポットの紹介あるいはもっと具体的にあるカテゴリのオススメ商品の選び方ポイントなどを初心者、上級者それぞれのターゲットに合わせて編集した記事ページが用意されています。

ここには、商品ページの内容がスペックを中心としたスモールワードや専門用語が中心に対し、ビッグワードやミドルワードあるいはユーザ目線の単語とともに、幅のあるワードから商品ページに記載のワードへと繋がるようなテキストが展開されています。

多少盛った話もあるでしょうし、中にはネイティブ広告色が強いものものもあり、バイアスもかかっているものもあるかもしれません。ただし、それでもユーザーに訴えかけるようにテーマ選定し、編集されているコンテンツと言えると思います。 (繰り返しますが、ページが作られている目的からいえば当たり前かもしれません。)

クソミソ方式:極論気味のお話

前項では、記事ページってこうだよねと棚卸ししてみました。もう一度言うと、記事ページは検索エンジンの適合度などの観点からするとそのドメインに特徴的なワードの宝庫だと思われます。

この特徴を考えると、という話になりますが、検索サイトのユーザ層次第ではあるものの、私なぞは実際のところ、例えば、ECでの商品サイトであれば、商品ページも記事ページもくそみそ一緒にして検索してしまえ派です。

もちろん、記事ページには商品検索結果をサイドバーにレコメンドしたり、ElasticsearchでいうところのMore Like Thisクエリーをかまして、商品ページへの誘導 をつけても良いでしょう。

クソもみそもと言いましたが、(ドキュメントのフィールド設計が不自然にならない範囲で)検索機能を統合するだけで、一度記事ページを経由した方が良いような初心者ユーザは記事ページがヒットするでしょうから、そこで予備知識をつけつつ、レコメンドの商品ページに誘われるのは自然でしょう。一方、上級者は結果的に商品ページが検索結果に優先されて表示されることになるでしょう。つまり、ある程度住み分けされつつ、上級者だけど適切な検索語がわからないなという時にも対応できる、一挙両得なものになりうる、しかもサイト側もさほど手間をかけずに...という考えです。

ナレッジインデックス(仮称)方式

f:id:azotar:20181219222432p:plain

言っておきながらですが、クソミソ方式は少し極論なのは事実です。

実際のところは、検索サイトのメインとなる商品ページ群と記事ページの間には一線を引いておきたいというポリシーのサイトも多いでしょう。

とはいえ、せっかくの記事ページを有効活用することはできないでしょうかと貧乏性な私は思います。

そんな私にとって、ElasticsearchのSignificant Terms/ Significant Text(やTerms vector)との出会いはラッキーでした(なんか変な通販みたいな言い方になっている(苦笑))。

こやつらは、雑に言うと、検索結果のドキュメント内に特徴的なワードがあればそいつらを返してくれます。

ここで、記事ページ自体を通常の変換マップ表であるシソーラスより上位の「検索対象のドメインに関するナレッジ」とみなしたインデックス(ナレッジインデックス)を考えます。

ナレッジインデックスは、記事ページそのものの検索インデックスとしても使えますがここではひとまず別物とします。また、商品ページのものとも別のインデックスです。

このナレッジインデックスは、商品ページのインデックスを検索する前のプレ検索(検索語自体で検索してSignificant Text等で関連語を検索することによる、検索語のリファイン(あるいは幅出し))、もしくは検索したあとに0件ヒットの場合の「もしかして検索」とアナザーバージョンとして使えるのではと考えています。

えてして精度が高いとはかぎらないユーザーの検索語を咀嚼した真の検索語を得ることに繋がる期待もあるのではと考えます。

そんなに都合の良いケースばかりでもないでしょう。ただ、機械学習などを使った手法やヘビーなテキストマイニングを行うことなく、... というかそもそものドメイン知識を有するヒトが作成した記事なので、機械学習などの結果として得たいそのモノだったりするように思います。

むしろ私の知識不足・知見不足にすぎず、よくある当たり前の使い方のひとつかもしれません。

Elasticsearchなど、検索結果とファセット(Aggs)をワンパスで取得できる検索エンジンを使う世界感においては、ワンパスでシンプルになった分、ある種の空きリソースを活用して、あえてのナレッジインデックスを別立てにするという案はいかがでしょうか。

つづきます

ほんとにうまく行くんかいというところもありますが、弱エンジニアだからこそ気づけたのではと思う観点でしたので、ひとまず、主張したいことを綴ってみました。

一応続きがあります。 ↓

itdepends.hateblo.jp