OpenCVを利用した矩形検出の試行錯誤

以前作ったレシートのOCRアプリを改善するため、OpenCVを利用した矩形検出について試行錯誤を行った。

環境

Python 3.7.8
OpenCV-Python 4.5.1.48

課題設定

レシートOCRアプリにおいて、一部のレシートが正しく検出されないことがそれなりの頻度で発生している(人間の目で見る分には背景との差異がある程度あるように思える場合にも)。レシートの矩形を過不足なく検出できるようにしたい。
レシートの検出結果(赤枠が検出できたレシート)

現在の検出手順

どこに改善点があるか調べるため、現在の処理手順を順に追ってみる。
[ 手順 ]
1. 読み込んだ画像をグレースケールに変換
2. 白と黒の2値化(大津の2値化を利用)
3. 輪郭の検知
4. 検知した輪郭を条件で取捨(内の面積が一定以上ある輪郭に絞った上で、輪郭の形状を近似し、頂点が4点の輪郭のみ選択) 手順2.の2値化の段階でレシートの地が白、背景とレシート印字が黒になるイメージだったが、背景の一部も白になってしまっている。そのため、レシートと背景の境界が一部消え、手順3.で輪郭が検知されない結果となっている。

改善ポイント

2値化の段階で、レシートと背景をうまく分離できれば、その後の輪郭の検知等はうまくいくと思われる。そのため、レシートを白・背景を黒に変換する精度を上げることを目指し、2値化の前処理について下記のような複数の手法を試してみた。
改善案1 減色する
改善案2 HSV色空間にする
改善案3 エッジ検出を使う
改善案4 適応的閾値処理を使う
改善案5 ハフ変換を使う ※ レシート外形を検知できなかったので省略

先に結果

上記改善案1~4を試した結果を先に示しておく。
詳細は別記事に記載しているが、この画像を元にパラメータを調整したのでどの手法でも全てのレシートが検出できている。
どの程度汎用性があるか見るため、異なる条件を付加した2パターンの画像で試してみた。
まず、背景の明るさの変化が大きい画像で試した。
背景の明るさの変化というより、各手法の中で指定しているパラメータの範囲外になることで検出できない部分が生じている。
次に部分的に影がかかっている画像で試した。
影の影響を受けやすい案1、2において、検出できないレシートが生じている。

各改善案の詳細

各改善案の詳細・コードは下記に分割して記載した。
OpenCVを利用した矩形検出の試行錯誤_減色・色空間の変更
改善案1 減色する
改善案2 HSV色空間にする
OpenCVを利用した矩形検出の試行錯誤_エッジ検出・適応的閾値処理
改善案3 エッジ検出を使う
改善案4 適応的閾値処理を使う

まとめ

今回試した3つの画像に限ると、適応的閾値処理が一番検出率が高い結果となった。しかし、この方法も万能ではなく、設定したパラメータから外れる画像に対しては検出できない結果となる。
より良い手法や調整の仕方がありそうだが、今の知識でわかるのはここまでなので、いったんレシートOCRアプリを適応的閾値処理を利用する形で修正を行った。

大人こそ自由研究をしよう

これはGMOペパボ ディレクター Advent Calendar 2020の18日目の記事です。


自由研究が好きだ。

 

自由研究は子どもの特権ではない。むしろ、大人になってからの方が基礎知識や考える力、経済力がついている分、研究できる範囲は広がっている。

 

これは、大人になってから行う自由研究は楽しいよ、みんなやろう、そして研究結果を教えてほしい、あわよくば自分の結果も見てほしい、という趣旨のエントリーである。

自由研究の楽しいところ

やりたいことが無限に存在する中で、あえて自由研究に時間を注ぎ込むのは、一言でいえば(苦しくも)楽しいからである。
何が楽しいかというと、「自分が知りたかったこと」かつ「まだ誰も知らないこと」を自分が動いたことで最初に知ることができるのである。興奮しますね。

テーマの選び方

自由研究をするには、まずテーマを決める必要がある。
テーマは大きなものや人類全般に役立つものでなく、自分が本当に感じたことから発想したものの方がよい。

ある一定期間そのテーマを考え続けることになるので、人の役に立つことよりも自分が本当に知りたいことの方が、モチベーションが維持されやすいからである。

私の場合は、日常の中で感じたことや課題をメモしておいて、しばらく経ってもまだやりたいと思えるものの中から選定している。

テーマのアイデア出し
Trello にどんどん追加していく(後から見るとなんのことかわからないものも多い)

いかにくだらない(しかし自分には重要な)内容をテーマに据えてきたかをイメージしてもらうため、過去に取り組んだテーマとそのきっかけを記載してみる。

    • 精神や体調は、何によって決まるのか(2018)
      前職で毎日ぎりぎりの闘いをしていた中で、切実に「風邪をひきたくない」「少しでも心健やかにいたい」と感じたことがきっかけ。活動量や食事内容、睡眠時間等の様々な値を記録し、それと体調等の関係性を検証した。
    •  なるべく日陰を通るには、どのルートを選ぶべきか(2019)
      2018夏、初めて福岡で迎える夏があまりに暑く「もしやこれから毎年この暑さを乗り切らねばならぬのか」と恐れおののいたことがきっかけ。出発点と到着点を指定した時、どのルートを通るとどの程度日陰になるか計算を行った。(なお、2018夏は特異な年で、翌年以降はそれほど暑くなかった)
    • レシートの家計簿入力を少しでも楽したい(2020)
      スプレッドシートに手入力していたが、「外税か内税か軽減税率適用商品か目で見て判断するのほんとめんどい」「同じ品目名何回も入力するの人生の無駄だな」と思ったのがきっかけ。レシートをOCRした後、結果を調整してCSVに吐き出す仕組みを作った。

自由研究の進め方

取り組むテーマが決まったら、いざ研究に手をつけていく。
ここでは、上記で事例に挙げた「精神や体調は、何によって決まるのか」を例に、私なりの自由研究の進め方を示していく。

 

1.   すでに調査・実現している先達がいないか確認する
「体調に最も影響をもたらすファクターは○○!」みたいな結論を誰かが出しているなら、そのテーマに取り組む意義は薄くなる(「誰かが類似研究をしていても、〜の点を解決したいから or どうしても自分でやってみたいから このテーマを押し進める」という判断をすることもある)。
このテーマの時は、仮に先行研究があったとしても「自分という個体にカスタマイズして知りたい」状態だったため、ざっとCiNiiで検索する程度でよしとした。

 

2.   何がわかればそれを調査・実現できるか検討する
取り組む前から手順が想像できていれば容易いが、どうやって取り組めばよいかわからない状態で始めることも多い。
このテーマの時は「たぶん各種指標と体調の相関を見ればよいのでは」程度の認識だったので、「相関ってどうやって計算するのか」「本当に相関を計算することで検証できるのか」をまず調べることが必要だと判断した。

 

3.   2.で検討した内容を学ぶ

読んだだけだと即忘却し、元のわからなかった状態に戻るので、内容をメモしながら進める

このテーマの時は、統計の勉強をしないと調査を進められないと感じていたので、相関を含め統計の基本を勉強した。具体的には、統計の入門から書かれている本を10冊程度並行して読んでいった(1冊を集中して読む方法もあると思うが、複数読むことで何度も出てくるところは重要だとわかるし、1冊で理解できなかった箇所を他の本の説明で理解できるメリットがある)。

 

4.   テーマの調査・実現を試みる

たとえ実質進捗がなくても、やったことを記載して自分を励ますことが肝要である

3.で理解した内容をもとに、テーマの解決を実際に試みてみる。これで結論までいけそうならそのまま進めるが、実際は「やってみたらここが不明だからこれ以上進められない」とどこかでつまづくことが多い。そうしたら、またその不明な点がわかるように勉強、新たに得た知識を元にまた進める、を繰り返していく。
このテーマの時は、「時系列データの時は見せかけの回帰というのが発生するらしい」と知ったので、そのあたりを学べる本を探して読んだ(難しくて理解できず、かなり苦しかった記憶がある)

 

5.   ある程度区切りがよいところまで進んだらまとめる
自分の中でいったんここまで、というところまで来れたら、アウトプットする。仕事でなく遊びなので、「飽きた」「明確な結論が得られなかった」状態でもやめてよいことにしている(ただ、それであっても何らかの形でアウトプットを行う)。
子どもの自由研究は見てくれる人(教師なりクラスメイトなり)が労せず得られるが、大人になると「見てもらう」こと自体も難しい。まずは「理解してもらえる形で目に触れる場所にある」ことが最低条件となる。
一人で進めていると内容が誤っている可能性もあるのが怖いところだが、機会があれば勉強会等で発表すると助言が得られることもある。

自由研究のメリットデメリット

「楽しい」というだけでやるに値するが、一応メリットデメリットを整理する。

メリット:心の安定に寄与する
自分が動くことで、今までなかったものが生み出される。たとえ、仕事で価値をあまり生み出せていない時期も、自由研究で成果が出れば、自分の中で多少心の支えになる(業務面の課題は何も解決していないのだが、別方面で成果を得ると脳が錯覚するのである)。
また、業務後に仕事とは異なる内容に頭を使わないといけなくなり、業務のあれこれを考える余裕がなくなるので、結果的に気持ちの切り替えもしやすい。
※ なお、自由研究でも進捗が出なくて二重に苦しくなることもままある

デメリット:可処分時間の減少
自由研究に取り組むと、思った以上に時間を食うことに気づく。しかし、可処分時間の大半を注がないとなかなか進捗が出ないので、楽しくならないというジレンマがある。遊ぶ時間や他の勉強をする時間に影響するので地味につらい。

結論

大人になってから行う自由研究は(苦しくも)楽しいぞ、みんなやろう

 

よい自由研究ライフを!

レシートを読み取ってCSVに変換するデスクトップアプリを作った

こんな感じで、レシートを検知して切取り → 1枚ずつOCR → 誤って読み取ったところの修正・付加情報の追加を手作業で受け付け → CSVで保存 …という流れをGUI上で行えるようにした。

 

きっかけ

元々手作業でレシートの入力を行っていたが、少しでも手間をかけずに行いたいと思ったため。

要件

全て電子決済にしてその電子的な履歴を参照するようにすれば、こんな面倒なことをする必要はないと思う。今回、以下の要件を実現したかったため、このようなちょっと操作が面倒なものになった。

    • レシートの合計額でなく、品目単位で見れるようにしたい
      1回の購入の中でも品目の分類が異なる(スーパーで食品と雑貨を同時に買うとか)ことがあり、分けてデータを集計できるようにしたいため、品目ごとの取得が必要だった。
    • 軽減税率や外税/内税等を反映させて品目ごとの価格を自動で計算したい
      レシートを観察すると、表記の仕方として外税の場合と内税の場合があり、さらに品目により軽減税率の場合もある。なるべくそれらの条件は自動で読み取って、計算を自動で行うようにしたかった。
    • レシートを複数枚同時に扱えるようにしたい
      OCRするには当然レシートの画像が必要だけど、1枚ごとに撮影するのは面倒なので、複数枚の画像でも扱えるようにしたかった。
    • そのものの品目名で印字されていないが繰り返し出てくる品目は、2回目からは自動で入力してほしい
      例)「スッキリCAテツ1L」というのは「牛乳」という名称で登録したい(「すっきりCa鉄 1000ml」という名称で販売されている、カルシウムや鉄分が付加されている牛乳である)。
    • 分類もなるべく自動で判定してほしい
      「牛乳」が分類としては「食費」というのは(自分の分類方法では)自明であり、こちらも2度目からは自動入力されるようにしたかった。
    • ローカルで操作を完結させたい
      レシート画像を通信するのは何となく不安だったので、全ての操作がローカル上で完結するようにしたかった。

処理の流れ

大まかには以下の流れで処理するようにした。

 

1 OpenCVでレシートの矩形を検知して切取り
   ↓
2 TesseractでOCRを行う
   ↓
3 正規表現で頑張って品目や価格、購入日等を抽出
   ↓
4 品目名等を過去の履歴を参照して修正したり、品目ごとの価格(税込・軽減税率適用)を計算したりする
   ↓
5 Tkinter(GUI作成用のPythonの標準ライブラリ)でデータ(品目や価格等)を表示、手作業での修正を受け付ける
   ↓
6 最終的なデータを取得してCSVに吐き出し

今後

とりあえず最低限動くというレベルなので、今後自分で実際に使っていってみて、不便なところを徐々に直していくようにしたい。
特に以下の点についてはすでに気になっている。

 

    • 品目ごとの価格の調整
      外税表記で、合計額に対し消費税を最後に計算されているレシートだと、品目ごとに消費税を計算すると合計額が実際の価格から数円ずれてしまうことがある。今は単純に小数点以下四捨五入にしているけど、何らかの調整を入れて合計額がぴったり合うようにしたい。
    • レシートの矩形の検知性能向上
      レシートと背景のコントラストが低い画像の場合など、うまくレシートを検知できない場合がある。OpenCVや画像認識を勉強して、もう少し性能を上げたい。
    • 文字の認識精度向上
      レシートは半角カタカナもけっこうな頻度で使われていて、特にその場合の認識精度は相当低い。ただ、OCRはtesseractを使わせてもらっているだけなので、今のところ具体的な改善点を思いついていない。
正直、すでに「レシートをまとめて撮影するのさえ面倒だな…」という気持ちが芽生えており、このファーストステップの簡略化の方法も検討が必要かもしれない。

Couseraの機械学習講座を受講した

オンライン教育サービスであるCouseraで、機械学習の基礎的な学習講座として有名なMachine Learning講座を受講した。

前々から気になりつつ、「自分がついていけるレベルだろうか」「英語わかんないしな」(講義には日本語訳がついているけどテストや課題は英語)、「Pythonじゃないし」(プログラミング課題はOctaveで提出する)となかなか踏み出せずにいた。
在宅勤務になり家にいる時間が増えたことも後押しになって、受講することができたので、感想等記録しておく。

扱われる内容

下記のキーワードに関連する内容について、11週にわたり講義が行われる。

Week 1 機械学習の概要
線形単回帰、最小二乗法、最急降下法

Week 2 線形重回帰
特徴量のスケーリング、正則化

Week 3 分類 ロジスティック回帰
過学習、正則化、One-vs-All

Week 4・5 ニューラルネットワーク
隠れ層、論理ゲート、backpropagation、gradient checking、ランダム初期化

Week 6 機械学習の評価
交差検証、high bias、high variance、学習曲線、適合率、再現率、F値

Week 7 サポートベクタマシン(SVM)
マージン、決定境界、カーネル法

Week 8 クラスタリング・主成分分析(PCA)
K平均法、局所最適、エルボー法
次元削減、射影誤差、共分散行列

Week 9 異常検知・レコメンドシステム
正規分布、多変量正規分布
協調フィルタリング、類似度

Week 10 大規模データ
確率的勾配降下法、ミニバッチ勾配降下法、逐次学習、並列化

Week 11 Photo OCR
パイプライン、スライディングウインドウ、データ合成、ceiling analysis

学習の進め方

ノート
こんな感じでノートにメモしていった

各週、動画による講義 + テスト + プログラミング課題から構成されている。
私は講義を平日にざっと見て(わからないところがあってもあまり気にせず最後までいったん見る)、休日にもう1回見直しながら話の流れをノートにメモしていった。平日見たときは「何言ってるか全然わからん」と思った箇所も、2回目に休日に見た時は「あれ、なんで理解できなかったんだろう」となることも多かった(何回見直してもやっぱりわからん、となる箇所ももちろんあった)。
講師のAndrew先生の説明がわかりやすかったのはもちろんだけど、ノートにまとめようとする過程で理解が曖昧な箇所をつぶし、流れをしっかり捉えられるのが自分にはよかったのかなと思う。

かかった時間

ネット上の体験談だと1ヶ月未満で完了したという方も割と見かけたけど(みんな天才なのかな?と思った)、私は設定されているのと同じペースで進めていったので、約2ヶ月かかった。週により内容の重さの差が大きくて、3時間で終わった週もあれば15時間以上かかった週もあった。

感想

講座全体を通じて感じたことを記録しておく。

  • 勉強を続けて納得感を高めていきたいと思った

とても勉強になったので受講してよかったなと思う。
説明が非常にわかりやすいので、そのアルゴリズムの意図や、数式の意味するところの大枠について理解することができた。全体的に納得しながら進めることができたのだけど、「この数式がなぜこうなるかは、ここでは説明しない」という説明で次に進む部分もしばしばあったので、数式の導出などは少しもやもやが残った。また、課題は一から自分でコードを書くのではなく、重要な数式部分のみ自分で書くという形式だった。そのため、ふんわりとした理解になっている自覚がある。自分でPythonで書き直してみるとか、数式の導出をしてみるとかするべきなのだろう。勉強を続けていって、理解を深めていきたいと思う。

  • 英語をもうちょっと読めるようになると色々楽だなと思った

講義には有志の方がつけてくれた訳文がついているけど、テストや課題は英語で書かれている。最初の数週は頑張って単語を調べながら読んでいたけど、途中から面倒になってDeepLに突っ込んで訳文を読むようになった。俄然課題の進みが速くなって、自分の英語力の低さを改めて実感した。DeepLは訳が自然で、訳文だと意味が不明で結局原文を読むみたいなことはほとんど発生しなかった(訳文を読んで「解くのに必要な前提条件が足りない…」となって、原文見たら訳されていない箇所があった、ということは数度あった)。

  • ペース配分を自分でしなくて済むのって楽だなと思った

課題を提出すると、何割終わったか示してくれる

オンライン講座では当たり前なのかもしれないけど、見終わった動画にはチェックマークがついたり、プログラミング課題も途中提出の度にどこまで終わったか明確に表してくれる。「今週中にあと3個動画見て課題を解くから、x時間くらいで終わる」とわかると、残りの時間は別の学習に充てようなど見通しが立てやすい。ペース配分を自分でしなくて済むこと、どこまで進んでいるか示してくれることがこんなに楽なのかというのは意外な発見だった。

今後機械学習の勉強をしていく上で、非常に役に立つ講座だった。継続して学習していきたい。

データ分析者がCS経験から得たこと

※ ここでのCSはカスタマーサービス(コンピュータサイエンスではない)

昨年、分析を中心としたディレクター職から、CS職に異動して半年ほど過ごした。
この半年間の経験から何を得て、意識や行動がどう変わったのか、再びディレクターに戻り数ヶ月経った現在までに感じた差分をふりかえってみる。

  • ユーザーの姿が以前より立体的になった
    CS業務の中心である問合せ対応を行うなかで、自分の中のユーザー像が少しずつ具体的になっていった。以前はデータから「こういうお客様がいるのかな」と思ったり、周りから「こういう傾向のユーザーが多いよ」と聞いて、漠然とユーザーの姿を想像していた。それが、お客様からの問合せを千数百人分読み、1件ずつ自分の手で回答文を作成することで、「〜を目的として操作する人には〜でつまずく人が多く、それはこの部分が理解しづらいことが原因かもしれない」などの仮説が自分の中に蓄積されていった。
    データを見るとき全体的な視点になりがちだったけど、1件のデータの裏にいる1人のユーザーを実感を伴った形で想像できるようになったことで、データから仮説を考えやすくなったと感じている。1

  • 「分析が役に立てる」と認識する範囲が広がった
    CS業務を毎日していると、「こういう傾向の問合せが多いから改善したい」という部分(明らかな不具合であれば即エンジニア等に対応してもらうけど、そうではなく、より使いやすくなるような改善点)が見えてくる。そう感じる部分は同じサービスに携わるCS内では大抵共通認識となっていたけど、他職種の人も同様に認識しているかというと必ずしもそうではない場合もあった。
    その内の1つについて、事象の起こっている件数、それによる損失額、具体な原因と改善案を問合せ対応の合間にまとめ、CS外に提案したところ、実際に改善に至ることができた。
    以前は、当たり前なことを可視化しても「そうだね」「知ってる」となるだけで、さほど意味がないのではと思っていた。でも、当たり前の範囲は人により差があり、各自が認識しているサービスの姿は思っている以上に異なっていることがわかってきた。また、当たり前であってもその課題の大きさや姿は具体的に認識されていない場合もあることもわかった。
    こういった点について、分析者が媒介になる(課題を適切に可視化して共有することで、解決につなげる)ことも分析が役に立てる範囲なんだなと認識が広がった。
    媒介となるためには、分析者が課題の存在を認識している必要がある。CSにいればサービスへの反応を自然と知ることができるけど、現在は離れているし半年で認識できた範囲はわずかだと思うので、これからもCSのメンバーに積極的に話を聞かせてもらいに行こうと思っている。

  • 分析を通じて貢献するぞ、という意識が強くなった
    CS在籍中、エンジニアやデザイナーなど色々な人が折にふれ、気にかけて声をかけてくれた。また、CSの同僚も私が分析をやりたい(CSとして今後やっていきたいわけではない)ことを知った上で、色々質問しても、どの部分を見てどう考えた結果その判断に至ったのか、サービスがそうなっている経緯など納得するまで教えてくれた。
    ディレクターに戻り、それらに報いたいという気持ちとともに、分析面でしっかり貢献できないと(分析専門でない部署で分析中心にやっている分余計に)自分がここに存在する意味がなくなってしまうなと思うようになった。
    存在意義を示すためには、分析により改善につながる部分を見定めること、分析結果を示すだけでなくそれを施策に落として提案することが必要だと思っている(そもそもの分析能力も当然まだまだ精進が必要だけど)。色々スマートにできなくて転げながらやっている毎日だけど、分析を業務としてやれることが嬉しいから、かっこわるくてもやっていく。


  1. 利用中に一度も問合せをされないお客様も多いので、問合せだけからユーザーの姿を思い込みすぎるのは危険である、という点も留意する必要はあると思っている 

暖かいルートを検索するWebアプリを作ろうとした話_うまくいかなかった部分

こちらでふりかえりを書いたWebアプリについて、デプロイでうまくいかなかった部分を記録しておく。

概要

ローカルでは一応意図した形で動いてくれていたDjangoをHerokuにデプロイしたところ、エラーが出てうまく動かなかった。そして、そのエラーを解決することができなかった。
自分の理解では、利用しているライブラリが参照しているファイルをうまく参照させることができなかったのでエラーとなったと認識している。
コードはここにいったん置いた。

詳細

1- ログの確認

Herokuでデプロイして、該当サイトにアクセスしたところ「Application error」という表示になっていた。

その下に表示されていた文に従い、ログを確認した。

$ heroku logs --tail

(前略)
2019-12-30T05:46:04.064263+00:00 app[web.1]: import osmnx as ox
2019-12-30T05:46:04.064272+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/osmnx/__init__.py", line 9, in
2019-12-30T05:46:04.064274+00:00 app[web.1]: from .core import *
2019-12-30T05:46:04.064276+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/osmnx/core.py", line 10, in
2019-12-30T05:46:04.064278+00:00 app[web.1]: import geopandas as gpd
2019-12-30T05:46:04.064288+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/geopandas/__init__.py", line 1, in
2019-12-30T05:46:04.064290+00:00 app[web.1]: from geopandas.geoseries import GeoSeries # noqa
2019-12-30T05:46:04.064292+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/geopandas/geoseries.py", line 15, in
2019-12-30T05:46:04.064294+00:00 app[web.1]: from geopandas.base import GeoPandasBase, _delegate_property
2019-12-30T05:46:04.064296+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/geopandas/base.py", line 16, in
2019-12-30T05:46:04.064298+00:00 app[web.1]: from rtree.core import RTreeError
2019-12-30T05:46:04.064300+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/rtree/__init__.py", line 1, in
2019-12-30T05:46:04.064302+00:00 app[web.1]: from .index import Rtree
2019-12-30T05:46:04.064310+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/rtree/index.py", line 5, in
2019-12-30T05:46:04.064312+00:00 app[web.1]: from . import core
2019-12-30T05:46:04.064314+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/rtree/core.py", line 125, in
2019-12-30T05:46:04.064316+00:00 app[web.1]: raise OSError("Could not find libspatialindex_c library file")

計算して求めたルートを地図にプロットするのに、osmnxというライブラリを利用している。そして、osmnxではRtreeというライブラリが使用されている。Rtreeのコードの中で、libspatialindex_c library fileが見つからなかったことによりエラーになっていると理解した。

2- ローカルではエラーが起きなかったのは何が違うのか確認する

上記のエラー文にFile "/app/.heroku/python/lib/python3.7/site-packages/rtree/core.py", line 125 とあり、.venv/lib/python3.7/site-packages/rtree/core.pyの125行目付近を確認した。
/app/.heroku/~というのがどのように確認できるかわからなかったのでvenvでローカルでインストールしたものと同じだろうと考えて上記を確認した)

elif os.name == 'posix':
    if 'SPATIALINDEX_C_LIBRARY' in os.environ:
        lib_name = os.environ['SPATIALINDEX_C_LIBRARY']
    else:
        lib_name = find_library('spatialindex_c')

    if lib_name is None:
        raise OSError("Could not find libspatialindex_c library file")

    rt = ctypes.CDLL(lib_name)
else:
    raise RTreeError('Unsupported OS "%s"' % os.name)

lib_nameが定義できていなかった(環境変数の中にSPATIALINDEX_C_LIBRARYがなく、またspatialindex_cというライブラリも見つけられなかった)ためにエラーとなっていたことがわかった。

ターミナルでPythonを立ち上げて、ローカル環境だとどうなっているか確認してみた。

>>> import os
>>> os.name
'posix'
>>> os.environ
environ({'TERM_PROGRAM': 'Apple_Terminal',(後略)

>>> from ctypes.util import find_library
>>> find_library('spatialindex_c')
'/usr/local/lib/libspatialindex_c.dylib'

os.environでSPATIALINDEX_C_LIBRARYがなかったことから、ローカル環境ではlib_name = find_library('spatialindex_c')の方が実行されたのだろうこと、またspatialindex_cはローカル環境だと/usr/local/lib/libspatialindex_c.dylibにあるのだということがわかった。

検索してみると、ローカル環境で同じような問題にぶつかっている人がいた。Homebrewでspatialindexをインストールしたら解決したとされており、$ brew listで確認してみるとインストール済みだった。そのため、ローカルでは問題が起きなかったのだろうと思った。

3- どうしたら本番環境でも参照できるようになるか考える

ローカル環境では該当ファイルが存在していたため参照できたが、本番環境には該当のファイルがないためエラーになる。
pipでインストールできるファイルならrequirements.txtに書いておけるけど、そうではなさそうだ。それなら本番環境にもそのファイルを置いて、参照できればとりあえずは解決できるのではと考えた。

/usr/local/lib/libspatialindex_c.dylibをコピーして、本番環境のプロジェクト直下にlibディレクトリを作り、置いてみた。

これが正しい行為なのかわからないけど、環境変数にパスを設定してみた。
$ heroku configで、登録できていることが確認できた。

(前略)
SPATIALINDEX_C_LIBRARY: lib/libspatialindex_c.dylib

この修正をしても同様のエラー画面だったため、再度、$ heroku logs --tailでログを確認した。

(前略)
2019-12-30T06:38:08.950338+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/site-packages/rtree/core.py", line 127, in
2019-12-30T06:38:08.950339+00:00 app[web.1]: rt = ctypes.CDLL(lib_name)
2019-12-30T06:38:08.950340+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.7/ctypes/__init__.py", line 364, in __init__
2019-12-30T06:38:08.950342+00:00 app[web.1]: self._handle = _dlopen(self._name, mode)
2019-12-30T06:38:08.950344+00:00 app[web.1]: OSError: lib/libspatialindex_c.dylib: invalid ELF header

表示が前回と変わっている。
最初のエラーログで出ていたraise OSError("Could not find libspatialindex_c library file")はなく、次の行のrt = ctypes.CDLL(lib_name)が表示されているので、libspatialindex_cを見つけること自体はできたと考えてよいのだろうか。

エラーの最後の行で、invalid ELF headerと表示されている。検索すると、違うOSでのビルドだから読み込めない場合に表示されるという記載があった。

「2- ローカルではエラーが起きなかったのは何が違うのか確認する」に記載した.venv/lib/python3.7/site-packages/rtree/core.pyで、もう少し上の方に以下のような記載箇所がある。

if 'SPATIALINDEX_C_LIBRARY' in os.environ:
  lib_path, lib_name = os.path.split(os.environ['SPATIALINDEX_C_LIBRARY'])
  rt = _load_library(lib_name, ctypes.cdll.LoadLibrary, (lib_path,))
else:
  rt = _load_library('spatialindex_c.dll', ctypes.cdll.LoadLibrary)
if not rt:
  raise OSError("could not find or load spatialindex_c.dll")

このspatialindex_c.dllの方が必要なのかなと思い(多分違う)、探したが見つけられなかった。

4- いったん諦める

いったん諦める。もう少し知識がついたらまた考えてみる。

暖かいルートを検索するWebアプリを作ろうとした話_作業経過

初めてWebアプリを作ろうとして、方法の検索からデプロイ(失敗した)まで色々試行錯誤したので、経過を記録しておく。

目的

そもそも作ろうと思ったのは以下のようなことが理由だった。

  • 途中まで作った日陰の計算を一般的に使えるものにしたい
    以前、道路にかかる影の割合を計算して、任意のルートの涼しさを調べようとしたことがあったので(「夏には日陰を歩きたい」という欲望を満たすために計算する)、これをローカルで実行できるだけでなく一般的に使えるようにしたいと考えた。
    考えた時点ですでに夏が過ぎていたので、涼しい道でなく暖かい道を調べられるよう、直射日光の量を計算するように変えた。

  • Webサイトの仕組みについて、雰囲気だけでもつかみたい
    「ブラウザからリクエストが来たら、なんか処理して、レスポンスが返されるんでしょ…?」という非常にふわっとした理解だったので、「なんか処理」の部分がどのように作られているのか少しでもイメージできるようになりたいという気持ちがあった。
    普段仕事をしていても、エンジニアやデザイナーが何をして、サイトの表示やアカウントの登録等ができているのか、それを実現するコードがどのようなものなのか想像ができなかった。一緒に仕事するにあたり、詳しくはわからなくても、概略をイメージできるとよいなあという気持ちがあったので、自分で作ってみることにした。

できたこと

緯度経度と日時を入れたら、暖かいルートを表示する仕組みを作った(ローカルでは動く)

できなかったこと

ライブラリがうまく扱えず、デプロイができなかった
※うまくできなかった部分の詳細については、こちらに記載した。

作業経過

1- 調べる : 1h
「Pythonでweb上で動く仕組みを作るにはどうしたら良いのかな〜」と思い、そのまま「Python Webアプリ」と検索した。
DjangoとかFlaskというのが有名で、Djangoの方が色々やれることが多いらしいとわかった。自分のやりたいことに対してFlaskでもできるかの判断がつかず(多分できた)、Djangoでやってみることにした。

2- Djangoの学習をする : 24h
最初にDjangoのチュートリアルをやった。
丁寧に手順を追ってくれていてその通りに操作をすることはできたけど、各操作が何を目的としたものなのかや、全体的なファイル構成とかがわからなかったので、下記のような感じで各ファイルのつながりをメモしながら再度なぞってみたら少し理解ができた。

この時点で実際に自分の作りたいものに着手して、よくわからなくなったら本や動画を参照する形で進めた。以下を参考にさせていただいた。
本:現場で使える-Django-の教科書《基礎編》
動画:【3日でできる】Django 入門

3- 暖かいルートを計算するにはどうしたら良いか考える : 18h
以前計算した道路にかかる影の割合(「夏には日陰を歩きたい」という欲望を満たすために計算する)をベースに、暖かいルートをどのように計算したら良いか考えた。
結果、厳密には正確ではないだろうが「距離当たりの体の受ける直射日射の量」で代替することにした。太陽から降り注ぐ日射のうち、角度のついた鉛直面の日射量を計算する方法を本やネット上の論文から調べた。また、計算に用いる道路の緯度経度をOpenStreetMapから得て、各道路の方位角と長さを求めた。
大まかには、以下のような計算を行っている。コードはこちら

4- コードを書く : 28h
日射の計算のコードを書いていたのが10hくらいで、残りはDjango関係のコードを書いていた。地図の表示や、Djangoでの画像の表示がうまくいかず、時間がかかった。
コードはとりあえずこちらに置いてある。

5- デプロイする(失敗) : 22h
最初は別サービスを使ってデプロイしようとしたがうまくいかず(色々いじっているうちになぜかpipがうまく働かなくなり、最終的にOSの再インストールに至った)、Herokuを利用して進めていった。
Herokuのアカウントを作って、チュートリアルをやった。丁寧に説明されていて、例となるコードをクローンして進める形だったので静的ファイルの取り扱い等も参考にでき、初めてでもすごくわかりやすかった。
結局、ライブラリの扱いの部分でうまくいかず、年内の完成を目指していたのでいったん諦めることにした。
※うまくできなかった部分の詳細については、こちらに記載した。

やってみてどうだったか

当初の目的に対し、どうだったかをふりかえる。

  • 途中まで作った日陰の計算を一般的に使えるものにしたい
    → 結局デプロイできなかったので、一般に使えるものにならなかった。悔しいので、またもう少し理解が深まったら、つまづいた部分を解決してデプロイまで至るようにしたい。

  • Webサイトの仕組みについて、雰囲気だけでもつかみたい
    → (Djangoの場合の)各ファイルがどのように関係して、サイトが表示されるのか(MTVモデル)がうっすらわかった。Djangoの内部でどのような処理が行われているかはわかっていないので、「フレームワークすごいな〜」という気持ちになった。Webサイトの作りについて、さわりだけ知れたかな…という感じ。

何に時間を使ったか_2019上期

統計検定を受けた時のふりかえりで「何に時間をかけているか意識をしよう」と考えてから、ちまちまと記録をしていたので、半年間の時間の使い方をふりかえってみる。
以下、自分で「学び」系の区分に入れていた時間の内訳を記載する(なので、一般に「これは学びというより趣味なのでは…」という部分も含まれる)。

⑴ データ分析関係

  • 分析手法を学ぶ 18h 7冊
    データ分析の手法や、業務として分析を進めていく方法などについて書かれている本を読んだ。読んだ中では、以下の本が一番実際の業務に当てはめながら読めて、学びが多かった。
    ビジネス活用事例で学ぶ データサイエンス入門

  • 機械学習について学ぶ 15h 2冊
    scikit-learnのライブラリを利用して実行する方法と、機械学習に使われている数学の基礎について書かれた本を途中まで読んだ。半年で15hと、お前やる気あるのかという状態なので、来期はもう少しまじめに勉強する。
    機械学習を理解するための数学のきほん
    Pythonではじめる機械学習 scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎

  • 実際のデータで試す 62h
    上記で学んだ手法を実際のデータで試して遊んでいた時間。けっこう時間かけている割には、ちゃんとまとめておらず、ちょっと試してみたJupyter Notebookが散逸しているような状態で、自分の中に残っているものが少ないのが反省点。

⑵ エンジニアリング関係の学び

⑶ 業務理解

  • ビジネス系の本を読む 34h 16冊
    周りの人がごく普通に感覚として持っていることでも、自分は知らないなと思い、なるべく業務に関係しそうな本を読んだ。

この辺は、知識として読んでよかったなと思う。
カスタマーサクセス――サブスクリプション時代に求められる「顧客の成功」10の原則
サブスクリプション――「顧客の成功」が収益を生む新時代のビジネスモデル

逆に、直接的に業務とつながるわけではないけど、「面白い」と思ったのはこの辺り。
TRUST 世界最先端の企業はいかに〈信頼〉を攻略したか
Airbnbが登場当初、「見知らぬ人の家に宿泊する」というアイデアへの理解・信頼を得るため、どのようなサイト構成にしたのかという部分が興味深かった。

すいません、ほぼ日の経営。
“いまは「あなたはなにもしなくていい」という商品ばかりが売れる時代です。でも手帳は、あなたがなにかをしなければいけない商品です。使いながら完成させていくものです。ぼくらは、いわば未完成品を売っているわけで、それを完成品にするのは使う人です。”

色々条件は異なるけど、作ってもらうという部分は今の仕事で扱うサービスと共通で、なんかヒントがありそうだなと思った。

  • インタビューについての本を読む 17h 3冊
    社内で分析課題について探る時、「その業務に精通している人から的確に話を聞いて、分析課題に落とす」ことが上手くできていないなという気持ちから、インタビューにヒントがないだろうか、と考え読んだ。メモを取ってまとめたは良いものの、そのままになってしまっていたので、見返して日々に活かすにはどうしたらよいかを考える必要がある。

マーケティング・インタビュー 問題解決のヒントを「聞き出す」技術
ユーザーインタビューをはじめよう ―UXリサーチのための、「聞くこと」入門
インタビュー 木村俊介

⑷ アウトプット

仕事でPythonに触れる割合が減ったので、忘れないようにという意味合いもあって行った。作成のふりかえりはすでに書いた。
はじめてのLTで緊張したけど、一人で作っていたものについて人が話を聞いてくれて、感想まで教えてくれるってすばらしいなと思った。

ブログへのまとめに時間がかかっている。本編より長い。まとめ出すとよくわかっていない部分に気づいて調べたり、どう書いたらわかりやすいのか考えたりとかしているうちに時間がかかってしまっている状態。もう少し短縮したい。

⑸ その他

  • ふりかえり 14h
    主に「今週のふりかえり」(何をしたか、何を考えたか)をしていた時間。平均0.5h/週。今のところ「やっててよかった」と思う機会は訪れていない。

  • 色々考える時間 23h
    仕事/仕事外両者について、「こういう分析やれないかな」「やるには具体的にどうしたらいいか」等考えていた時間。のはずなんだけど、しっかりまとめてメモしていなかったせいで、かかっている時間の割には実りが少ない。平均1h/週。

  • その他 28h
    Pythonのライブラリ入れてうまくいかなくて調べたり、疑問に思ったことを調べたりしていた時間。

  • 不明 26h
    「何やったか書くのめんどいな…」と思ってその場で書かないとこうなる。⑴〜⑶のいずれかに入ると思われる。

ふりかえっての所感

記録を取ることで時間に対する感覚が転換した

以前から、時間の使い方は毎日記録していた。ただ、それは睡眠x時間、仕事y時間…という項目別のレベルの記録だった。それだと、学びに対して「今週はx時間かー」というふりかえりになり、「いかに学びにかける時間を増やせるか」という見方になっていた。
それが学びの中身を具体的に記録するようになったら、「この本読むのにもうx時間かかってるのか…急ごう」となり、「いかに短い時間で学べるか」という見方に変わった。
どちらも「記録する」という点では同じなのに、自分の見方が逆に変わったのは面白いなと思う。

漫然と学んでしまっている

その週、仕事で触れていた分野だったり、図書館で偶然借りた本だったりを学んでいることが多かった。計画性に欠けていた。
半年単位で集計すると数十時間かけていたことでも、ふりかえると「こんなにかけてた…?ていうか何も得てなくない…?」と思う分野もあった。特に「本を読んだ」分野だと、メモを取っていないと内容がほぼ記憶に残っていない(メモを取っていても、鮮明には残っていない)。

勉強すること自体には意味がない

そう、「勉強すること」自体には意味がないのである(※ 娯楽として勉強する場合は別として)。今の自分の学びは、基本的に「データ分析の観点から役に立てるようになる」ためにやっているので、学んで「なるほど」で終わったら意味がないのである。この点の意識が不十分だったなと思う。

半年あったら、もっとできるんじゃねーの、と思った

総量的な意味でも、時間あたりの質的な意味でも、ふりかえるともっとできるよね?という感じ。

図書館を利用することの弊害が出ている

8割方の本は図書館で借りさせていただいて読んだ。図書館で借りることのメリットは、返却期限があることで、強制的に勉強する期限が設定できることだ。
一方、図書館で借りて「腰を据えて勉強しよう」と思った本は買って読むんだけど、そうすると「いつでも読める」と思ってしまう。結果、自分にとって重要度の高い本を読まずに、返却期限のある本を優先することになってしまっていた。

バックアップはちゃんと取ろう

6/29、PCが壊れ、ここ半年で学んでローカルに保存していたファイルやデータベースが消えた。バックアップの重要性が身にしみた。この記事もスマホで書いているけど、画面が小さくてつらい。

で、これからどうするの?

  • もう少しこまめにふりかえって、軌道修正するようにする
  • 学んだ内容をちゃんと文に落とす
  • 図書館で本を借りるのはもう少し抑える
  • 理論面、技術面を真剣に学ぶ

「『夏には日陰を歩きたい』という欲望を満たすために計算する」のふりかえり

やろうと思った目的

  • GW10日間チャレンジ
    今回、10日間という例年にない長期休みだったため、普段の勉強とは違い、何か具体的な課題に取り組みたいと考えた。

  • 課題への時間の見積もりができるようになりたい
    業務で分析課題を行う際、「どのくらいでできるか」と問われてもなかなか答えられなかった。これは、経験値が少ないことも当然あるのだけど、自分が「何の作業にどのくらいかけているか」をあまり意識できていないこともあると思った。
    今回、解決までが見通せていない課題に取り組む中で、どのような過程で何を考え、何に何時間かけたかを記録して、時間感覚を得たいと考えた。

  • 見えていないものを見える化してみたい
    普段漠然と感じていることを数字に落とし、それを視覚化することで容易に理解できる形にする。すでにある数字から何かを作るのではなく、数字化する部分からやってみたいと思っていた。

当初目標としていた内容

当初は、地図の画像を読み込んで建物を検出し、それぞれに高さを与えて任意の時間における影を地図に重ねて描きたいと考えていた。
結果的に、以下の理由により断念した。

  • 建物の輪郭だけを得るのが大変
    無料で使用できる白地図が見つからなかったので、OpenStreetMapを利用させてもらうことを考えた。だが、自分の技量不足により、建物の輪郭を検出する際に、地図上の建物名や通り名等も検出されてしまい、うまくできなかった。

  • 建物高さを得るのが大変
    とりあえず自分の行動範囲だけでよかったので、Googleマップ等で目視で調べようかと思っていた。だが、GW中に完成させるという目標に対し、それをしている時間がなかった。また、地図上の建物と高さを機械的に紐づける方法も思いつけなかった。

8日目の時点で諦め、目標を「単純化した建物モデルに対し、道路の方位角ごとに影のでき方を調べる」に変更した。

作業の変遷

開始時点では、以下のような認識だった。

  • 各建物のコーナー部の座標が得られれば、そこから影は求められそう
  • 建物の色と地の色に差があれば、画像処理でうまく建物だけ検出できないかな? 画像処理ってどうやるのかわからないけど
  • 著作権の問題で、Googleマップとかは使えないかもなあ… 無料で使える地図ってないのだろうか

そのような認識から始めたので、作業は以下の3分野を並行して進めていくことになった。

  • 地図から建物外形を得る:地図に描かれた建物の枠線を検出しようとしていた。建物名や通りの名前が入った状態の地図から枠線をうまく検出できず、最終的に断念した。

  • 建物の座標を得る:テスト用の図を自分で用意し、その図の中の建物の枠線を検出し、コーナー部のみになるよう間引き、その座標を得ることができた。

  • 影を計算して描く:建物のコーナー部の座標に対し影の位置を計算し、影の枠線を描き、道路のうち影の重なる面積を求めた。また、ある経路の方位角を求め、日陰になる割合を求めた。

3分野ごとの作業内容を時系列で示すと以下の通り。

円の面積はかかった時間に比例している。また、灰色の円は結果的に不要だった作業を示す。
かかった時間は全体で60時間、そのうち結果的に不要だった作業は9時間くらいだった。また、色々調べたり検討したりしていたのが12時間、コードを書いていたのが48時間くらい(後者はコードを書くために調べていた時間も多いとは思う)だった。60時間と聞くと「かかりすぎだな」と思うけど、上図のように一つずつの作業に分解すると、「今の自分の力だとそのくらいはかかるか…」という気もする。

次回の改善点

  • 作業に取りかかる前にゴールを明確に設定する
    今回、8日目の段階で当初の目標を断念して目標を変えたため、結果的に無駄になる作業が出たり、回りくどいコードになったりしてしまった。開始当初の時点で「現時点の知識からして、10日間でこの目標を達成するのは難しそう」と感じていたので、変更した目標を当初から目標としていれば、もう少し短時間で目標を達成できたと思う。

  • もう少し系統立てて進められるようにする
    やり方を調べるところから始める部分が多く、最後の数日になるまではずっと混沌の中で進めていた。
    「今どこまでできていて、何が課題なのか」を脳内で考えるだけでなく書き出して進めていけば、自分の脳内を整理する意味でもよかったかなと思う。

感想

  • 普段の土日を何週か使って進めていたら、たぶん途中でやめていた。「遊びのコード書いてないで、普段の業務に直結することやろう…勉強したいこともたくさんあるし」となっていたと思う。「この10日間で完成まで持っていく、何らかの形にする」と決めてやったことで、集中してやることができた。
  • 段階ごとに自分で小さな問いを立てて、それをクリアするとゴールに近づいていく嬉しさがあった。今回の課題は、さらにそれが現実世界とつながっている面白さみたいなものもあったと思う。
  • 楽しかったけど、やっている最中は「どこが間違っているかわからない」「落ち着け」「あと3日なのに、全然ゴールが見えない」「ここ直したいけど数時間かかるだろうし」とほぼ焦燥感の中にいた。他者が作ってくれたライブラリを利用させてもらっているだけで、しかもうまく使えてないし、色々わかってないし、と道のりの長さを感じるけど、足元に目を落として一歩ずつ淡々と進んでいくしかない。

「夏には日陰を歩きたい」という欲望を満たすために計算する

目的

昨年の夏、外出のたびに「暑い…少しでも日陰を歩きたい」と思っていた。出かける前に、道路の方位と太陽の位置から影の出来かたを脳内でなんとなくシミュレートするんだけど、だいたい実際の影とずれていた。
夏を迎える前に、時間帯ごとの影の位置を求めておきたい。何時に出かけるのがベストなのかを知った上で、覚悟を持って日差しの元に出て行きたい。
そう思ったので計算した。

作ったもの

まず、与えた建物と道路のモデルに対し、道路の何割が日陰になるかを求めるコードを書いた。
– 入力:求める地点の緯度経度、日付、建物と道路の平面図、建物の高さ
– 出力:建物と道路の平面図に影を重ねた図、道路の方位角ごとの影の割合を求めたグラフ

次に、この結果を使って経路の何割が日陰になるかを求めるコードを書いた。
– 入力:出発地点と到着地点の緯度経度
– 出力:時間帯ごとの影の割合を求めたグラフ

結果

先に結果を示す。

影の計算は、下図のような単純化した建物と道路のモデルを対象とした。4.0mの道路から1.0m後退した位置に、幅・奥行・高さが外法で6,000mmの建物が、1.0mの離れで立ち並んでいる状態とした。
モデル

道路の方位を22.5°ずつ変え、日の出〜日の入りまで、15分ごとに道路のうち影のかかる割合を計算した。(道路の方位は北を0°とした。南北に通る道路なら0°、北西-南東に通る道路なら45°となる) 地点は福岡市(ここでは福岡県庁の緯度経度を代表として利用)とした。

まずは、夏至(2019/6/22)の結果を示す。

  • 南中時刻には、どの方位でもほとんど影ができていない(太陽高度が約80°とかなり天頂に近い位置に太陽があるので、どの方位であっても影ができにくい)。
  • 方位によってかなり影の割合が異なることがわかる。例えば90°(東西の道路)だと、8時過ぎには影がなくなり、16時をすぎるまでそれが続く。一方、0°(南北の道路)だと、10時でもまだ半分以上影ができている。

  • 特徴的なのは67.5°と112.5°の時。前者を取り出して見てみる。
    67.5°

太陽方位と道路の方位が一致、あるいは180°となる(つまり影のできる方位と道路が平行になる)のが日が出ている間に2回あり、その前後は道路に全く影ができない状態になる。
南中後、他の方位だとどんどん影の割合が増えるのに比べ、16時頃を頂点として影の割合が増えた後は太陽方位が道路の方位に近づくのに比例してまた影が減っていっている。

次に、真夏(2019/8/1)の場合の結果を見てみる。

1ヶ月強で、けっこう変化があることがわかる。
方位角と時間帯によって影の割合が違うので、すごく頑張って毎日方違えとかすれば、日差しを避けて生きていけそう。

方位角と時間帯ごとの影の割合がわかったところで、目的地まで至る経路上の影の割合を時間ごとに求めてみる。
今回は緯度経度の代表として利用した福岡県庁から、天神駅までの経路を対象とした。最短経路の方位と距離を求めて、方位については22.5°ずつの方位で近似した。

route

この方位ごとの延長距離から、経路上の影の割合を時間帯ごとに求めてみた。

方位角に偏りのある経路を選んだので、一番長い方位角(135°)の形状を概ね踏襲した結果となった。

計算方法

Pythonで画像処理ができるOpenCVと地理情報を得られるosmnxを利用して求めた。
コードはJupyter Notebookの形式でGitHubに置いた

大まかな計算の流れは以下のとおり
1. モデルとなる画像を読み込み、建物の高さを画像のピクセル数に変換しておく
2. 道路の方位ごとの計算を行うため画像を回転させる
3. 建物の輪郭を検出、間引いてから座標化
4. 緯度経度・日付・時刻から太陽高度・方位角を求める
5. 建物の輪郭の座標ごとに影の位置を求め、それを図形化する
6. (建物と影を重ねた図を書き出し)
7. 影と道路の重なる割合を求め、グラフ化
8. ある経路上における道路の方位とその距離を調べる
9. 方位の割合に応じた時間ごとの影の割合を算出する

反省点/改善点

  • モデルは適当なの?
    正直十分ではないと思う。道路からの後退距離や隣の建物との離れなど、もっと色々なパターンを用意したモデルの平均値を取るなどした方がよかったかもしれない。例えば、今回のモデルでは隣の建物との離れの位置を道路の両側で同じにしているけど、これを互い違いにしたモデルで計算したら影の割合が10%程度違う時間帯もあった。

  • 建物の高さが均一というのはどうなの?
    道路の幅と建物の高さは、ある程度一定(幅の広い道路沿いほど建物の高さは高い)となる傾向にあるのでは、と思う。そうすると、高層地域と低層地域で時間帯間で比べた影の割合は似た傾向になるのでは…という仮説になった。(経路上に高層地域と低層地域両方がある場合は上記の仮説は成り立たなくなるので、現実的ではない)
    今回、GWの10連休中に作り切る、と決めていたので、かなり乱暴な近似をしているけど、影のでき方の傾向性を見る程度には使えるのでは、と考えている。

  • 道路全面に対する影の割合を計算しているけど、真ん中を歩くことは実際にはないよね?
    ある程度幅の広い道路なら、中心でなく端の方を歩くので、道路の端1.0m程度での影の割合を計算する方が実際的だったと思う。ただ、そうすると左右の端ごとの計算が必要になり、結果が複雑になるので今回はやらなかった。

  • ある程度幅の広い道路だと、街路樹があるよね?
    街路樹は今回考慮に入れることができなかった。建物は垂直方向の遮蔽物だけど、樹木は水平方向の遮蔽物で、影のできる面積がかなり大きいので、本当は考慮に入れたかった。幅の広い道路に対しては、x[m]おきにy[m]の高さの円錐状の木のモデルが立っている、というようにすれば良いかもしれない。

  • 隣の建物にかかった影の形が再現できていない
    隣の建物の壁に影がかかると影の形が変形するけど、それは再現できていない。ただ、影を遮った隣の建物による影もあることを考えると、道路にかかる影の量は結果的に変わらないはずと考えている。

  • わざわざ画像として読み込まなくても、もっと簡単にモデルを作れたんじゃない?
    たぶんそうだと思う。当初は実際の地図から建物を読み込んでそれに対する影を計算しようとしていたので、そういう回りくどい形式になってしまった。1回計算するのに20分くらいかかる。

  • コードが整理できていない
    そう思う。後日整理する。

その他全般的な反省は別記事に書いた。
→ 「『夏には日陰を歩きたい』という欲望を満たすために計算する」のふりかえり

MySQLでCSVファイルからデータベースを作る

MySQLをインストールして、CSVファイルからテーブルを作るところまでやってみた過程を記録する。
きっかけは、趣味で計測している時間の使い方記録が11,000行を超え、Excelによる取り回しが重くなってきていたこと。CSVのままPythonで操作するのでもよかったけど、せっかくなのでデータベース化をやってみた。

バージョン

  • macOS 10.13.6
  • MySQL 8.0.15

具体的な操作

以下、リンク先は公式ドキュメントの参照したページ

1 – MySQLのインストール

まず、homebrewでMySQLをインストールした。

$ brew install mysql

(前略)
==> Caveats
We've installed your MySQL database without a root password. To secure it run:
mysql_secure_installation

MySQL is configured to only allow connections from localhost by default

To connect run:
mysql -uroot

To have launchd start mysql now and restart at login:
brew services start mysql
Or, if you don't want/need a background service you can just run:
mysql.server start
(後略)

一番目に書かれている$ mysql_secure_installationをやろうとする。

$ mysql_secure_installation

Securing the MySQL server deployment.

Enter password for user root:
Error: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

パスワードの設定をしていなかったので、Enter password for user root:でそのままEnterを押したが、エラーになった。

can-not-connect-to-server

A MySQL client on Unix can connect to the mysqld server in two different ways: By using a Unix socket file to connect through a file in the file system (default /tmp/mysql.sock), or by using TCP/IP, which connects through a port number.

とあり、接続方法の一つであるソケット接続に失敗したようだ。

結局、MySQLの起動($ mysql.server start)が必要だったようで、その後なら$ mysql_secure_installationを行えた。

$ mysql.server start

Starting MySQL
........ SUCCESS!
$ mysql_secure_installation

Securing the MySQL server deployment.
(長いので後略)

これにより、どのようにセキュリティが向上するかがmysql-secure-installation に書かれている。

  • anonymousユーザー(匿名ユーザー)の削除
  • リモートホストからroot ユーザー(MySQLの操作に対し全権限を持つユーザー)でのログイン禁止
  • testデータベースの削除

などを行った。

2 – MySQLに接続する

$ mysql -u user -pという形で接続する。-uでユーザー名を指定し、-pとすると Enter password: というプロンプトが表示されるのでパスワードを入力する。

rootユーザーでつないでみる。

$ mysql -u root -p

Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 8.0.15 Homebrew

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

つながった!

どんなデータベースがあるか見てみる(testデータベースが消えていることが確認できる)。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.06 sec)

3 – データベースを作る

lifeというデータベースを作り、その中にtime_bookというテーブルを作成する。

データベースを作る

mysql> CREATE DATABASE life;
Query OK, 1 row affected (0.08 sec)

テーブルを作る

mysql> CREATE TABLE life.time_book(
-> id INTEGER PRIMARY KEY AUTO_INCREMENT,
-> start_datetime DATETIME NOT NULL,
-> end_datetime DATETIME NOT NULL,
-> duration TIME NOT NULL,
-> content VARCHAR(24) NOT NULL,
-> memo VARCHAR(60)
-> );

Query OK, 0 rows affected (0.69 sec)

CREATE TABLE db_name.table_name(col_name data_type, …);という形で作れる。

  • data_typeはデータ型 今回使用しているのはDATETIME(日付と時刻),TIME(時刻),VARCHAR(可変長文字列)
  • NOT NULL:そのカラムにはNULLを格納することができなくなる
  • PRIMARY KEY:主キー制約 行の一意性を確保する
  • AUTO_INCREMENT:連番を自動的に振る

テーブル一覧を確認する

mysql> SHOW TABLES FROM life;
+----------------+
| Tables_in_life |
+----------------+
| time_book      |
+----------------+
1 row in set (0.09 sec)

テーブルのカラム一覧を確認する

mysql> SHOW COLUMNS FROM time_book FROM life;
+----------------+-------------+------+-----+---------+----------------+
| Field          | Type        | Null | Key | Default | Extra          |
+----------------+-------------+------+-----+---------+----------------+
| id             | int(11)     | NO   | PRI | NULL    | auto_increment |
| start_datetime | datetime    | NO   |     | NULL    |                |
| end_datetime   | datetime    | NO   |     | NULL    |                |
| duration       | time        | NO   |     | NULL    |                |
| content        | varchar(24) | NO   |     | NULL    |                |
| memo           | varchar(60) | YES  |     | NULL    |                |
+----------------+-------------+------+-----+---------+----------------+
6 rows in set (0.06 sec)

4 – CSVファイルからデータを挿入する

まずデータベースを選択する。

mysql> use life;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

CSVを準備する
~/time_book.csv

id,start_datetime,end_datetime,duration,content,memo
1,2017-01-02 09:45,2017-01-02 10:34,00:49,'移動',
2,2017-01-02 10:34,2017-01-02 11:34,01:00,'家事','料理'
…

データをローカルから読み込もうとする。

mysql> LOAD DATA LOCAL INFILE '~/time_book.csv'
-> INTO TABLE time_book
-> FIELDS TERMINATED BY ','
-> IGNORE 1 LINES;

ERROR 1148 (42000): The used command is not allowed with this MySQL version
  • FIELDS TERMINATED BY ','はカンマ区切りを指定している
  • IGNORE 1 LINESはCSVの1行目がカラム名なので、読み込まないようにするため

エラーになった。
load-data-localを参照すると

  • セキュリティの問題から、デフォルトではLOAD DATA LOCALを使えないようにしてある
  • サーバー側、クライアント側それぞれで許可の設定が必要

なことがわかった。

サーバーサイド

The local_infile system variable controls server-side LOCAL capability. Depending on the local_infile setting, the server refuses or permits local data loading by clients that have LOCAL enabled on the client side. By default, local_infile is disabled.

option-modifiers

The “enabled” form of the option may be specified in any of these ways:
(中略)
–column-names=1

とあるので、1にすれば有効になる。

mysql> SET PERSIST local_infile = 1;
Query OK, 0 rows affected (0.08 sec)

SET文でシステム変数を変更できる(再起動すると設定した内容が失われる)。 SET PERSISTでパラメーターの値を設定すると、再起動後も値が保持される。

mysql> SELECT @@local_infile;
+----------------+
| @@local_infile |
+----------------+
| 1              |
+----------------+
1 row in set (0.00 sec)

クライアントサイド

For the mysql client, local data loading is disabled by default. To disable or enable it explicitly, use the –local-infile=0 or –local-infile[=1] option.

接続時に–local-infile=1と指定する
$ mysql -u root --local-infile=1 -p

サーバー側、クライアント側それぞれで設定できたので、改めてLOAD DATA LOCAL INFILEしたところ、先ほどのエラーは出なくなったが、以下の結果になった。

Query OK, 0 rows affected (0.10 sec)
Records: 0 Deleted: 0 Skipped: 0 Warnings: 0

OKって出てるけど、Records: 0ということはつまり…中身を確認する。

mysql> select * from time_book;
Empty set (0.04 sec)

やっぱり読み込まれていない。

試行錯誤しながら問題がある部分を修正していったので、以下1つずつ示す。

  • datetimeとtimeの型の部分を””で囲っていなかった

最初はLOAD DATA LOCAL INFILEでやっているのが原因かと思い、試しにinsert文で読み込めるかやっていた中で気づいた。

mysql> insert into time_book (start_datetime,end_datetime,duration,content,memo) values(1990-01-01 00:00, 1990-01-01 00:01, 00:01:00, "a","b");

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '00:00, 1990-01-01 00:01, 00:01:00, "a","b")' at line 1
mysql> insert into time_book (start_datetime,end_datetime,duration,content,memo) values("1990-01-01 00:00", "1990-01-01 00:01", "00:01:00", "a","b");
Query OK, 1 row affected (0.03 sec)

“”で日時を囲ったら読み込めた。ごく基本的な話なんだけど、わかっていなかった。
CSVファイルを修正し、LOAD DATA LOCAL INFILEにFIELDS ENCLOSED BY "'"を追加した(これは要素を囲むのに’ ‘を使っていることを指定している)。

  • 文字コードを変えた

まず、MySQL側での文字コードを確認する。

mysql> SHOW VARIABLES LIKE 'char%';
+--------------------------+------------------------------------------------------+
| Variable_name            | Value                                                |
+--------------------------+------------------------------------------------------+
| character_set_client     | utf8mb4                                              |
| character_set_connection | utf8mb4                                              |
| character_set_database   | utf8mb4                                              |
| character_set_filesystem | binary                                               |
| character_set_results    | utf8mb4                                              |
| character_set_server     | utf8mb4                                              |
| character_set_system     | utf8                                                 |
| character_sets_dir       | /usr/local/Cellar/mysql/8.0.15/share/mysql/charsets/ |
+--------------------------+------------------------------------------------------+
8 rows in set (0.00 sec)

utf8mb4って何…?と思い内容を見てみる。

mysql> SHOW CHARACTER SET LIKE "utf8%";
+---------+---------------+--------------------+--------+
| Charset | Description   | Default collation  | Maxlen |
+---------+---------------+--------------------+--------+
| utf8    | UTF-8 Unicode | utf8_general_ci    | 3      |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4      |
+---------+---------------+--------------------+--------+
2 rows in set (0.00 sec)

utf8mb4もUTF-8の一種っぽい。

csvファイルの文字コードを確認する

$ file --mime time_book.csv

time_book.csv: text/plain; charset=unknown-8bit

UTF-8でなかったので、$ iconvで文字コードを変えようとする。

$ iconv -f unknown-8bit -t utf8 time_book.csv > time_book.csv

iconv: conversion from unknown-8bit unsupported
iconv: try 'iconv -l' to get the list of supported encodings

unknown-8bitという文字コードは対応していないと言われる。$ iconv -lで見てみると、確かにunknown-8bitというのはない。$ iconvでは出来ないのか…と思ったが、unknown-8bitはshift_JISのことのようだとわかった。

$ iconv -f shift_JIS -t utf8 time_book.csv > time_book.csv
$ file --mime time_book.csv

time_book.csv: text/plain; charset=utf-8

CSVファイルをUTF-8に変換できた。

  • 改行コードを指定していなかった

上記を修正しても、LOAD DATA LOCAL INFILEで1行目しか読み込まれなかったことから気づいた。
LOAD DATA LOCAL INFILEにLINES TERMINATED BY '\r'を追加した。

  • (これは、読み込みができなかったのと直接関係しないが)id列について、auto_incrementなのに数字を入れていた

mysql> SHOW COLUMNS FROM time_book FROM life;で見ると、id列のNullがnoになっていたので、auto_incrementだけど数値を入れていた。
example-auto-increment

No value was specified for the AUTO_INCREMENT column, so MySQL assigned sequence numbers automatically. You can also explicitly assign 0 to the column to generate sequence numbers, unless the NO_AUTO_VALUE_ON_ZERO SQL mode is enabled.

とあったので、id列は0に変更した。

結果、CSVはこのような形になった。

id,start_datetime,end_datetime,duration,content,memo
0,'2017-01-02 09:45','2017-01-02 10:34','00:49','移動',
0,'2017-01-02 10:34','2017-01-02 11:34','01:00','家事','料理'

再度csvを読み込み

mysql> LOAD DATA LOCAL INFILE '~/time_book.csv'
-> INTO TABLE time_book
-> FIELDS TERMINATED BY ',' ENCLOSED BY "'"
-> LINES TERMINATED BY '\r'
-> IGNORE 1 LINES;

Query OK, 11348 rows affected, 1 warning (0.59 sec)
Records: 11348 Deleted: 0 Skipped: 0 Warnings: 1

読み込みができたっぽいぞ!

mysql> SELECT * FROM life.time_book LIMIT 2;
+----+---------------------+---------------------+----------+---------+--------------+
| id | start_datetime      | end_datetime        | duration | content | memo         |
+----+---------------------+---------------------+----------+---------+--------------+
|  1 | 2017-01-02 09:45:00 | 2017-01-02 10:34:00 | 00:49:00 | 移動    |              |
|  2 | 2017-01-02 10:34:00 | 2017-01-02 11:34:00 | 01:00:00 | 家事    | 料理          |
+----+---------------------+---------------------+----------+---------+--------------+
2 rows in set (0.00 sec)

できた!

感想

なるべく公式ドキュメント(英語)のみで理解しようとして進めたけど、けっこうつらかった。今回よりも古いバージョンなら日本語のドキュメントもあって、わからない場合はそちらも参照したけど、英語で読んでわからない場合は日本語でもだいたいわからず、英語力でなく知識不足が原因か、と思いつつ不明な単語等を調べて進めていった。

単に1つのテーブルを作るだけなのに各所で色々引っかかったけど、作業自体は全般的に楽しかった。やっとデータが読み込めた時はうれしくて、ひとり小躍りした。

楽しんで働くへの現時点の返答

前職にいた時、必要以上に真面目に取り組むことで許されようとする悪癖があった(今もちょっと残っている)。
期限に間に合わせるため、土日や年末年始にも非公式に職場にやってきて働くとか、そういう行動のことです。

この行動は、前職においておおむね批判されなかった。仕事量も人員数も調整がきかない場だったので、便利な存在という側面もあったと思う(そうやって働く人も多かった)1
一方で、その姿勢を諌めてくれる先輩もいた。「お前のまじめさは美徳なんかじゃねーからな」と。
だって、やるしかないじゃん、と思っていた。さらには、この日々は何らかの形で報われると勝手に思っていた。

3年目のある日、気づいた。
部長や課長、あるいは組織がこの頑張りに報いてくれるわけでないし、私の人生に責任を持ってくれるわけではない(当然、持つ必要もない)。
頑張りによる成果は彼らを喜ばせ、それは単純に嬉しいけど、それを自分の最終目的にしても、(頑張っている内容からして)自分に何かが残るわけではない。

この人生を生きているのは誰?
私だ。
まじか、そうかと思った。

目前の仕事だけでいっぱいな日々は、その間思考停止できて、実は楽だ。「でも今はこれをしなきゃ」が汎用的な言い訳になる。
でも、それを何年も続けていると、自分の価値は目前の仕事だけだから、その出来に精神状態が100%左右されるようになる。そして、頑張る見返りとして、自分の人生の責任を取るのは自分であるということを放棄したい心持ちになっていった。

(しつこいですが、ここまで前職での話です)

転職して数ヶ月、楽しんで働くってなんだ、とぼんやり考え続けていた2

まだ考えがまとまっていないけど、一つ考えたことは、真面目であることは、真剣なのと似ているけど違うということだ。真面目さは義務感から発している一方、真剣さはその対象を自分ごととして捉えることから発している。真面目さは深刻さへつながっていく一方、真剣さはユーモアと同居できる。
「どうしたらそれができるって思う?」「今の自分・状況で取りうる、最善の行動ってなんだろうね?」と自分に問いかけてやって、困難3にも、にいっと笑って、楽しんでやっていきたい。今、自分は事業にしっかり貢献できるだけの実力が持てていないけれど、その中でも、考えることはできるはずなのだ。

真面目さで評価されようとするよりも、真剣に取り組んで成果を出す方が厳しい世界なのは気づいている。
無意識に慣れた手法を取ってしまうけど、頑張り方も変えていきたい。1歩進んで、1歩下がって、の繰り返しだろうけど、それでも振り返れば進んでいる。


  1. 数年前の話なので、今は改善されている部分もある。また、強制されたものではない 
  2. 入った当初言われた「楽しんでやりましょう」の衝撃が大きくて、ずっと心の中にあった 
  3. ここでいう困難とは、あくまで現段階の自分から見ての話 他の人から見たらそんなの困難と呼ばないよ、というものも含む 

混乱した統計検定2級の用語をまとめる

統計検定2級の過去問を解いていた時、似た単語がよく出てきて「何でしたっけ…」となっていた。忘れないうちに、それらの単語を書き出してみる。

標本の抽出方法

母集団の要素全てに対して調べることは現実的にできない場合、その中からいくつか取り出して調べることになる。どのように取り出すのがなるべく偏りが出ないかというのと、調べるにあたってどれだけ手間をかけられるか、両者をどこでバランスさせるかによって、色々な手法がある。

単純無作為抽出法

母集団の要素から、どの要素も同じ確率となるよう無作為に抽出する方法。
– 母集団N個から標本n個を抽出する時に、各個体が標本として選択される確率n/N
– どのn個の個体の組も選択される確率が1/NCn
→ 10人から4人選ぶ時に、男2女2という条件がつくと、単純無作為抽出ではなくなる(層化無作為抽出法になる)

系統抽出法

要素全てに番号をふり、1つ目の要素は無作為に抽出し、2つ目以降は等間隔の番号を抽出する方法

層化無作為抽出法

性別、年代、…などで母集団が複数の層にわかれる場合に、いずれかの層に偏りが出ないよう、層ごとにランダム抽出する方法

多段抽出法

例)全国から複数の県を抽出、各県から複数の学校を抽出、各学校からクラスを抽出…というように絞り込んでいく方法
段数が多くなるほど、平均などの推定精度は悪くなる

層化多段抽出法

層化抽出法と多段抽出法を組み合わせたもの

クラスター(集落)抽出法

母集団を分割してクラスターを作った上で複数のクラスターを抽出し、その成員全てを対象とする方法
精度は低め

二相抽出法

調査しやすい項目をまず調査し、それを補助情報として調べたい項目の調査を行う方法(標本を抽出する操作を2度行う)

価格に関係して出てくる用語

ローレンツ曲線、ジニ係数

ローレンツ曲線:
分布を持つようなある事象の偏り(所得の偏りでよく出てくるので、その場合偏りが格差となる)がどの程度あるかを表す
完全に偏りがない場合、下図の点線(均等配分線という)で示す直線になる
ジニ係数:
均等配分線とローレンツ曲線で囲まれた面積の2倍が、縦軸・横軸で囲まれた面積に対して占める割合
0(ローレンツ曲線と均等配分線が一致)だと偏りがないことを示す 最大値は1となる

ラスパイレス指数

物価の変動を表す時によく使われる
下に示すように、基準時とそれと比較する時の価格の変化を基準時の数量をウエイトとして計算する

オッズ比

2つの群を比較した時に、起こりやすさを示す尺度
オッズ比が1だと2つの群の間に起こりやすさの差がないことを意味する

統計検定2級を受けた

先日、統計検定2級の試験を受けた。感触がいまいちだった1ので、結果が出る前にふりかえりを書いてしまおうと思う。(後日、運よく受かっていたことがわかった)

知識のついていく過程

基本的に、過去問2を解くことを中心に学習した。その合間に、公式のテキスト3を読んだり、理解が曖昧で何度も調べる部分をまとめたりした。
過去問の年度別に、正答を導けた割合の変化を時系列で見てみると下図のようになっている。

一応、どの年度も解き直しをする度に少しずつできる割合が上がってはいる。今見ると、解き直しでも8割程度しか取れていないあたり、理解や演習が足りていない様子が表れているなと思う。

大まかに次の3段階で理解が進んでいった。
– 初期:理解していることが自分の中で整理できていない状態。全体の5割は解説を読めば理解できたけど、3割くらいは解説を読んでも理解できなかった。
– 中期:公式テキストをざっと読んで自分の中で整理し、最低限公式的な部分は覚えてしまうことで、過去問の解説を読めばほぼ理解できるようになった。
– 後期:1度理解しただけでは自分で再現できないような、あやふやな部分を解き直した(が、おそらく身についてはいなかった)。

かけた時間

初見の過去問に対し正答を導けた割合と累積学習時間を重ねてみると、割と比例していることがわかる。おそらく、この分野に対する学習がまだ初期段階だから、やるだけ理解が上がっていく楽しい時期にいるんだと思う。

試験終了後に集計してみるまで、これほど時間を投入していたと思わなかった(合計で40~50時間の感覚だったのに、2倍くらい費やしている)ので、投入時間の割に自分の理解のレベルが低く感じ、数日悲しい気分になった。

いったいどの部分にこれほど時間がかかっていたのか?
体感だけど、過去問に下記の時間をかけていたと思う。


これに公式のテキストでの学習やまとめていた時間を足しても、おそらく60時間程度にしかならない。
約30時間が闇の中である4

統計検定の勉強による変化

良いこともあって、社内でやってもらっていた読書会5に使用していた本(統計学入門6)に対する見え方が変わった。
読書会中はだいぶ難しく感じて、「統計とかデータ分析やってる人ってみんな天才なのかな…?」「ていうか自分が向いていないだけか」と思ったし(今もこれはよぎる)、本に対し30回くらいは「いったい何言っているんだ…」と思った。統計検定の勉強をした後に見ると、理解できるようになっていて「かなりわかりやすく丁寧に書いてくれているな」「確かに入門の本だな」と思えるようになった(入門レベルだと判断できることと、その内容を完璧に理解できることはまた別ではあるが)。

学習へのふりかえり

今回、学習時間は記録していたけど、その中で何をしたかは記録していなかった。遊びとして記録していたから、最初にしっかり考えなかったけど、最後に何を見たいのかを考えて記録すべきだった。
今まで学ぶ量(時間)を増やそうという方向に意識が向かっていたと思う。でも時間は有限で、さほど若くないのに新たな分野を物にしようとするなら、何をしたのか、それに時間がどれだけかかったのか、もう少し自覚的になる必要があると思った。試しに1ヶ月、勉強した時間とその内容を記録してみようと思う。


  1. 自己採点だとほぼボーダーライン上だった。1〜2問足りずに落ちそうである(結果発表までどきどきが楽しめてお得だと思うことにする) 
  2. 日本統計学会公式認定 統計検定 2級 公式問題集/日本統計学会編 
  3. 改訂版 日本統計学会公式認定 統計検定2級対応「統計学基礎」/日本統計学会編 
  4. たぶん、理解できない部分についてずっと考えていたり、仕事帰りに勉強のため寄ったカフェで虚無状態になっていた時間が入っていると思われる 
  5. 読書会が相当役に立っていて、これがなかったら統計検定の勉強を途中で諦めていたと思う 
  6. 統計学入門/東京大学教養学部統計学教室 編 

デスクをホワイトボード化した話

ホワイトボード化までの思考回路

きっかけは、思いついたことをふせんにちまちま書いて壁に貼っていたことだった。
ふせんは狭い。ぼんやりと考えたこととかは切り捨てて内容をまとめ、収まるように調整してしまう。とりあえず書き出したい。余計なことを考えず、自由に書きたいのだ(幼児のお絵描きが画用紙を飛び出して壁や床に行われる時と同じ思考回路である)。

Amazonに壁に貼れるホワイトボードのシートがあったので、はじめはそれを机の横の壁に設置することを考えた。
でも、側面だと45度回転しないと書けない。新しいものを導入した時にちょっとした手間があると、結局活用しなくなってしまう。がんばらなくても使える状態にする必要がある。
どこならそのままの状態で目に入り、書けるか。

机の上だ。

作業内容

机の上にはキーボードや本を置いていたけど、物があるとインクがつくことを気にして自由に書けない。
そこで棚とキーボードの収納スペースを作って避難させた上で、机にホワイトボードのシートを設置した。


元の状態(見た目的にはこっちの方がすっきりしていて好き)
→ 改造後(なお、ホワイトボードに書いた計算は間違えている)

コストとしては、ホワイトボードシート・木材・金具類等で、7,000〜8,000円くらい。ホワイトボードシートだけだったら、2,000円しない。
散発的に進めていたけど、累計で、サイズを決めたり計画するのに3〜4時間、材料をネットやホームセンターで集めるのに5〜6時間(キーボードのスライド棚に使ったスライドレールについて調べるのに手間取った)、作業に4〜5時間くらいかかった。

やってみてわかったこと

  • 太いペンで、自由に書けるのは、気持ちいい。いらない紙に書いてその後捨てるのと原理的に変わらないはずなのに、それよりも書くことへのハードルが下がる。アイデアを考えるとか、何となく考えてることを発展させるとかいう用途に特に向いてるなと感じた。
  • キーボードは避難させたけど、ノートや本を広げる時は結局ホワイトボードの上になるので、それが不便。ノートを使うときには一旦消す必要があるな、とか、この書き込みはしばらく残しておきたいから端の方に書いておこう、とか本質と違うことを色々考えてしまう。そういう点では、壁設置に軍配が上がるなと思った。
  • 机が白いと、結構目が疲れる。天板が白い机って結構あるけど、白色度はあまり高くないのは、そういう理由もあるのかもしれない。
  • 線が太いので、文字や図が大きくなる。それが書くことの気持ち良さにつながっている部分もあるんだけど、この広さでも狭いなと感じてしまう。
  • 職場の机(白い)にナチュラルにペンで書こうとして、一人ではっとする。

まだ設置して間もないので、しばらく使ってみたら、新たな発見があるかもしれない。

判別分析についてわかったこと

今、判別分析について学んでいて、少しずつわかってきた(ような気がする)ので、現時点で理解した(と思っている)内容をまとめてみる。
※ 以下、私の理解不足による誤りがある可能性もある。

1  判別分析とは何か

  • ある計測・観測した値から、結果(どちらの群に属するか)を推測する手法
    例1)飲酒量や検査の値などから病気の発病を予測する
    例2)顧客の情報から商品の購入を予測する

例1)なら
– 目的変数:病気を発症する群と発症しない群
– 説明変数:飲酒量、検査の値など
となり、病気を発症する群としない群をなるべく精度良く分けるための数式を考える。そして、新たな人の飲酒量等の数値を数式に当てはめることで、その人が発症するかどうかを予測する。

2  どのように判別するか

主にマハラノビスの距離による判別と線形判別式による判別がある。

2-1  マハラノビスの距離による判別

各群の中心(重心)を求め、そこまでの距離が短い方の群に属するとする方法。距離は単純な距離でなく、各群のばらつきを考慮した(1標準偏差あたりとした)距離を使う(マハラノビスの距離という)。

以下、説明変数が一つの場合でまず考え、その後に説明変数が2つの場合に応用する。

2-2  線形判別式による判別

以下、説明変数が2つの場合を例として考える。
その2つの説明変数を2軸とした平面で考えると、最も良く群を分離する直線を引くことを考える方法。

3  判別の精度をどのように測るか

 

参考

以下の書籍で勉強させていただきました。

入門はじめての多変量解析
石村 貞夫 石村 光資郎
東京図書

 

 

川崎 智也 稲垣 具志 寺内 義典 石坂 哲宏
コロナ社

 

 

 


(感想など)
  • まだ不十分なので、後日追記(+修正)したい。あと、判別分析の説明変数がカテゴリデータだった場合である数量化二類についても追記したい。

  • もともと画像にすることを考えずに書いていたこともあって、思った以上に見づらい。ただ、数式の添え字等を打つのが手間がかかるので、画像をもう少し見やすくする方向で改善したい。

2ヶ月めにやった学習のふりかえり

具体的にやったこと

データサイエンス領域
「基礎統計学Ⅰ 統計学入門」

  • 1ヶ月めに引き続き、読書会形式で一緒にやっていただいた。先月より内容的に難しく感じる部分が増えて、進捗としては本の半分を超えた程度。
  • 数式を追っていくとそうなることはわかるけど、その結論は感覚的には納得できない、というような部分で引っかかっていた。
  • そういった部分について、具体的にPythonで計算してみたりした。結果、納得とまではいかなくても、やる前よりはその概念に対する理解を少し進めることができた。

データエンジニア領域
「Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理」

  • 上記の本を参考にしながら、Pandas等で何をすることができるか学び、実際のデータに適用してみて理解を深める、ということをした。
  • 先月やった1冊めの本(Pythonの基本的な使い方を網羅する本)では出てこなかったデータ分析のためのPythonでの書き方を知ることができ、先月より具体的なイメージを持つことができた。

事業ドメイン領域
実際のデータを分析して、事業に貢献する会が始まった。

  • 実際のデータに触れるのはとてもテンションが上がる。学習用のデータと何が違うんだ、と言われると明確な差があるわけではないけど、おそらく、本当の出来事のデータであるという点と、そのデータを通して自分が貢献できる可能性があるという点が異なるからだろうと思う。(逆にいうと、貢献できなかったら自分の存在価値をどこで示していけるかというプレッシャーも当然ある)
  • 残り2つの領域が、データ分析における道具に当たるのに対し、この領域は分析の前提に当たる根幹部分だと思う。道具でない分、その身につけ方は不定形で捉えづらい。実際、上記の会でも話についていけない部分があって、一方でそれを理解できるようになるにはこれを学べばよい、という類いのものとも思えず、漠然と悩んでいる部分である(悩みが漠然としている点からも、考えの整理ができていないことを示しているなと思う)。
  • 当初は浅く広く事業全般を知ろうとしていたけど、割と今は上記の会のミッションに向けた知識のつけ方に向かっていて、まずはそれをしっかりやるべきだろうと思う。

全体的なふりかえり

学びの第2段階に入った(特にデータエンジニア領域)
  • 1ヶ月めは、本に沿って一項目ずつ押さえていく学び方をしていて、これはPythonの全般的な使い方を学ぶという目的に合致していた。今月やった2冊めは、本の内容を全て学び切るというより、データ分析という目的に向け、本は参考として自分を分析できる状態にすべく統合的に学ぶ必要があったのだと思う。それが当初は理解できていなくて、メンターの方の言葉で気づかせてもらえて、学び方を変えることができた。
  • 学び方は段階によって違うんだなと思った。学ぶ目的は、別に本の内容を完全に理解して再現できることでなく、その技術を使って自分のやりたいことをできるようになることである、という根本的なことを認識することができた。
  • まだ第3段階、第4段階とあると思う(例えば、今はまだできていないけど、関数の中身がどのように書かれているかのぞいてみるとか、他の人のコードを読んで書き方を学ぶとか)ので、学ぶ方法も深化していけたらいいと思う。
3領域が融合し始めた
  • 統計学で学んだ分布をPythonで書いてみたり、実際のデータを使って分析し始める等の変化があった。1ヶ月目では完全に各分野を分けて学んでいる状態だったのが、それらの知識を統合し始める段階に入ってきた。知識はそれ単体で持っているよりも、自分の中で有機的につなげることができると面白くなってくる。
  • 今の立場は、各領域の専門家に比べると知識が浅く広く必要になる立場で、自分がどういう方向に向かっていくか意識しないと中途半端になる可能性もあると思うけど、この立ち位置ならではの面白さもありそうだなと感じた。

公務員から転職して感じたこと

新卒で入った地方公共団体で数年間働いて、今回、GMOペパボ株式会社に仲間に入れてもらった(この経緯もいつかまとめられたらと思っている)。
ここでは、公務員という立場から転職して感じた違いについて書いてみる。

一言でいうと、全体的にワンダーランドだった。
共通点の方がむしろ少なくて、いちいち新鮮で面白かった。
入る前の想像と違うということでなく、頭で理解しているのに体がついていかないというような感覚だった。単に仕事内容や進め方の違いだけでなく、何を大切にするかという根本的なところが違っていたので、そう感じたのだと思う。
たくさんの違いの中でも、象徴的だなと感じたのは以下の2点だった。

  • 楽しむということ:
    当初、言われた言葉「楽しんでやりましょう」。
    楽しむって何だと思った。今まで、楽しそうにしていると通報されることのある立場だったから(改めて考えるとすごい)、無意識に自制するようになっていた。
    仕事を楽しむってどういうことかわからなかったけれど、まずは「見当違いかなと思ってもとりあえず手を動かしてやってみる」ことをしようと考えた。
    前職で難しい対応をする時、相手の課題をより良く解決しようという視点でなく、訴えられない等の自分を守る視点になっていることが時々あって、そういう時は本当に楽しくないし、その守るという結果以外、何も新しいものが生まれなかった。1
    おそらく、楽しむというのはそれと逆で、フラットな状態で物事に接し、前向きに取り組んで、結果としてもっとおもしろいものが生まれる、という状態なのかなと現時点では考えている。

  • 組織への愛情:
    最も違いを感じた部分かもしれない。
    新卒生の研修終了の会を見ていて、ペパボへのある種純粋すぎるほどの愛を感じて、すごくまぶしかったし、その一員となれたことを嬉しく思った。
    同時に、前職の若手の仲間の顔を思い出していた。ペパボと同じくらい、前職の皆も住民のために頑張っていて、概ね人間関係も良好なのに、なぜ組織への誇りを持てない雰囲気だったのか 2 、そういう空気しか作れなかったことを後輩に申し訳なく思った。
    ペパボの雰囲気は、各人が醸成している部分もあるし、そうなるように意識的に作り維持されている部分もあるのだろうと思う。私はそれをいいなと思って入ってきたので、どうやって作られたものなのか観察して、前職との違いを生んでいる原因は何か考えたいと思っている。


  1. 色々な前提が異なるので、仕方ない部分もあると思う。きっと、その職場ごとに「楽しんで仕事をする」ということの姿は異なっている 

  2. 個人的な感想。あの場所だからこそ学べたこともいっぱいあったし、転職した今もお世話になった先輩・後輩への感謝の気持ちは強い 

1ヶ月めにやった学習のふりかえり

学習に入る前の状態

  • データ分析・プログラミングとも未経験で、全く別分野の仕事をしていた
  • 休日に趣味として少し勉強していた
  • データ分析は、本当に基本的な統計の本を何冊か読み、当時触っていたデータが時系列データだったので、それに必要な専門書を数冊斜め読みした
  • プログラミングは最初Pythonを勉強していたけど、具体的にデータ分析ができるレベルになるのはなかなか難しいと感じ、途中からRを使うようになった
  • 手元のデータを実際に触ってみて、わからなくなったら参考書に戻って、という繰り返しをしていた

1ヶ月目にしようとしたこと

大まかには、下記3領域について1

  • 事業ドメイン領域
    事業に関し、社内/外部・競合/ユーザーの概要について理解する

  • データサイエンス領域
    基礎的な統計知識を身につける : 「基礎統計学Ⅰ 統計学入門」
    データ分析の概要を掴む : 「データサイエンティスト養成読本」

  • データエンジニア領域
    Pythonの基本的な文法の使い方を習得する : 「詳細!Python3入門ノート」

結果、できたこと

事業ドメイン領域

  • 事業の概要についてお聞きした。
  • ユーザー側にいた時にはわからなかったサービスの現状や課題、目標を知ることができた。ただ、今知っているのは本当に概要のまとめで、もっと事業ドメインの知識やその肌感覚を知らないと、仮に分析手法が身についたとしてもできるのは表面的な分析になってしまうだろうと感じた。
  • 今後、特にディレクターの方が、日々何に取り組んでいるか、具体的に何を解決しようとして今何を考えているのかを知ることで、自分の中の理解を深めたい。CSの方が日々どんな問合せを受けているかという点もデータの宝庫だと思うので、お話をお聞きしたいと思った。

データサイエンス領域

「基礎統計学Ⅰ 統計学入門」

  • 原則毎日1時間、読書会をしていただいた。1冊の1/3くらいまで進んだ。自分で読んで理解した内容を言葉にしたりホワイトボードに書いたりして、逆に理解できなかった部分を教えていただくような形式で進めた。読書会後、内容をまとめていると、実は理解できていなかったことに気づいて考え直すこともしばしばあった。
  • 今までは、初学者向けの本を数冊と、趣味でやっていた分析に必要な部分の本を読んでいるだけだったので、この本によって網羅的に知識を身につけることができると思う。
  • 統計学的にでなく数学的に理解ができず時間がかかった部分があったので、微積や行列など、統計に関連する部分の復習をしたい。

「データサイエンティスト養成読本」

  • 読んで、不明な点を調べてレポートにまとめ、それに対しレビューしていただく形で進めた。3章分。データ分析の流れや各段階での注意点、データベースについて等。
  • 本で解説している用語に対し、その解説内で出てくる単語がわからないという状況だったので、調べる中でおぼろげにデータ分析の世界が見えてきたような知識レベルにいる。実際に分析をやっていくことで、こういうことを言っていたのかと理解できるようになるのではないかと思っている。
  • 分析結果の表現はその意図がなくても恣意的になってしまうので、自身に対する健全な猜疑心が大事という文が印象的だった。確かに、導いた結論を象徴している部分に注目しがちになるので、気をつけたいと思った。

データエンジニア領域

「詳細!Python3入門ノート」

  • 写経してjupyter notebookに章ごとにまとめ、レビュー(不明点についての回答やコードの書く際の注意点など)していただく形で進めた。一応1冊最後まで一通りやった。
  • 前半は以前に自分で学んでいた内容と概ね重なっていたが、後半ジェネレータやクラスの辺りは初見の部分があり時間がかかった。まだ腹落ちした感覚がない部分もあるけど、何度も触れることで頭の中にその分野の回路ができるので、定期的に復習するようにしたい。

全体的な感想

  • 学習に対して、1ヶ月はあっという間だった。もう少しスピードを上げていきたいと思いながら、この業界にいる人はおそらく身についているのが当然なのであろうGitやシェルの使い方など、基本的な部分で引っかかって時間を食うことが多かった(自分で調べて解決する力が必要なので、無駄ではないとは思う。また、ごく基本的な部分しかわかっていないので、もっと習熟する必要がある)。

  • 20代の時間を別分野の事に使ってきたので周りの方との差があるのは当然で、焦りはあるけれど、毎日今の自分にできる、自分の力を伸ばすためにできることは何か考えて、それに取り組み続けるしかないと思う。やりたいことはいっぱいある一方、1日で進むのは微量でもどかしいけれど、3ヶ月後、1年後の自分に期待するわくわくする気持ちがあって、自分は学び続けることができることを知っているので、がんばりたい。


  1. データサイエンティスト協会が示している「データサイエンティストに求められるスキルセット」の3つの領域より http://www.datascientist.or.jp/news/2014/pdf/1210.pdf