コワーキングスペースの申請のため Groovy x POI で Word ファイルを操作する
ふくもく会では、福井産業情報センターのコワーキングスペースにお世話になることが多いのだけど、ここを使うには利用申請書を提出する必要がある。この利用申請書が .doc ファイルで、これをなんとかしてサクッと記入できないか考えてみた。
最初は勉強のために Rust で書こうとしたけど、Rust には docx を編集するライブラリ、その名も docx しかなさそうだった。じゃぁ Python でいいかと調べてみても、こちらも docx を編集するライブラリしかなさそう。例えば python-docx。
諦めて手書きするかなーと @macoshita にぼやいてたら「この分野は POI が最強っすね」と言われて Apache POI のことを思い出した。Java かー。もうちょっと軽い気持ちで書きたいんだよなー。ということで Groovy から使ってみることにした。
Groovy 書くのが5億年ぶりなので、イチから調べてみたところ、Grape という仕組みがあって、依存をすっと書けるらしい。サンプルとして次のコードが書かれていた。
@Grab('org.springframework:spring-orm:3.2.5.RELEASE') import org.springframework.jdbc.core.JdbcTemplate
build.gradle
とかが無くても、こんな感じで groovy ファイルに @Grab
で書いとくだけで、勝手に依存する jar を取ってきてくれるんだって。夢か?Denoか?
そんなわけで、これを活用して POI でコワーキングスペースの利用申請書の doc ファイルを開いて、適当にテキスト置換して、書き出すコードを書いてみた。
// TextRangeReplatement test as an HWPFDocument example // http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestRangeReplacement.java?revision=1872041&view=markup @Grab('org.apache.poi:poi:4.1.2') @Grab('org.apache.poi:poi-scratchpad:4.1.2') import org.apache.poi.hwpf.HWPFDocument import org.apache.poi.hwpf.usermodel.Range import org.apache.poi.hwpf.usermodel.Section FILENAME = 'coworking_format1.doc' try (HWPFDocument daDoc = openSampleFile(FILENAME)) { Range range = daDoc.getRange() def 申請日 = '令和2年 11月 5日' def 住所 = '福井県福井市' def 利用日 = '令和2年 11月 23日(月)' def 氏名 = '福井太郎' // dump(range) range.replaceText('平成 年 月 日( )', 利用日) range.replaceText('年 月 日', 申請日) range.replaceText('住所 ', '住所 ' + 住所) range.replaceText('氏名 ', '氏名 ' + 氏名) dump(range) daDoc.write(new File('coworking.doc')) } void dump(Range range) { println("numParagraphs: " + range.numParagraphs()) for (int j = 0; j < range.numParagraphs(); j++) { println(j + '[' + range.getParagraph(j).text() + ']') } } HWPFDocument openSampleFile(String filename) { try { InputStream is = new FileInputStream(filename) try { return new HWPFDocument(is) } catch (Throwable e) { is.close() throw e } } catch (IOException e) { throw new RuntimeException(e) } }
これを groovy main.groovy
とかで実行するだけ。
おー、できたできた。便利すぎるな。この方向で進めてみようと思う。
細かいメモ:
- POI の数ある機能のなかで Word ファイル (.doc) を操作する機能は HWPF という部分。
- Word ファイルを読むと
HWPFDocument
という形になる。 - コメントに書いた通り TextRangeReplacement というテストコードが
HWPFDocument
でテキスト置換するときのいい例になっている。実際、このコードからすべてを学んだと言ってもいいぐらい。 - 「住所」を「住所 福井県」に
Range#replaceText
すると処理が終わらない。たぶん無限ループにハマってる。仕方なく「住所 」を「住所 福井県」に置換して無限ループを回避した。他にもSection
を特定してRange#insertAfter
を駆使するなど、細かい工夫が要りそう。
Angular の ui-router で lazy loading を書くと a の href がイマイチ
- Angular の ui-router を使っていて
- uiSref を使ってリンクを書いていて
- lazy loading を素直に実装すると
- lazy loading 後のページを指す A 要素の href がイマイチになる
イマイチ、というのは、だいたいこんな雰囲気のルーティングをしたときに……
// app.module.ts { state: 'items.**', url: '/items', loadChildren: () => import('./items/items.module').then(m => m.ItemsModule) } // items/items.module.ts { state: 'items', url: '/items', component: ItemsComponent }, { state: 'items.new', url: '/new', component: ItemsNewComponent }
lazy loading 前のページから <a uiSref="items.new">New Item</a>
すると、その a 要素の href が /items/new
になってほしいところ /items
になってしまうというものです。
クリックして遷移するだけなら動作に問題ないのですが、右クリックして別タブで開くといった操作をすると /items
が開いてしまって残念な感じです。
「items.edit
という状態の URL は /items/new
ですよ」という情報が lazy loading 後じゃないと得られないので、当然といえば当然ですね。
これに対して解決策も示されていますが、なかなか煩雑な印象です。
Angular 標準のルーターなら最初から routerLink
で URL を直接書くので、特に問題になりません。
ただの日記 2020/10/19
前回更新から1ヶ月空いちゃって、このままだと文章化スキルが落ちそうなので、ただの日記で文章化スキルをメンテします。
ISUCON 10 本選どうだったのか
前回更新「ISUCON 10 予選を nodejs 実装で突破しました(へしこず) 」で書いた話の流れですね。さる10月3日に行われた ISUCON 10 本選。結果は暫定で最下位、最終的に最下位から2位でした。肌感覚としても、歯が立たなかった、というもので、しばらく打ちひしがれていました。
ありがたいことに、いくつか賞をいただきました。
そうそう、#isucon のまたあいま賞と、ブログアワードの副賞が届いてたんですよ。とてもうれしい。ありがとうございます!
— りちゃ🏠🌈 (@rch850) 2020年10月19日
これで来年の家族Authも通るはずー。また会いましょう! pic.twitter.com/xJYfb64qcx
ひとつは「またあいま賞」。fail せず点数がついたチームの中で、下位のチームに与えられた賞です。がんばろう。
もうひとつは「ブログアワード次点」。これはアフターイベントの中で発表された賞でした。予選突破の記事でベンチマークシナリオを解析していたところが評価されていました。あと nodejs 実装で挑んだことも。ブログ書いてよかったー。ありがとうございます!
ワークマン本を読んだ
こちらのところてんさんのツイートを見て気になっていたワークマン本を読みました。
ワークマン本、読了
— ところてん (@tokoroten) 2020年7月11日
これは最高のDX事例。
「がんばらないでマニュアル化でオペレーショナル・エクセレンス」があったからこそ、土屋氏が入ってきて、DXが成功したのだという確信を得た。
逆を言うとDXは「頑張るカルチャー」と相性が悪いのだと思う。https://t.co/MZSbVhRvGB pic.twitter.com/bBA8XCCJpT
会社で数ヶ月前から毎週統計の勉強会をやっているところで、グサッと来る内容でした。需要予測のアルゴリズムの検証会議を週1で開催して、条件を見直しているあたりとか。こういう本はめったに買わないんですが面白かったです。
統計といえばこんな本も買いましたね。正直、最初のページの「This is a ball」が一番の謎。
ベイズ確率完全に理解した pic.twitter.com/FOjiUrpERj
— りちゃ🏠🌈 (@rch850) 2020年10月10日
PG BATTLE 2020 に出ます
ISUCON で魂が抜けているところですが PG BATTLE 2020 に出てきます。チームメンバーの実力差がそこまで大きくないので、どの問題をやるかはくじ引きで決めました。魂をつなぎとめたい。
以上!
ISUCON 10 予選を nodejs 実装で突破しました(へしこず)
「へしこず」の rch850 です。ISUCON 10 の予選を nodejs 実装で通過しました。奇跡的な予選通過の ISUCON 5 以来、2度目の本選進出です。やったぜ!チームメイトは machosita と emittam です。いつもありがとうございます。
個人的には nodejs で予選を突破できたのがうれしいです。 nodejs への思いは最後に。
macoshita の予選レポートが実況風味なので、こちらは箇条書きにします。実況風味はまた時間があるときに。
なおリポジトリはこちらです。
スコア推移はこう。
スコア推移。例年と違って早すぎる最適化を避けた結果、こんな推移になった #ISUCON pic.twitter.com/TSHmMosU7E
— りちゃ🏠🌈 (@rch850) 2020年9月14日
やったこと
サーバ構成
シンプルにこうしました。DB 分割したチームが結構いたようですが、そこまで考えてませんでした。しなくても勝てたし、して勝てたかは分かりません。
効果が大きかったかもしれないもの
- bot 対策 (nginx側, nodejs 側)
- /api/estate/low_price と /api/chair/low_price をキャッシュ (commit)
- どのベンチマーカーも最初にアクセスしてくる。そして更新頻度がとても低い
- 物件は入稿時のみ、椅子は入稿時と在庫が切れた時にキャッシュを破棄
- あとから Redis に乗せた (commit)
- nazotte をワンクエリにして spatial index 追加 (commit)
効果がそこまで大きくなかったかもしれないもの
- /api/estate/req_doc/:id で id 29500 以下なら即 OK、それ以外は Redis に id をキャッシュしてその後のアクセスはキャッシュを見て OK (commit)
- /estats/:id のキャッシュ (commit)
- nazotte に limit 指定が無かったので追加 (commit)
- これに気づいたのが残り10分切ってから。今からこれやる!?って10秒ほど相談して投入した。これが最後のコミット
- rent, (stock, price) のインデックス追加 (commit)
- (door_width, door_height), door_height, popularity のインデックス追加 (commit)
- (door_width, door_height, rent), (kind, stock, price) のインデックス追加 (commit)
- search 系のカウントと検索を
Promise.all
で並列化 (commit) - 不必要な
SELECT *
の改善
やろうとしたけどできなかったこと
自分がやってたとこ。悔しいので晒します。
- recommended の OR を減らす
- 短い2辺 (len1, len2 とする) が通ればいいから
(door_width >= len1 AND door_height>= len2) OR (door_width >= len2 AND door_height>= len1)
だけでいいよね? const [len1, len2] = [chair.width, chair.height, chair.depth].sort()
すればいいんだな。はいコミット (commit)- あれ?検証エラー?
- ソート順間違えたかーー???えいやー
const [_, len1, len2] = [chair.width, chair.height, chair.depth].sort()
(パニック状態) (commit) - また検証エラー??????完全に正解してるでしょ????
- MDN 見たら sort は「デフォルトではUnicodeコードポイントの昇順にソート」って書いてある!
parseInt
したら通るやろ! (commit)[1, 10, 4].sort()
は[1, 4, 10]
になると思い込んでたけど、実際は[1, 10, 4]
[1, 10, 4].sort((a, b) => a - b)
なら[1, 4, 10]
になる
- 通らない……もう限界。revert します
ラスト数分でこうすればいいことに気づくが、もうコードを変える時間は残っておらず終了
// ここまでのコード const [_, len1, len2] = [parseInt(chair.width), parseInt(chair.height), parseInt(chair.depth)].sort((a, b) => a - b); // 多分これが正解 const [len1, len2] = [parseInt(chair.width), parseInt(chair.height), parseInt(chair.depth)].sort((a, b) => a - b);
- 短い2辺 (len1, len2 とする) が通ればいいから
どういう方向性で動いたか
今回はこれまでの ISUCON の動き方からちょっと変えてみました。
- 落ち着いて取り組むようにした(というのは方針で抽象的なので、以降具体的な話)
- 自信がある nodejs を選んだことで落ち着けたというのはあると思う
- アクセスログからベンチマーカーの動きを見て、どうやって得点を稼げるか考えた(詳細は次のセクションに)
- ウェブサービスを自分で触って挙動を理解するようにした
- これも上と関係した話ですね
- Redis を使うようにしたのが19時過ぎ(残り2時間弱)だったり、API サーバを増やしたのが20時過ぎ(残り1時間切ってる)だったりと、普段は早めにやっていた作業を、かなり後回しにした
- 「早すぎる最適化」を懸念したり、ぬか喜びしたくないなという考えから、意図的に後回しにしました。結果にどう影響したかは分からないけど、たぶん良かったんでしょう
ベンチマーカーの行動パータン
nginx のアクセスログから調べました。User-Agent に UUID らしきものがついていたので、それで絞り込んでパターンを分類しました。
3パターンの行動が確認できました。他にもあったかもしれませんが、ひとまずこの3つで。末尾の数字はその時点での req_time です。これを見て、あるパターンのユーザーが得点に至るまでの合計時間を意識するようにしました。low_price は全パターンに効いてくるとか、search は回数が多いから大事とか。
ユーザーパターン 1(条件検索して資料請求)
"GET /api/estate/low_priced HTTP/1.1" 0.003 "GET /api/chair/low_priced HTTP/1.1" 0.071 "GET /api/estate/search/condition HTTP/1.1" 0.001 "GET /api/estate/search?page=0&perPage=25&rentRangeId=2 HTTP/1.1" 0.079 "GET /api/estate/search?page=3&perPage=25&rentRangeId=2 HTTP/1.1" 0.114 "GET /api/estate/search?page=3&perPage=25&rentRangeId=2 HTTP/1.1" 0.118 "GET /api/estate/search?doorWidthRangeId=1&page=0&perPage=25 HTTP/1.1" 0.168 "GET /api/estate/search?doorWidthRangeId=1&page=4&perPage=25 HTTP/1.1" 0.185 "GET /api/estate/search?doorWidthRangeId=1&page=3&perPage=25 HTTP/1.1" 0.180 "GET /api/estate/27131 HTTP/1.1" 0.002 "GET /api/estate/24611 HTTP/1.1" 0.001 "POST /api/estate/req_doc/24611 HTTP/1.1" 0.002
ユーザーパターン 2(なぞって資料請求)
"GET /api/estate/low_priced HTTP/1.1" 0.010 "GET /api/chair/low_priced HTTP/1.1" 0.176 "POST /api/estate/nazotte HTTP/1.1" 2.000 "GET /api/estate/low_priced HTTP/1.1" 0.016 "GET /api/chair/low_priced HTTP/1.1" 0.095 "POST /api/estate/nazotte HTTP/1.1" 0.223 "GET /api/estate/16302 HTTP/1.1" 0.005 "POST /api/estate/req_doc/16302 HTTP/1.1" 0.002
ユーザーパターン 3 (条件検索して購入)
"GET /api/estate/low_priced HTTP/1.1" "GET /api/chair/low_priced HTTP/1.1" "GET /api/chair/search/condition HTTP/1.1" "GET /api/chair/search?depthRangeId=1&page=0&perPage=25 HTTP/1.1" "GET /api/chair/search?depthRangeId=1&page=2&perPage=25 HTTP/1.1" "GET /api/chair/search?depthRangeId=1&page=0&perPage=25 HTTP/1.1" "GET /api/chair/search?color=%E3%83%8D%E3%82%A4%E3%83%93%E3%83%BC&page=0&perPage=25 HTTP/1.1" "GET /api/chair/search?color=%E3%83%8D%E3%82%A4%E3%83%93%E3%83%BC&page=3&perPage=25 HTTP/1.1" "GET /api/chair/search?color=%E3%83%8D%E3%82%A4%E3%83%93%E3%83%BC&page=2&perPage=25 HTTP/1.1" "GET /api/chair/2032 HTTP/1.1" "GET /api/recommended_estate/2032 HTTP/1.1" "GET /api/chair/19330 HTTP/1.1" "GET /api/recommended_estate/19330 HTTP/1.1" "POST /api/chair/buy/19330 HTTP/1.1"
また、これらのパターンに加えて、資料請求とイス購入のどちらの得点が多いかも調べました。序盤は 5:1 で資料請求が多め。後半はさらに顕著になって 10:1 ぐらいになっていました。これをもとに、資料請求大事という方向性が見えました。
なぜ nodejs 実装にしたのか?
これは予選突破した今だから言えることだし、とてもとてもおこがましい話なのですが、「Go じゃないと ISUCON は戦えない」というハードルができてしまうのが嫌でした。これは自分たちが ISUCON 9 を迎える時に感じていたハードルでした。結果、Go を選んでも勝てなかった(そもそも書けないんだけど)。Go でもダメなら諦めないとダメかな。そう思った時に、慣れた nodejs 実装のことを考えました。
そこで、これまでの ISUCON を振り返りました。
初めて ISUCON に参加した ISUCON 5 から一昨年の ISUCON 8 まで、ずっと Ruby 実装で挑戦していました。3人とも Ruby ばっかり書いているというタイプではなかったのですが、それなりに書けてました。
ISUCON 5 では奇跡的に予選突破できたのですが、そのあと Ruby で書き続けても、予選突破できない年が続きました。ISUCON 9 まで来て、さすがに Golang にしないと勝てないかな、と思って Golang に切り替えたのですが、まぁ書けない書けない。それなりに予習したつもりでしたが、それでも競技時間中に「これってどうやって書くんだっけ」と調べることが多かったのが反省です。
そして ISUCON 10。正直、一番書けるのは nodejs 実装だというのは確信してました。でもそれで勝てるのか?というのが不安でした。実際、nodejs で予選突破したチームは非常に少ないです。
これで大丈夫なんだろうか……
そんな心配をしながら、昨年予選の nodejs 実装を開いてみると、なんと TypeScript 実装。これは捗る!しかし手元でベンチマークしてみると Go や Ruby の半分程度のスコア。これは厳しいか……と思ったのですが cluster で fork したらあっけなく他言語に並びました。これは行ける!!!!
そう考えて今年の ISUCON 10 に挑み、なんとか予選突破できました。
これで「nodejs でも予選突破できるんやで」って胸を張って言える……そう思っていました。
Nodejs 2組 6.5%
他にもいたんだね。どこのチームだったのかな?
ponyopoppoが普段から使っているNode.jsです。
( ゚д゚)……………
「nodejs で予選1位通過しました」に勝てるわけないやーん。
ま、それを証明するのは自分じゃなくてもいいよね。nodejs でも ISUCON 戦えるぞ!立ち上がれ!nodejs の民よ!来年の ISUCON で待ってるぞ!
「ダブルチェックの有効性を再考する」をプログラマ視点で読んだ
このツイートで見かけたのがきっかけで、ダブルチェックの有効性を再考する (PDF)を読みました。プログラマ視点で。エンジニア視点って書くと主語広くなっちゃうので、今日はあえてプログラマ視点という表現にしてます。
トリプルチェックしているからヨシ!
— ところてん (@tokoroten) 2020年9月8日
トリプルチェックがシングルチェックと変わらないエラー検出率なの、面白いなぁhttps://t.co/KCbVH8FLoK pic.twitter.com/GLkrNXkxtj
まず、そもそもこの資料での「ダブルチェック」は医療現場での話で、現場猫が出てくるような工事現場やシステム開発の現場じゃないです。
また、ソフトウェア工学の論文などに首を突っ込めば、ここで出てくる話の検証などがあるのかもですが、そこまでは調べていません。
小さいリスクは整理して捨てる話
これはスライド中の「小さいリスクは整理して捨てる」(p.39) といった表現や、それを具体的に言った「有害事象につながらないエラーは発生しても許容しましょう」(p.55) という話のことです。ただ楽をしましょうということではなくて、大きな事故を減らすために何ができるかという話です。大きな事故が減った様子は p.59 にあります。
話題がダブルチェックなので、コードレビューと対比してしまうのですが、そちらに置き換えると、CSS を1行1行チェックする行為がしっくりきます。念入りにチェックしている方の気分を害したらすみません。
自分が CSS をレビューするとき、見た目に問題がなくて、コードもざっと見て大丈夫そうなら割と LGTM 出しています。例えば .foo, .bar { color: red; }
と書いてあって、この .bar
のセレクタが実は使われていないということもあるかもですが、そういったところまで 100% 確認しているかというと、できていないと思います。
他にも確認すべき大事なところがあるならば、細かいところよりもそっちに時間をかけたほうがいいでしょう。
確認エラーの分類
p.12 からの「ダブルチェックしたのになぜ?」で、確認エラーの分類というものが出てきます。「うっかり」「失念」「思い込み」「ルール違反」の4つに分かれていて、プログラミングではどう分類されるか考えてみました。
- うっかり
- typo
- 不等号の向きを間違える
- 失念
- リソース解放忘れ
- 仮データをそのままコミット
- 思い込み
- typo ではなく本人が正しいと思っている英語間違い(registUser, denyed など)
- 「ドキュメント修正」と書かれたコミットをよく見ずにマージしたけど、よく見たらドキュメント以外に意図しない変更が入っていた
- ルール違反
- 時間がない、面倒、など
- 例外握りつぶし
- グローバル変数
なんでルール違反を?と思うところですが、時間がなかったり、面倒でさぼってしまったりで発生するのでしょう。後で戻すつもりの一時的なルール違反をそのままコミットしてしまうのは、失念の方に分類されそうです。
ダブルチェックとペアプロと不具合
ダブルチェックの現場の様子が何度か出てきますが、2人で一緒に作業をしているという点ではペアプロに近いように感じられました。
個人的には、ペアプロは開発効率を上げるためのもので、不具合を減らしたりするものではないと考えています。結果的に減るかもしれないけど、それを目的としているわけではない、ということです。
有効なダブルチェックをするなら、同席同時ではなく独立がいいとありますが (p.28)、これはプログラミングでも同じだと思います。
その他雑多な話
- 「誤薬防止のための6R」なるものがあるそうで、プログラマ的にも「正しい計算量」「正しい権限」みたいに書けると教育に良さそう
- ダブルチェックは、エラーそのものを減らすのではなく、発見するため
- James Reason のエラーの定義では「偶然の作用に起因しない」。Wikipedia のヒューマンエラーにもそう書いてあった
- 話題のトリプルチェックの図は「人間による防護の多重化の有効性」がソース。日本品質管理学会の、島倉、田中による論文
- 最初読んだときは SLO との類似性を求めていたけど、読んでいったらちょっと違う感じだった
以上、プログラマが読んでも色々と考えさせられる資料でした。
ウェブサービスなら多少のエラーは許容する戦略がありうるけど、医療現場でそれはできないだろうな……と思ったら「有害事象につながらないエラーは発生しても許容しましょう」という結論だったのにはびっくりしました。でも実際に効果が出ているようで安心しました。
Notion で書籍や記事を「効率的に読むには」
まず、タイトルの「効率的に読むには」を「」でくくっているのは『エンジニアの知的生産術』へのリスペクトです。
そして本題。Notion で Reading List に書籍やウェブの記事を溜め込んでいるという話を少し前に書いた。
この使い方について他人に説明しようと思ったときに、『エンジニアの知的生産術』のことを思い出した。この本のどこかに、自分がいまやっていることが書いてあった気がする。自分がやっていることが、書いてあったことに似ているのか、書いてあったことに影響されて、自分がそれをやっているのか。それは今となっては分からないけど、どこだったかな 🤔 ……気になってその部分を探しに行った。
Notion で読書ノートを取る
それは第4章「効率的に読むには」にあった。この章は、自分がこの本を買ってすぐ、この本を効率的に読むために読んだ章だ。
そこで紹介されている、高田明典の『難解な本を読む技術』が、自分の今の感覚に近かった。まだこの本自体は読んでいない。
この書籍でいう難解な本は、フロイトの「無意識」などの思想書らしいけど、自分がおもにターゲットにしているのは技術的な書籍、サイトだ。でも、なにか通じるものを感じた。
『エンジニアの知的生産術』が紹介するには、読書ノートを用意して、章の小見出しを書き写すとのこと。これがまさに自分が Notion の Reading List でやっていることだった。また、読みながら、分からないこと、何度も出現する単語を記録していくというのも、自分がやっていることに近かった。
ここでひとつ実際に取っていた「読書ノート」的なものを。Dropbox が Nginx から Envoy に引っ越した記事について、こういうまとめを取っていた。ここで、自分は Bandaid が何か詳しく分かってないけど、とりあえずの足がかりとして記録しておいた。
こういったメモが『難解な本を読む技術』の言う「読書ノート」に近いんじゃないかなと思った。実際の「読書ノート」では、図を描くなどのアナログ的な要素があるらしいのだけど、自分が読んでいる範囲では必要ないので Notion で大丈夫。
こうやってメモをとることが、どうして「効率的に読む」ことにつながるのか?そのポイントは『エンジニアの知的生産術』や『難解な本を読む技術』に譲っておきたい。今ここで書いているのは、自分が『エンジニアの知的生産術』で得た「効率的に読む」方法が Notion で実践できてるよという話。
Notion を使って読みかけを忘れないようにする
書籍でもネットの記事でも、その内容をしっかり理解するには他の情報を見に行く必要があったり、そもそも読みかけで放置してしまったりすることがある。えっ、ない?そうかー。でも自分はあるんです。
そんな読みかけを助けてくれるのが Reading List の Status。Status を Reading などにしておけば、後で「こんなに読みかけが溜まってるー」と気づきやすい。「Add a View」で「Board」を選んで「Reading Board」といった名前のボードを作っておくと、一覧しやすい。
これで「Reading が10個もあるわー」みたいな状況も気づきやすい。なお 自分の状況では Finished は14個しかなくて、ここがめちゃくちゃ量が増えたらどうなるかは知らない。
そういうわけで Notion は書籍や記事を「効率的に読む」ために役立つなという話でした。
Google オプティマイズのベイズ推しがすごい
最近、社内で週一で統計の勉強会をしてて、統計関係の記事の読みあわせをしたり、AIcia Solid Project の動画を見たりしながら、分かる、とか、分からんとか言ったりしてる。統計と言ってももっぱらベイズ関係の話なんだけど、ベイズ寄りになってる理由のひとつに、Google オプティマイズがものすごくベイズ推しだということがある。
Google オプティマイズは、Webサイトなどの A/B テストをサクッとやってくれるプラットフォームで、ヘルプの一覧がこれ。
Google オプティマイズでは「レポート」という形でテスト結果が出てくるので、その読み取り方を知りたいと思ったら、まずは「レポートの概要」を開くんじゃないかと思う。開いてみると、導入の文章が終わるとすぐに「ベイズ推定」っていうセクションが出てくる。
この言葉に免疫がある人ならともかく、ベイズのベの字も知らない人が見たら面食らうんじゃないか?でもそれだけ Google オプティマイズがベイズ推定を推しているってことなんだと思う。
さっきのヘルプ一覧に戻って、下の方にスクロールすると「方法」というまとまりで、詳しい話が出てくる。
こっちはさらにベイズ推しで、頻度論と比べてベイズ推定なら覗き見OKとか、説明しやすいとか、どんどん攻めてくる。頻度論には強烈な「p 値を提示しても、ほとんどの人は正確には理解できません」なんてことも言ってくる。「頻度論的アプローチとの違い」では、さらに頻度論とバチバチになっている。
もちろんこれらは一般的に言われてることで、Google オプティマイズが特段偏った情報を発信しているわけではないと思うのだけど、Google が言うのだからインパクトが強い。ベイズ推定を使う側からしても、他人に説明するときに「Google オプティマイズが使ってるやつ」と言えるのはとても便利なので、ぜひともこのままのテンションを維持してほしいところだ。