OpenSource の Wikiツール Growi の個人的に便利と思う点

はじめに

最近筆者が所属するデータ分析人材の相互扶助コミュニティ*1において、

10人程度のメンバーで集まって分析プロジェクトがスタートした。

これまで、主な情報交換手段はSlackとScrapBoxとTrelloを使っていて、

  • リアルタイムな情報共有
  • (チャットに流れてほしくない)情報の蓄積
  • タスク管理

といった点はカバーできており問題はなかった。

しかし、今回のプロジェクトでは「構造化されたドキュメント管理手段」が必要になり、 いろいろ考えた結果、WikiツールのGrowiを使ってみることにした。

github.com

ここでは、Growiを1週間ぐらい使ってみて感じた「いい点」を挙げたい。

結論的なもの

GrowiはMarkdown形式でドキュメントを書けるWikiツールである。 競合はたくさんあって、実際に全部を比べた訳ではないが

【2020年版】社内wikiツール おすすめ15選(有料・無料) | NotePM

主に以下の点が良いと感じた

  1. 画像をコピペで追加できてラク
  2. 表の作成+追加がラク
  3. UMLなどの図作成+追加がラク

1. 画像をコピペで追加できてラク

f:id:massox:20200713145331g:plain
画像の追加

画像の保存先は、Growiの設定によって異なる。

今回は、GrowiのSaaS版であるGrowi.cloudを利用している。

この場合、デフォルトではGCPのCloud Storageが利用される。(100MBだったか1GBだったか) 筆者の場合は、自前のAWSアカウントのS3バケットに紐づけているのでそちらに保存される。

2. 表の作成+追加がラク

f:id:massox:20200713150602g:plain
表の作成・追加

  • Markdownのテーブル作りは要素数が多い時はちょっとめんどくさい
  • Growiではテーブル用のエディタが用意されているので追加も編集もとてもラク

3. UMLなどの図作成+追加がラク

UMLの追加 with Draw.io

  • Markdownのつらみのもう一つが図を描くのにひと手間必要ということ
  • 普段使いのVSCodeではPlantUMLとかMermaidJSとかと連携させてるけどサクッと書きたい時はめんどくささが勝ってしまう
  • Growiはボタン一つでDraw.ioの編集画面が起動し、サクサクUMLを書ける、そしてボタン一つで保存できる
  • 保存したUMLは(詳細は調べてないけど)エンコーディングされてテキスト情報としてMarkdownに記述される
  • 嬉しいのは、エンコーディングされたテキスト上でもう一度Draw.io起動ボタンを押すと、続きから図を編集できる点である

データサイエンティストの為のDocker入門勉強会#1を実施したので振り返る

私が所属しているデータラーニングギルドというオンラインコミュニティにおいて、「データ系職種の人が最低限知っておきたいDockerのあれこれを学ぶこと」を目的として勉強会を開いた。

本エントリーでは、第一回の勉強会をKPT方式で振り返ろうと思う。

まず、第一回の勉強会をどのように実施したのかを説明し、その中で得た学び(Keep, Problem)と次のアクション(Try)を記述する。

同日Gitの勉強会も実施しました。↓↓

thinker-masso.hatenablog.com

1. 第一回勉強会の内容

どのようにやったか

  • 形式
    • 講義形式(筆者が説明)+実践(ハンズオン)
    • ※オンライン
  • 教材
  • 参加者のDockerに関する前提知識
  • その他
    • 参加者には予め以下の準備を依頼
      • 教材のサイトをさらっと見ておくこと
      • Dockerhubアカウントを作成しておくこと
      • Play with Dockerを動かせるようにしておくこと

何を勉強したのか

  • Dockerにまつわる概念的説明
    • Dockerとは?
    • Hypervisor型仮想化とコンテナ型仮想化の違い
    • コンテナ型仮想化の有り難み
    • Docker Image
    • Docker Container
    • Dockerfile
  • Dockerの基本操作
    • 公開されたイメージを使う
      • docker pull/run (published image name)
      • FROM (published image name) at Dockerfile
  • イメージやコンテナを見る
    • docker image ls
    • docker container ls
  • Dockerの挙動
    • 一度もプルされたことないイメージは docker pull/run 時に適宜ダウンロード+プルされること
    • 以前プルしたことのあるイメージならば、ローカルに保存されたイメージを使う
  • Dockerfileの書き方(FROM/COPY/CMDのみ)

参加者からの講義に関する質問や意見など

  • 質問
    • docker pull ubuntu しかしてないのに docker ruby run するとrubyが実行できるのは何故か?
    • VMを使えばMac上でWindowsを動かせる。Dockerではそういったことはできない?
    • Dockerを勉強する際は、手を動かして理解していったのか?記事を読んでいたらわかってくるものなのか?
  • 意見
    • (筆者)今回の勉強会をどのように感じたか?講義形式ではなくSlack上で非同期に質問受け付けたりの方がよかったのでは?
    • (上記問いかけに対して)今回の勉強会により学習のとっかかりが掴めた気がする。そのおかげでSlack上でも質問を投げやすくなったと感じる。
    • 例えば、コミュニティの共有ノート(Scrapbox)に質問を書き込んでもらい、一定量たまったら解説会を開くなど良いかもしれない
    • Slackだと質疑応答内容が流れていくし、実際にデモで確認できるとなれば分かりやすい

など

2. Keep

  • ライブコーディングでDockerの操作を行ったこと、ハンズオンでDockerを触ってもらったこと
    • 口頭のみで説明した概念的ものを実際にDockerの出力結果として見せることができた
    • 売ったコマンドによって、何がプルされているのかを一緒に出力メッセージを見ながら解説できた
  • 参加者の中から議事録作成者を募集したこと(そして、有難いことに立候補してくれる人がいたこと)
    • 書いていただいた議事録のおかげで、今まさにこの振り返りもスムーズにできている訳だし......

3. Problem

  • 実践する時間が少なかった為、ハンズオンでの質問を多く受けられなかった
    • 個人的には、実際に触ってから生まれる疑問に答える方が学びの手助けとして重要と考えている

4. Try

(Git勉強会のTryと被るけど...)

  • いつでも質問大歓迎の雰囲気作り、場作り
    • データラーニングギルドのSlack内に本勉強会用チャンネルがあるが、
    • 勉強会の実施要領などを通知するだけの場になっている
    • 質問文を考えること自体コストがかかるし、それを勉強会の時間内で全員ができるかといえば難しい
    • さらに、Dockerの操作に関しては、触って覚えた方が早い。Dockerfile作成のベストプラクティスを学ぶ時にまた理論(レイヤーやキャッシュ)に戻れば良い、と個人的に考えている
    • したがって、非同期での質問受付の場を設けることとする。かつ初学者でも質問しやすい空気作りをする。(具体的にどうするかはまだ考え中)

データサイエンティストの為のGit入門勉強会#1を実施したので振り返る

私が所属しているデータラーニングギルドというオンラインコミュニティにおいて、「データ系職種の人が最低限知っておきたいGitのあれこれを学ぶこと」を目的として勉強会を開いた。

本エントリーでは、第一回の勉強会をKPT方式で振り返ろうと思う。

まず、第一回の勉強会をどのように実施したのかを説明し、その中で得た学び(Keep, Problem)と次のアクション(Try)を記述する。

1. 第一回勉強会の内容

どのようにやったか

  • 形式
    • 講義形式(筆者が説明)+実践(ハンズオン)
    • ※オンライン
  • 教材
  • 参加者のGitに関する前提知識
    • 触ったことがない〜業務外で個人的に使ったことがある
  • その他
    • 参加者には予め以下の準備を依頼
      • 教材のサイトをさらっと見ておくこと
      • Gitをインストールしておくこと
      • Githubアカウントを作成しておくこと

何を勉強したのか

  • Gitにまつわる概念的説明
    • バージョン管理とは
    • ローカルリポジトリ、リモートリポジトリとは
    • 分散バージョン管理とは(SVNの違いとか)
    • コミット、プッシュ、プル
    • インデックスとワーキングツリー
  • Gitの基本コマンド
    • git init
    • git status
    • git add
    • git commit (-m "comment")
    • git push
    • git clone
    • git pull

参加者からの講義に関する質問や意見など

  • 質問
    • コミットとプッシュの違いは何か?
    • プッシュとクローンの違いは何か?
    • ローカルで3回コミット後プッシュした場合、リモート側でも3回分の履歴が残るのか?
    • 他の人と競合したらどうするのか?
    • インデックスとワーキングツリーはどう違うのか?
  • 意見
    • SVNとGitの違いがわかってよかった
    • コミットとプッシュの違いがわかった
    • git statusした時のターミナルの出力からいろんな情報が読み取れることがわかった

など

2. Keep

  • 教材サイトの節目節目で質疑応答の時間をとったこと
    • 私の予想していないところで参加者の方から疑問が上がった。早い段階でその疑問点を払拭することができた。
  • ライブコーディングでGitの操作を行ったこと
    • 「今のその部分の作業は何をしたのですか?」のような質問があった
    • 口頭のみで説明した概念的ものを実際にGitの出力結果として見せることができた
  • Git初心者ではない参加者の方にサポートしていただいた(具体的には以下)
    • 私が口頭で説明仕切れなかった部分の補足解説をチャット上でしてくれた
    • 参考サイトのURLを貼ってくれた
  • 参加者の中から議事録作成者を募集したこと(そして、有難いことに立候補してくれる人がいたこと)。議事録を書く方が最も多くの質問をしてくれた

3. Problem

  • 本勉強会の為の説明スライドを用意しなかった
    • 教材サイトの中からピックアップして説明したので、あちらこちら行ったり来たりして、参加者の方は聞きづらかったのではないかと危惧している
  • 第一回勉強会のゴールが明確でなかった
    • この勉強会を終えてどのような状態になることが目標なのか?
    • この講義におけるスコープは何か?
    • など...
  • 実践する時間が少なかった為、ハンズオンでの質問を多く受けられなかった
    • 個人的には、実際に触ってから生まれる疑問に答える方が学びの手助けとして重要と考えている

4. Try

  • 本勉強会の為の説明スライドを作成する
    • 本勉強会のターゲット(データ系職種、Git初心者)向けとしてふさわしいトピックのみをまとめた資料を作成する
    • ただし、Git周辺の概念的な説明文や模式図などはすでに存在している素晴らしい資料を参考にして省エネする
  • いつでも質問大歓迎の雰囲気作り、場作り
    • データラーニングギルドのSlack内に本勉強会用チャンネルがあるが、
    • 勉強会の実施要領などを通知するだけの場になっている
    • 質問文を考えること自体コストがかかるし、それを勉強会の時間内で全員ができるかといえば難しい
    • したがって、非同期での質問受付の場を設けることとする。かつ初学者でも質問しやすい空気作りをする。(具体的にどうするかはまだ考え中)

Pandasを使ってCSV形式のログファイルを解析する時に特定のログメッセージの行だけを抽出する方法

シナリオ

以下のようなCSV形式のバッチ処理のログから、任意のメッセージが記された行をカウントしたい。

log.csv カラム構成

No column type
1 date date
2 time time
3 level string
4 message string

f:id:massox:20200626105840p:plain
あるメッセージが記された行をカウントしたい

方法

  1. 結論
  2. pandas.Series.str.contains() を使うとできそうだ
  3. 試しに動かしてみる
  4. 思わぬ落とし穴―引数regex
  5. 最終的な答え

1. 結論

やりたいことを実現するには、以下のコードを書けば終わり。

import pandas as pd

df_log = pd.read_csv('log.csv')

# 今回の結論
target_str = 'カウントしたいログメッセージ文字列'
df_log_extracted = df_log[df_log['message'].str.contains(target_str, na=False, regex=False)]

print('カウント結果:', df_log_extracted.shape[0])

今回の学び

  • pandas.Series.str.containsメソッドで指定文字列に部分一致する行を抽出できる
  • 引数naで、NaNの行をTrueにするかFalseにするか決める。NaNがある場合、この設定がないとValueErrorになる
  • 引数regexで、指定文字列を正規表現として解釈するか、ただの文字列として解釈するか決める。デフォルトでは正規表現として解釈される。
  • 指定文字列に正規表現として意味を成す記号(例えば「()」など)がある場合、regex引数の明示的な指定が必要になる

2. pandas.Series.str.contains() を使うとできそうだ

「pandas 文字列 部分一致 行抽出」などで検索すると、

高確率でこのメソッドに行き当たるだろう。

pandas.Series.str.contains — pandas 1.0.5 documentation

ざっくり説明すると、

  1. pd.Series.str.contains('TARGET_STR')によりTARGET_STRを含む行をTrue、それ以外をFalseとしたSeriesが生成される
  2. 上記1で取得したBooleanのSeriesをDataFrameのインデクスに与えることでTrueの行だけを抽出できる

といった流れ

めっちゃ簡単やん。Pandas先輩さすがっす。

3. 試しに動かしてみる

テストデータを作って、試してみよう。

まずは、適当なテストデータを作る

import pandas as pd

df_test = pd.DataFrame({'A': [0, 1, 2], 'msg': ['normal', 'normal', 'warning']})
print(df_test)

f:id:massox:20200626130610p:plain
テスト用のテーブル

次に、msgカラムが「normal」である行を抽出する。

部分一致であることを確認する為、あえて検索文字列は「norm」にする。

df_normal = df_test[df_test['msg'].str.contains('norm')] 
print(df_normal)

結果は、この通り

f:id:massox:20200626131020p:plain
msg == normal の行だけ抽出した

4. 思わぬ落とし穴―引数naとregex

実は、これだけでは上手く行かないケースがある。

import pandas as pd
import numpy as np

# NaN, () を含むDataFrameを作る
df_test2 = pd.DataFrame(
    {'A': [0, 1, 2, 3, 4],
    'msg': ['normal', 'normal', 'warning(Code:2)', '', 'warning(Code:1)']}
  )
df_test2 = df_test2.replace('', np.nan)
print(df_test2)

f:id:massox:20200626132921p:plain
デフォルト引数だと失敗してしまうDataFrame

4.1. 引数naの役割を知る

試しに先ほどと同じ処理を実行してみる。

df_normal2 = df_test2[df_test2['msg'].str.contains('norm')] 

以下のような例外が出る。

# ...
ValueError: Cannot mask with non-boolean array containing NA / NaN values

どうやら、NAやNaNを含む、NotBooleanな列はmask処理が出来ないということらしい。

では、NaNを含むようなテーブルにどう対処すればいいかというと……

公式ドキュメントにはちゃんと書かれている。

pandas.Series.str.contains — pandas 1.0.5 documentation

f:id:massox:20200626133417p:plain
pandas.Series.str.contains

今回のケースは、指定した文字列を含むものだけがTrueになってほしい。

したがって、NaNの行はFalse(=抽出しない)という挙動になってもらいたい。

では、これを改善してもう一度チャレンジ

# 引数naをFalseに設定
df_normal2 = df_test2[df_test2['msg'].str.contains('norm', na=False)] 

f:id:massox:20200626133706p:plain
na引数を設定して抽出できた

成功した。

4.2. 引数regexの役割を知る

次は、msgカラムが「warning(Code:1)」の行だけを抽出してみる。

df_warning_c1 = df_test2[df_test2['msg'].str.contains('warning(Code:1)', na=False)] 

すると、こんなWarningが出てくる

UserWarning: This pattern has match groups. To actually get the groups, use str.extract.
  return func(self, *args, **kwargs)

「このパターンだとたくさんのグループにマッチした。

それらのグループを実際に取得するには、str.extractを使え」とのことだ。

「たくさんのグループにマッチした」 という点がよく分からない。

ちなみに、抽出結果のDataFrameは空である。

f:id:massox:20200626134728p:plain

参考になるサイトはこちら。

pandasで特定の文字列を含む行を抽出(完全一致、部分一致) | note.nkmk.me

結論は、指定した第一引数の文字列が正規表現として解釈されていることが問題。

検索文字列に含まれる「()」が問題の箇所だ。

公式のリファレンスを見たら分かるように、デフォルトで第一引数の文字列は正規表現として解釈される。

f:id:massox:20200626134543p:plain
引数regex

今回は、普通の検索ワードとして判断してほしいので、引数regexをFalseに設定すると良さそうだ。

以下のように改善しよう。

df_warning_c1 = df_test2[df_test2['msg'].str.contains('warning(Code:1)', na=False, regex=False)] 

期待通りの挙動を実現することが出来た。

f:id:massox:20200626134838p:plain

5. 最終的な答え

冒頭の結論に書いた通り。

whyから始めるようになったのは何故か?

 

 

サイモン・シネック氏のゴールデンサークル理論は有名だけど

サイモン シネック: 優れたリーダーはどうやって行動を促すか | TED Talk

 

自分なりに、「なぜwhyから始めることが大切であり、多くの人の共感が得られたのか」を考察してみたのでメモしておく。

 

 

注意

本エントリーは、私の妄想であり、根拠となる事実に基づいたものではない。

 

結論

whyの問いかけにより選択肢を比較的精度良く、

満足のできる形で絞る事が出来たから。

 

 

かつてはwhatとhowで事足りたのではないか

 

まず、昔より現在の方があらゆるビジネスシーンで選択肢が増えたと思う。これは客観的事実として取り扱って良いだろう。それほどに自明である。

 

かつて、(具体的にいつとは言えないけど)選択肢が少なかった頃は、

whatドリブンで価値のある問題解決が出来たと推測する。

 

選択肢が少ない理由は、技術的要因でそもそもhowが少ないからやれること(what)も少なかったってパターンもあれば、市場に出回る商品のバラエティが少なかった為に何とか競合の意表を突くようなクリエイティビティあふれる商品(what)を考える必要がなかったってパターンもある、と推測する。

 

この場合、市場競争で大事なのは、howの練度になるのではないだろうか。つまり、作業スピード、製造コストだ。howの練度により、「如何に大量の商品を如何に安く作るか」で競ったのではないだろうか。

 

ある時howの選択肢が飛躍的に増大したのではないか

これまでに何度か技術的なブレークスルーが起きた。小さなものも数えたらキリがないけど、私の記憶に強く残っているのはiPhoneの登場。

 

根拠はよく分からないが、iPhoneの登場によって時代が15年進んだとも言われている。

 

ビジネスシーンの選択肢増加という観点で言えば、

iPhoneの登場により、顧客が商品を目にする、手に取る、購入するタイミングやインターフェイスが変わった点が挙げられそうだ。

 

これにより、マーケティングブランディング、新商品の開発サイクル(速度や鮮度)等において、考慮すべき事項が増えたのではないだろうか。

考慮すべき事項が増えるとは、すなわち、選択肢が増えることに他ならない。

 

選択肢が飛躍的に増えたが、人間が生物学的に進化した訳ではない。人間の認知能力は変わらないまま、選択肢だけが増えた、と言っていいだろう。

 

そこで、人間はアプローチを変えることにした。

 

whyによるフィルタリングでwhatとhowを絞ることにしたら上手いこといったのではないか

whyから始めることで、大きく2つの効果が得られたのではないかと考える。

 

  1. 選択肢を効率よくフィルタリングできた
  2. (個人あるいはチームの)モチベーションを高めることができた

 

1. 選択肢を効率よくフィルタリングできた

 

why, what, how3つを比べると、whyが最も数が少ないと私は思う。

したがって、人間の認知コストを最小に抑えながら、選択肢を効率的に絞る事ができる。

 

一点注意すべきことは、what, why, how の関係性はどれが上位に存在するものでもないということである。あくまでも、現代は「why」から始めると上手く機能するってだけの話だと思う。

 

ではなぜ、上手くいくのか。

 

それは、whyを考えるシーンにこそ、最もクリエイティビティが必要とされるからだ。

 

出来ること・出来そうなこと(what)やその実現方法(how)は、膨大に存在していて、かつ、その多くは簡単にアクセス可能だ。インターネットによって。

 

だが、人生における目的(why)は簡単に探せない。自分にピッタリな目的が、どこかのデータベースに置いてあって、いつでもアクセスできる訳ではない。

 

だからこそ、自分に合った目的を見つけられずに苦労する人が沢山いる。

この辺の話は、『突破するデザイン』という書籍に分かりやすく書いているのでオススメ。

 

 

2. (個人あるいはチームの)モチベーションを高めることができた

 

人間のモチベーションが下がる原因の一つとして、「何のためにやっているか分からない」が挙げられる。

 

whyを明確にする事で、モチベーションの低下要因を一つ抑えることができる。

 

さらに、もうちょっと考えを飛躍させてみる。

ビジネスシーンでwhyを明確にしようとすると、高確率で「顧客にどんな価値を提供するのか?」という問いに行き着くと思う。

 

この問いに対する答えを明確にすると、自分が「顧客にどのように貢献しているのか」が明確になる。

 

人間のモチベーションが上がる要素の一つとして、「誰かに貢献している、と感じられること」がある。

※ここら辺は『科学的な適職』を参考にした

 

whyの明確化によって、期せずしてこの要素を満たしているのではないだろうか。

 

つまり、モチベーション低下要素を減らし、モチベーション向上要素を増やしたということだ。

なかなかやばい。

 

結構長くなったけど、以上。

ほぼ妄想のポエムでした。

【Python】【Pandas】数値データの頻度(割合)をヒストグラムで表示する

■ やりたいこと

具体的には、

100個のデータがあり、
各階級の値が「50, 20, 10, 10, 5, 5」であったとき、
単にこの数値を描画するのではなく、
「50/100, 20/100, 10/100, 10/100, 5/100, 5/100」
を描画したい。

プロットライブラリはseabornを使う。

■ まずseaborn.displotしよう

「seaborn ヒストグラム」で検索すると、まずこのメソッドが出る。
早速使ってみよう。

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

# 適当に配列作る
l = np.random.randint(1, 100, (1, 100)).tolist()[0]

# distplot をデフォルトで実行
sns.distplot(l)
plt.show()

デフォルト状態だとこんな感じで描画される。

f:id:massox:20200618125913p:plain

ヒストグラムと一緒に描画された折れ線グラフは何?

seaborn.pydata.org

この折れ線はKDEという。
KDEって何?:Kernel density estimation - Wikipedia

要は、あるデータが与えられたときにそいつの確率密度関数をノンパラメトリックに推定する手法ということ。
ノンパラメトリックとは、母数に依らないって意味。
母数とは、パラメータ。例えば、正規分布の母数は平均μと分散σ2
※サンプルサイズを母数と誤って理解しているケースが多いので注意したい
つまり、描画されるのは確率密度関数である。
これを任意の定義域で定積分することで確率になる。 -∞ ~ +∞ の範囲で積分すると1になる。

つまり、自分の欲しいグラフではないということ。
欲しいグラフはただの頻度(割合表示)なので。

■ 一旦、戻る。何をプロットしたいんだっけ?

シンプルに頻度データを割合にしたいだけ。
データ列⇒頻度データ(カウント)
⇒各頻度をデータ総数で割る⇒頻度データ(割合)

という流れを自分で作ることにする。
もしかしたら、seabornを調査したら楽に描画できる方法があるかもしれないが、
一旦やめておこう。

調べると、
pandas.DataFrameのvalue_countsを使えばできそうということが分かった。

Pandasでヒストグラムの作成や頻度を出力する方法 - DeepAge


単純にvalue_countsを使うだけだと、ユニークデータのカウントになってしまうので、
引数をちょっと調整する必要がありそう。
ということで、ドキュメントを見る。

pandas.Series.value_counts — pandas 1.0.4 documentation

以下の引数を調整すれば良さそう。
normalize を True に
bins を グルーピングしたい数に
設定してデータを作り、プロットすれば良さそう。

以下がそのスクリプト

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

# 適当に配列作る
l = np.random.randint(1, 100, (1, 100)).tolist()[0]

# 頻度データ(割合)を作る
sr = pd.Series(l)
vc_sr = sr.value_counts(normalize=True, bins=15)

# - ラベルをきれいに
vc_sr.index = [f'{x.mid:.2f}' for x in vc_sr.index]

# 頻度データ(割合)をプロットする
vc_sr.plot()
plt.show()


プロットした結果
f:id:massox:20200618150914p:plain

■ できたと思ったが……

実は、自分が欲しいグラフとは異なるグラフを描画していることが判明
自分が欲しかったグラフのイメージは、

f:id:massox:20200618145626p:plain
でも、実際は

f:id:massox:20200618145642p:plain
ということで、ここんとこ修正していきます。

■ 階級でソートする

公式ドキュメントを見る。

pandas.Series.value_counts — pandas 1.0.4 documentation
sort フラグがデフォルトでTrueになっている。
このせいで、頻度によってソートされてしまう。こいつをFalseにしてやるだけ。

すると、結果はこうなる。

f:id:massox:20200618151117p:plain

これでおっけい。

■ 補足:pd.DataFrame.value_counts と Interval オブジェクト

value_counts の戻り値は頻度データを保持する Series である。
この Series のインデックス(pd.Series.index でアクセス可能)は Interval 型である。

ここらへん初めて知ったのでメモしておく。
Interval型について、以下を参照

pandas.Interval — pandas 1.0.4 documentation


これは、数学で言うところの 区間 を表現するオブジェクトである。
詳細は、ドキュメントに譲るとする。

今回の場合、何も考えずに plt.plot() すると、
X軸ラベルにIntervalオブジェクト、具体的には以下のようなものが描画される。

( 0.1, 0.5 ] ※0.1より大、0.5以下の意

これがラベルに表示されると、
ぐちゃぐちゃになるので、
区間の代表値をラベルに表示したいと思った。
方法は簡単で、Interval.mid 属性を使うだけ。

それが、上記コードのこの部分
midプロパティで取得した値を f-string で文字列化してあげて、小数以下2桁で丸めている。

# - ラベルをきれいに
vc_sr.index = [f'{x.mid:.2f}' for x in vc_sr.index]

以上。

LINE Bot 開発チュートリアル

LINE Developers の MessagingAPIのドキュメントを参考にして、Botを開発したいと思います。

Messaging API | LINE Developers

1. MessagingAPIドキュメント概要

f:id:massox:20200617222509p:plain
LINE-dev-MessagingAPI-doc

内容はざっくりとこんな感じです。

  • 開発ガイドライン
    • Bot開発における禁止事項が記されている(大量リクエストしないでね、とか)
  • クイックスタート
    • Botを作る為に最低限知っておくべき情報が記述されている
  • ガイド
    • Botを作る為に必要なさらに詳細な情報が記述されている
  • Flex Message
    • メッセージの見た目をCSSを使って変更させるFlex Message 機能について書かれている
  • 概念
    • 用語説明
  • リファレンス
    • APIリファレンス

クイックスタートを読めば、サンプルのBotが作れそう。 まずはそこからやります。

2. クイックスタートを読む

■概要, メッセージタイプ

  • MessagingAPIでどのようなことを実現できるかが分かった
  • メッセージタイプにはいろいろあり、Botが発信する情報を豊かに表現できることが分かった
  • 【感想】だが、まずはテキストメッセージタイプによる情報の発信とユーザーからの情報の取得のみを行うこととする

■はじめに

  • チャンネルとは何なのかが分かった
  • 開発者コンソールで、任意のプロバイダについて、チャンネルを作成する手順が分かった
  • 【作業】とりあえず、チャンネルを作った

■権限を管理する

  • プロバイダに追加されたユーザーの権限として、AdminとMemberの二種類があることが分かった
  • チャンネルに追加されたユーザーの権限として、Admin, Member, Testerの三種類があることが分かった
  • 【感想】今回開発に関わるのは私一人なので、プロバイダとチャンネルに自分をAdminとして追加する(デフォルトがそれ)で良いと思う

■チャンネルアクセストーク

  • チャンネルへのアクセストークンには短期、長期、v2.1の三種類があることが分かった
  • サンプルでは長期を、本番用ではv2.1を使うことが推奨されていることが分かった

調査:JWTって何?

JSON Web Token(JWT)の紹介とYahoo! JAPANにおけるJWTの活用 - Yahoo! JAPAN Tech Blog

■JWTを生成する

  • アサーション署名キーを発行する必要がある(公開鍵をLINEが、秘密鍵を開発者が保持する)ことが分かった
  • アサーション署名キーとJWT用ライブラリを組み合わせてJWTによるチャンネルへのアクセスを実現するみたいということが分かった(この理解で合っているのかな…)
  • JWTライブラリがやることは、設定されたアサーション署名キー(開発者が保持する秘密鍵)を入力として、署名する(暗号化する?)ことで、JWTと言う名のコードが出力される。まだ理解はできてない。

3. サンプルEchoBotを作る

ここでは、以下のチュートリアルにしたがってEchoBot(おうむ返しBot)を作る。 LINEBot開発用のSDKがサポートしている言語は以下の通り。

参考:LINE Messaging API SDK | LINE Developers

本番開発では、Pythonを使うつもりだが、ここではチュートリアルに忠実に従うためJava用のSDKを使う。

■サンプルボットを作成する

手順は、ドキュメントに書いている通りでOK。綺麗に書かれていてわかりやすい。

developers.line.biz

Bot開発に慣れていない人で、初見の場合、困るかもしれないところを以下にメモしている。

  • チャンネルシークレットってどこにあるの?
    • LINEコンソール > プロバイダ > チャンネル > Basic Settings
  • チャンネルアクセストークンってどこにあるの?
    • LINEコンソール > プロバイダ > チャンネル > Messaging API Settings
  • HerokuのDeploy画面に表示されているLINE_BOT_CHANNEL_TOKENには何を設定するの?
    • LINEコンソールでコピーしたチャンネルアクセストーク
  • HerokuのDeploy画面に表示されているLINE_BOT_CHANNEL_SECRETには何を設定するの?
    • LINEコンソールでコピーしたチャンネルシークレット
  • Herokuで出てくるdynosって何?
  • デプロイ完了後BotサーバーのURLをLINEコンソールのWebhookURLに貼った後、Verifyボタンを押してもエラーになるんですが
    • ちょっと待った方が良い。自分の場合はデプロイ完了後2分ぐらい待ったらURLのVerifyが成功した。

■サンプルボットを使ってみる

LINEコンソール > プロバイダ > チャンネル > Messaging API Settings


Bot InformationにQRコードがあるので、お手持ちのスマホ(LINEアプリインストール済み)で読み取ればBotを友達に追加できる。 もし、スマホを持っていない場合やLINEアプリをインストールしたくない場合は、ごめん。どうしようもない。


では、適当にメッセージを打ってみよう。

f:id:massox:20200617231110j:plain


いや、なんか変なメッセージも一緒についてくるんですけど。
なんかデフォルトで設定されてるメッセージかな?
LINEコンソールを見に行くと、怪しいやつがいました。


f:id:massox:20200617231253p:plain


Auto-reply messages をDisabled にすれば良さそう。(実際そう)
付け加えると、Greeting messages は友達追加した時に最初に送られるメッセージのこと。
私は挨拶大事だと思うので、こっちはEnabledのままにしときます。


f:id:massox:20200617231741p:plain


ここまで終わったら普通におうむ返しをしてくれるようになった。


f:id:massox:20200617230631j:plain


という感じです。 次回は、テキスト以外のメッセージタイプも取り扱えるKitchensinkサンプルBotを作ります。