単体テストを学ぼう!① -自分のプログラムに自信を持ちたい-

f:id:monozukuri-bu:20191126184429j:plain

こんにちは、ふじもんです。
朝、お布団からでるのが辛い季節になってきましたね。
いつのまにか出勤時に乗る電車が前よりも20分遅くなっていました。

遅刻ギリギリ。

さてさてお話は変わりまして。

私は業務でプログラムを書くときは、分析データの整形など、分析の前処理のスクリプトが主です。
そしてプログラムを組んでいるときに常々不安を抱えています。
それは

私のプログラム、本当に合ってる???

って。
自分で組んだプログラムが想定通りの挙動をしているかいつも不安です。

毎回処理途中のデータや最終的なアウトプットをちまちまと目視で確認はしているけど、
何せプログラムで処理したいデータなんて少ないはずがない。
それはもう膨大

さて、どうしよっかな。

この不安感、なんとかして解消できないものか。

そう思い、上司に相談してみました。

上司「最近現場どう?」
私 「現場は順調なんですけど、ちょっと悩みが…」
上司「え。どしたの」
私 「自分でスクリプト書くじゃないですか。それを実行してみて、一見思い通りに動いてるように見えるんですけど、コレ本当に全てのデータに対して正しく動いてるのかなって…。」
上司「それならテストコード書けばいいんじゃない?」
私 「テストコード」
上司「…ふじもんって開発業務とかにおけるテストってどういうことするかわかる?」
私 「ばなな。」
上司「わからないなら素直に言おうか。じゃあまずテストとは何なのかを調べてみようか。そうしたら、自分のプログラムがちゃんと想定通りに動いてるか調べる方法とかもわかるかもね。」

テストってなんだ?

上司に言われるがままに、開発経験ゼロの私は早速テストについて調べてみました。
そもそも、開発業務に置けるテストには、大きく分けて以下の3種類があるようです。

単体テスト
 プログラム1つが期待した通りに動作するかテストを行う。

結合テスト
 複数のプログラムを組み合わせ、それらが一体として期待したとおり動作するかテストを行う。

・総合テスト
 システムの全機能を最初から最後まで一通り運転させ、期待したとおり動作するのかテストを行う。

ふむなるほど。
プロジェクトによってどのテストを行うかはまちまちだそうですが、上記3つは大体行われるようです。

また、テストの範囲はプログラムに限らずモジュールや関数などにも適用され、厳密には決まっていません。
システム開発ではこれらを行うことにより、仕様通りに動くことを確かめてからリリースします。

つまり・・・?

私の悩みは自分の書いたプログラム1つが正しい挙動をしているか確認したいので、
上記の3種類の中で必要なテストは「単体テスト」に当たります。

単体テストってどうやるの!!! はやく!はやくこの不安感から解放されたいんだ!!

単体テストってどうやるの?

逸る気持ちで単体テストについて調べていると、単体試験の中にも2種類あって、観点が違うもよう。

確かに、プログラムを確認する観点を決めていなかったな…
漠然と入出力を見て間違いがないかを確認していたな…
それで、確認しても不安が残っていたのか。。。

これは重大な気付きです!!!

ブラックボックステスト

テスト対象の入出力に着目し、入力に対して期待した出力になるかを検証する。

その観点は、

同値分割

起こりうる全ての事象をいくつかのグループに分け、各グループから代表値を選んでテストを行う。

境界地分析

同値分割によって分けられた各グループの境界値付近をテストを行う。

ホワイトボックステスト

テスト対象の内部構造に着目し、条件分岐や繰り返しなどの各部分を確実に検証する。

その観点は、

命令網羅(C0)

テスト対象となるプログラムの命令文それぞれが、1回以上実行されるようにテストを行う。

分岐網羅(C1)

テスト対象となるプログラムに含まれる判定条件について、真となるケース、偽となるケースそれぞれが、1回以上実行されるようにテストを行う。

条件網羅(C2)

テスト対象となるプログラムの条件文について、真となるケース、偽となるケースそれぞれが、1回以上は実行されるようにテストを行う。

なるほど!!!

ブラックボックステストは、ソースコードはどうあれ入力に対して出力が正しいかを確認して、
ホワイトボックステストは、ソースコードの中身の挙動が正しいかを確認するってことですね。

お、確かにこの観点でプログラムの挙動をチェックしていけば、「なんとなく大丈夫そう」よりは圧倒的に不安が取り除かれる気がする…!
これは不安から解放される日も近いのでは!

テスト基準のモノづくり -テスト駆動開発-

よーし、じゃあさっそくテスト…って思ったけど、テストするにもテスト対象のモノがない。
なら作れば良いじゃない。
じゃあテストをするための簡単なシステムを作ろう。
せっかくテストのことを理解するなら、テストしやすいものを作りたい。
何をどんな風に作ろうかなと調べていると、テスト駆動開発という言葉を発見しました。

テスト駆動開発(TDD:Test-Driven Development)

テストファーストという考えに基づいた開発手法。
テストファーストとは、まず本番で使用するプロダクションコードを書く前に何をテストするかを決め、テストコードを先に書くのが特徴。

そのテストファーストという考え方を組み込んだテスト駆動開発は、最低限必要なテストを積み重ねていき、少しずつ確実に実装できるそう。
そして、この手法の大きなメリットとして確実に動くコード読みやすく綺麗に書けること。

よく自分の書いたコードがあとで読めなくなったり、学生時代に「コードの汚さは頭の中の汚さ」とゼミの先生に言われていた私にとって、もうこんな最適な開発手法あったの?もっと早く知りたかった。
確実に動くコードを書けると、心理的負担も減りますしね!
他にもデバッグの時間が短くなったり、実装コードの修正があった場合に修正と無関係な部分を破壊していないかをチェックするリグレッションテストにも役立つメリットもあるようです。

まとめ

今回の記事では以下のことについて調べて記載しました。
開発におけるテストには以下の種類がある。
単体テスト
結合テスト
・総合テスト
それぞれを行うことにより、一つのシステムのバグの検証や確実性を高めてからリリースできる。

単体テストは以下の種類がある。
ブラックボックステスト
ホワイトボックステスト
それぞれを行うことにより、プログラムの入出力からソースコードの挙動までを確認することができる。

テストファーストの開発「テスト駆動開発」というものがあり、プロダクションコードよりテストコードを先に書くことにより、
確実に動くコードを綺麗に書けるメリットがある。

次回の記事ではテスト駆動開発でプログラムを実装しながら、テストについてより理解を深めていきます!

SKYWILLハッカソン2019冬の陣

今回のお題は【フレッシュ】

初めて参加される方もいらっしゃいましたが、雑談しながら楽しく開催することができました。

f:id:monozukuri-bu:20191126102250j:plain f:id:monozukuri-bu:20191126102318j:plain

チーム 1:写真共有アプリのフレーム作成

API の知識を深める
・node + express + mongodb の知識を深める

API 設計
URL メソッド 内容
/ GET 全ユーザ取得
/:id GET ユーザー取得
/ POST ユーザー登録
/:id PUT ユーザー更新
/:id DELETE ユーザー削除
$curl -X POST http://localhost:3000/ -H "Content-type: application/json"  -d '{"userid":"1","username":"sugi","password":"password"}'
{"user":{"_id":"5dd535f8c45a380d95a7555b","userid":1,"username":"sugi","password":"password"}}

$curl -X GET http://localhost:3000/ -H "Content-type: application/json"
[{"_id":"5dcfa5eadebf7f5a6f912840","userid":2,"username":"ryu","password":"password","__v":0},{"_id":"5dd535f8c45a380d95a7555b","userid":1,"username":"sugi","password":"password","__v":0}]

$curl -X GET http://localhost:3000/1 -H "Content-type: application/json"
{"_id":"5dd535f8c45a380d95a7555b","userid":1,"username":"sugi","password":"password","__v":0}

$curl -X PUT http://localhost:3000/1 -H "Content-type: application/json"  -d '{"username":"ryu"}'
{"_id":"5dd535f8c45a380d95a7555b","userid":1,"username":"ryu","password":"password","__v":0}

$curl -X DELETE http://localhost:3000/1 -H "Content-type: application/json"
使用技術

・JS
・Node
・Express
・MongoDB
・Docker

感想

・新しい言語を触って知識を深められた。
ペアプロスキルが向上した。
API の知識が深まった。

チーム 2:フレッシュな Web サービス

・あなたのフレッシュ度を判定する心理テスト
 5 つの質問に答えると、フレッシュ度を判定してくれるスグレモノ。

f:id:monozukuri-bu:20191126105907p:plain

・画像のフレッシュ度を判定する深層学習
 画像のフレッシュ度を計算してくれる。

f:id:monozukuri-bu:20191126174535p:plain

 ※画像はぱくたそ様よりお借りしました。(モデルリリース取得画像)
 photo by すしぱく / model by yumiko

・フレッシュな気持ちになれる Google Chrome 拡張機能
 背景など特定の要素だけフレッシュな色に変える魔法。 f:id:monozukuri-bu:20191126110119p:plain

使用技術

Python, Bottle, Jinja2
HTML5, JavaScript, CSS
・TensorFlow, Keras

感想

・深層学習は1時間でできた(精度無視)けど、まだフロントで躓くことが多い。
・自分の技術力を見直せた。
・ゼロからモノづくりしたことがなかったので、いい経験が出来た。

チーム 3:音楽データのパート分割・加工・合成

・最新の音楽素材分離エンジン「spleeter」を用いて、最新技術の使用を体験する。
・音声データを「ボーカル、ベース、ドラム、ピアノ、その他」の5分割をした。
・「pyaudio」ライブラリを用いて音の高さ・速度を変化させた。

f:id:monozukuri-bu:20191126180159p:plain f:id:monozukuri-bu:20191126180218p:plain

使用技術

Python
・spleeter, pyaudio

感想

・最新の技術なので記事が3つ程度しかなかったが、プログラムの構造を見て自分で考える良い機会となった。
・最新の技術を用いて音声データの分割・合成を楽しめるいい機会となった。
・ピッチの変更、instrumental や 8bit 曲の作成など個人使用の範囲で楽しめる可能性を感じた。

チーム 4:いつも通る道のりに、寄り道を加えて案内してくれるアプリ

いつもと違う道のりを歩いてみることで新しい発見を促す。

f:id:monozukuri-bu:20191126103346p:plain

使用技術

HTML5(GeoLocationAPI)
AWS(S3)
Google Maps API
・geolib
・ServiceWorker

感想

・PWA 化をするため、ServiceWorker, Manifest ファイル配置などを行ったが、うまく動かなかった。(ホーム画面に追加ボタンでホームに追加されない…)
HTML5 の位置情報を取得する API(GeoLocationAPI)が、GoogleChrome では HTTPS 接続でないと動作しなかった。

全体を通して

各チーム、発表時に動くもの、今後のベースなど作成できていて安心しました。

短い時間で何かを作ることはとても大変なことです。
チームのスキルセットを把握、役割分担し、作るものを決める。
やりたいこと、好きなことができるわけではないですが、チームとして手や頭を動かす必要があります。

苦労することもありますが、非日常的、刺激、チーム開発体験などハッカソンに参加する良さはたくさんあります。

今回は社内イベントとして開催しましたが、今後もconnpassなどで募集して社外向けイベントとして開催することもあると思います。
これを機にハッカソン参加してみようかな~と思ってくださったら幸いです。

参加してくださったみなさま、本当にありがとうございましたー!

データエンジニアとしての第一歩を終えて

f:id:monozukuri-bu:20191031163847j:plain

皆様お疲れ様です。
tsudaです。

新卒の研修を5月中旬に終え, そのまま現場に配属され早5か月が立ちました。
配属された現場も丁度今月で離任ということで, 研修と現場での業務について所感を述べます。

自己紹介

大学ではグラフ理論, 数理計画法, 統計学, 数値解析等を学んでいました。
プログラミング経験はCとPython, どちらも基礎文法レベルですが習得しました。

現場ではデータ分析ソフトウエアの環境構築, 運用に携わっていました。

研修で学んだこと

主にPythonを用いて"データ分析"の一連の流れをハンズオンで学びました。
手法の中身について詳しく掘り下げるというより, とにかく"データ分析"を流れでやってみるという部分に重点を置いており, 体系的に学ぶことでデータ分析業務に必要な能力が研修を学ぶ前よりも明確にすることができました。

その他にはLinuxコマンドの基礎を学び, 現場では構築作業の際に非常に役に立ちました。

携わった業務

PostgreSQLのユーザ権限設定

PostgreSQLを用いてユーザー, データベースの作成, 削除も行っていましたが,
「どのユーザーがどのデータベースのどのスキーマのテーブルを参照できればいいのか」
というアクセス権限の設計を主に行っておりました。
トライ&エラーで試すことができる環境もあり, パズルのようで楽しかったです。

運用ツールの作成

主に下記のツールの作成&単体試験を行いました。

bash:データベースのバックアップ, バキューム
VBAcsvファイルからグラフを自動生成
PowerShell:S3からテーブルデータ取得, インポート

現場で学べたこと

5か月と短い期間であったにもかかわらず, 上に述べたように様々な体験をすることができました。
これらの体験を通して重要さを痛感し勉強になったものが以下の2つでした。

わかりやすいコードを書く

現場に入るまでに書いたスクリプトは基本的にその日の内に完結するので, 最低限必要な動作を保証したうえで変数や関数の名前, コメントによる動作の説明等は便宜的に書いていました。
しかし現場ではそれが通用しませんでした。

後日仕様変更が起きていざ修正しようと思った時に, 謎の変数"s"や, なにかのユーザを取ってきているであろう"get_user"関数がなんの説明もなしに置かれているのです。
結局そのコードを理解するのに, 作成するのと同じくらいの時間をかけてしまいました。
それ以来, 引き継ぐ相手の為, 何より自分の為に後から見ても理解しやすいコードを書くことを意識するようになりました。

再現性のある手順書を書く

基本的に手順書というものは, 誰かに引き継ぐ, 共有するために存在しています。
「その手順書を読めば, 誰もが同じ結果を再現できる」こと,
つまりは再現性が必要ということが, 手順書の作成を通してわかりました。

次への意気込み

「知識とは球体である」と誰かは言いました。

知識を蓄えると球は膨らみ球の体積, つまり「知っていること」は増えるが,  
球の外側と触れる面である表面積, つまり「知らないこと」も増えていく。

研修, 現場を通して様々な「知らないこと」を知ることができました。
次の現場でも今の知識と技術を生かし, 増やし, 成長していきたいと思います。

データ分析エンジニアとしての第一歩を歩んだ所感

f:id:monozukuri-bu:20191018151721j:plain

こんにちは、ちょねです。
データ分析系の研修を経て、実際に業務としてデータ分析を行ってきましたので、所感をまとめたいと思います。

自己紹介

大学では教育学と情報系を専攻していました。
プログラミング経験としては、統計や機械学習の勉強をしてきた過程でPythonを扱ってきました。

現在は機械学習技術を用いたデータ分析の業務を行っています。
好きな言葉は「苦手じゃない。未熟なだけ。」です。

研修で学んだこと

入社後の研修では、GUIツールとPythonを用いたデータ分析や 機械学習の基礎を学びました。
その他、Linuxコマンド入門、Flaskを使った簡単なWeb開発を行いました。

この研修のよかったところは、今まであまり扱ったことのない 画像やテキストなどのデータの分析を学習できたことです。
研修の後半では、出された課題がわからない場面が多々ありましたが、ネットで調べれば大体同じような場面で躓いている人が質問していて、解決の糸口になるという感覚もつかむことができました。

求められたスキル

初めての業務は、機械学習モデルへ入力するデータの前処理がメインだったため、データを扱える技術が必要でした。

私はPythonをやってきたことからPandasでデータを扱っており、以前より使いこなせるようになっていく実感がありました。
特にgroupbyやapplyをよく使用していました。

また、機械学習の前処理の理解も必要でした。
データの処理方法によって予測精度が変わってくるため、どのような前処理を行い、どのような特徴量を用いれば精度が向上するのかを考えながら業務を行う必要がありました。

不足していたスキル

1つめは、データ処理の技術です。
実際の業務では、整ったデータから分析をスタートできるわけではないと痛感しました。
いくつかのデータを結合して思い通りの形に整形できるように、実際に手を動かして慣れておいたほうが良かったと思いました。

2つ目は、機械学習の評価指標を詳しくなっておくべきでした。
精度といえばAccuracyだという感覚だったので、Precision、Recall、F値、AUC、ROC曲線などの評価指標の知識があれば、 より理解がスムーズだったかと思います。

成長したポイント

業務を通じて私が学んだことは、自分本位に分析を進めてはならないことです。
こうすれば精度が上がるのではないかと推測して進んでいくのはお客様の要望と乖離していく可能性があるため、慎重になるべきです。

加えて、なぜその方法をとったのか説明できなければ、価値は生まれにくいです。
そのため、お客様の要望を満たしつつ、データ分析の専門家ではない人に対しても、分析の結果や過程について、十分な説明ができることが重要であると学びました。

意気込み

入社後、初のデータ分析エンジニアとしての業務を行ったことで、一歩前進することができたと感じています。
業務内では、自分で勉強するだけでは到底得られないような知識を身に付けることができ、良い刺激になりました。

データ分析に関してはまだまだ未熟なので、これからも精進していきます。

Pythonistに贈る統計学入門 ⑦検定

f:id:monozukuri-bu:20191016182446j:plain

商店街の魚屋でたむろしている猫に近づいたら、全力で逃げられました。
kanaiです。

今回は統計学における検定についてお話していきたいと思います。

検定とは

統計学における検定とは、確率をもとに仮説の成否を判定する手法のことです。

まずは例を考えてみましょう。

あるところに占い師がいた。
声をかけられたので占ってもらったら「あなたは5回連続失恋するでしょう」と予想された。
そしてその予想は見事的中した。

さて、悲しい例でしたが、この占い師は果たして本当に信用できるのでしょうか。
「5回連続で当てたのだから、信用できる」という意見もあれば、「5回連続で当てたくらいではまだ信用できない」という意見もあるかと思います。
このような場合に統計学を用いて仮説を判断する手法を検定と言います。

有意水準と棄却

では、どのように検定を行うのでしょうか。
上記の例で考えてみましょう。 (なお、失恋する確率は 0.5 とします。)

まず仮説H0を立てます。

H0:この占い師は予知能力がない。

この仮説が間違っていれば、以下の結論が導けますね。

H1:この占い師は予知能力がある。

そもそもH0が起こる確率は


0.5^5 = 0.03125

なので3.125%ですね。

ここで私が「3.125%というのは低い確率だからH0は間違っている(つまりこの占い師は予知能力がある)。」と判断したとします。
しかしこれはあくまで私の主観であり、3.125%は低い確率ではないという意見もあるかと思います。
統計学ではこの判断基準を有意水準と言い、あらかじめ計算をする前に決めておきます。

一般的に、5%や1%が使われます。

有意水準を5%としていたら、H0は間違っていると判断でき、結果的にH1の結論が導けます。
しかし、有意水準を1%としていたら、H0は間違っているとは言えません。

統計学では、上記で立てたような仮説H0帰無仮説と言い、H1対立仮説と言います。
また、仮説H0が間違っていると判断することを棄却と言います。
さらに、帰無仮説が正しかった場合に占いの結果が得られる確率3.125%をp値と言います。

ちなみに、p値が5%や1%を下回ったとしてもその確率は0ではないので、H0を棄却したが、実はH0が正しかったということも可能性としてはありますよね?
これを第一種の過誤と言います。
そして、実はH1が正しかったが、H0を棄却しないことを第二種の過誤と言います。

練習問題

それではせっかくなので、2018年11月の統計検定2級に出題された問題にチャレンジしてみましょう。
今回は、問16を解いてみます。

[1]

まずこの問題を解くためにはχ二乗検定について知っている必要があります。
χ二乗検定は独立性の検証をするために使われます。

今回のように、発生率は曜日に依存するか否かといった場合に使います。
niを観測データ、Eを期待値とすると、 χ二乗統計量は下記の計算式で求めることができます。


χ^2 = \sum_{k=1}^{n}\frac{(n_{i}-E)^2}{E}

ちなみに数式から読み取れる部分もあるかと思いますが、χ二乗統計量は観測データと期待分布との相違(ずれ)の測度です。

では、まず期待値度を求めるとこのようになりますね。


E = \frac{102}{6}=17

なので、χ二乗統計量は以下のようになります。


χ^2 = \frac{(14-17)^2+(19-17)^2+(15-17)^2+(22-17)^2+2\times(16-17)^2}{17}
(=2.588...)

よって答えは 1 です。

[2]

適合度検定では、χ二乗統計量が「自由度」=「カテゴリーの数」-1 に従うことを使います。

今回カテゴリーの数は 6 なので、自由度 5( = 6 - 1) のχ二乗分布を使って検定を行います。

統計検定の問題の付録3にカイ二乗分布のパーセント点が載っていますので、υ(自由度)5とα(有意水準)0.05が直行するところを見てください。

11.07と書いてありますよね。

χ二乗検定では、カイ二乗分布のパーセント点とχ二乗統計量を比較して帰無仮説を棄却するかしないかを決定します。

今回はカイ二乗分布のパーセント点がχ二乗統計量=2.588...よりも小さいので、帰無仮説は棄却できないという結論が導けます。

よって答えは 3 です。

Pythonを使うと

実際に統計検定を受けるときにはPCを持ち込めないので、このような感じで解くことになるでしょう。

それでは今度は、
もしPCが持ち込めたら...、そしてPythonが使えたら...
という妄想の下解いてみましょう。

下環境でJupyrer notebookを使うとします。

Python 3.7
Jupyter 4.4.0
matplotlib 3.0.2
Pandas 0.23.4
seaborn 0.9.0
scipy  1.2.1

scipyのchisquareを使用することでχ二乗統計量とp値を計算することができます。

import scipy.stats

obs = [14, 19, 15, 22, 16, 16] #観測データを配列に格納
statistic, pvalue = scipy.stats.chisquare(obs) #χ二乗統計量とp値を計算
print(statistic, pvalue)
if statistic >= 11.07:
    print("帰無仮説は棄却できます")
else:
    print("帰無仮説は棄却できません")

これを実行するとχ二乗統計量がp値を下回り、以下の結果が出力されます。

帰無仮説は棄却できません

このように、pythonを使用すれば、途中計算を省くことができ簡単に計算ができますね!

まとめ

今回は統計学における検定についてご紹介させていただきました。
統計学的にこの仮説は正しいです。」なんて言えたらかっこいいですよね!!
機会があれば活用してみてください。

Oracle Database ロックについて

f:id:monozukuri-bu:20191016173821j:plain

みちです。
Oracle Databaseのロックについて勉強していきたいと思います。

ロック

 複数のユーザーが同時に書き込み処理を行うと、矛盾が起きてしまう場合があります。
その矛盾が起こらないようにOracleサーバーでは、書き込み処理に対してロックがかかります。
変更の対象の行ごとに排他ロックをかけてからデータの変更処理が行われ、トランザクションが終了したときに解除されます。 ロックがかかっている行に対して変更処理を行おうとする場合、解除されるまで待機し、そのあとに変更処理が行われます。

実際に試してみましょう。
id、name、num、という列を持った表を作成します。

CREATE TABLE ex1
(
    id NUMBER(2),
    name VARCHAR2(100),        
    num NUMBER(10)
);

INSERT INTO ex1 VALUES (1, 'りんご', 100); 
INSERT INTO ex1 VALUES (2, 'バナナ', 50); 
select * from ex1;
ID NAME NUM
1 りんご 100
2 バナナ 50

ここまでのsqlを打つと結果として上の表のような結果が出ます。

USER Aでデータ確認をします。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 100

USER Bでデータを確認します。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 100

USER AがここでID=1の行に排他ロックをかけてデータを更新します。

UPDATE ex1
SET NUM = 25
WHERE ID = 1;

USER BはID=1の行に排他ロックをかけようとしますが、USER AがID=1の行に排他ロックをかけているため、待機状態になります。

UPDATE ex1
SET NUM = 200
WHERE ID = 1;

USER Aでデータを確認します。
NUM列が変更されていますね。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 25

USER Aでロックを解除します。

COMMIT;

USER Bでデータを確認します。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 200

USER Aの排他ロックが解除されたため更新処理がされました。

この状態で、USER Aでデータを確認します。
USER Bの変更は確定していないため、USER A側ではUSER Bの変更内容が入っていません。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 25

USER Bで変更内容を確定し、ロックを解除します。

COMMIT;

USER Aでデータを確認してみると、USER Bが行った変更が反映されていることが確認できます。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 200

まとめ

 今回はロックついて勉強しました。
身近に使われているシステムの仕組みを知れてよかったです。

Python開発の初現場を終えて、学んだこと

f:id:monozukuri-bu:20191016153457j:plain

4月に新卒として入社し、先輩エンジニアと一緒にPythonでの開発を経験しました。
そこで学んだことを整理したいと思います。

開発環境

・言語
 Python 3.7

・ORM
 SQLAlchemy

・データベース
 PosgreSQL

コーディングにはVSCodeを使用しました。

プロジェクトで携わった部分

プロジェクトでは、比較的納期が長く、簡単な以下の機能開発を担当させていただきました。

・DBから特定のデータを取得、整形し、所定のフォーマットでエクスポートする機能
・エクスポートされたデータを、別環境のDBにインポートする機能

機能自体はシンプルですが、最大で同時に約5000万件ほどのデータを扱うため、Pandasなどで処理できない量ではありませんが、少なくもないという絶妙なデータ量でした。

苦労した点

設計には関わっていなかったため、設計書の内容をそのまま実装しただけでしたが、処理自体はそこまで複雑なものではありませんでした。
ただ、データ量が想像していたよりも多く、メモリ不足や速度の面で試行錯誤が必要になったことが苦労しました。

データのエクスポート機能

まずは自力で実装

設計書には実現したい機能と処理の流れが記載されていましたので、Pandasとfor文で実装しました。
この段階では、自分一人で要求されていた機能を実装することができました。
ただ、いざ動かしてみると、1日分のデータをエクスポートするのに約30分掛かっており、実際の利用シーンで想定されるのは1ヶ月〜3ヶ月分とのことだったので、この処理速度では話になりませんでした。

原因の特定と解決策

というわけで先輩エンジニアに相談し、コードの改良に入りました。
まずはじめに、pstatsというライブラリを使用してコードのボトルネックを特定する作業に入りました。
その結果、やはりfor文の使い方が適切でなく、無駄にループしていたりネストが深いのではないかということがわかりました。

この課題の解決策として、Pandasの関数を使用して可能な限り列や行に対してまとめて処理するようにと先輩エンジニアからアドバイスを頂きました。
この修正を行うことで処理時間を大きく減らすことに成功し、1か月分で約15分という許容範囲に収めることができました。

また、ロジックの共通化やコメントの追加なども、ご指摘を頂いていました。
修正作業と合わせて指摘を反映していくと、自分が書いたコード量の約半分の記述で、より高速に動作し、理解しやすいコードに変身しました。

データのインポート機能

まずは自力で実装

こちらはエクスポート機能と同じ要領でまとめて処理するように実装し、データを整形する部分ではそれほど時間は掛かりませんでした。
ただ、データをテーブルにINSERTする部分が2時間近く掛かる状態になってしまいました。
このINSERT処理は、SQLAlchemyというORMを介して行っていました。

課題の原因と解決策

先輩エンジニアに状況を伝えたところ、INSERTを実現する処理には様々な方法があるので、色々試してみるとよいとのことでした。

最初に考えたのは、Pandasのdf.to_sqlを使用してDataFrameを直接テーブルにINSERTする方法です。
これでも処理時間は大幅に短くなりましたが、それでもまだ1時間近く掛かっていました。
他にも色々と試行錯誤を重ねた結果、最終的にはORMは使用せず、Psycopg2を使用してPosgreSQLのcopy_fromでINSERT処理を行うことにしました。
その結果、2時間掛かっていた処理を30分程度まで短縮することができ、許容範囲内に収めることができました。

プロジェクトの経験を通して勉強になったこと

・ロジックの共通化
・コードが短くなるアルゴリズムの組み方
・第3者が読んで理解しやすいコードの書き方
 (PEP8などのコード規約や変数名の定義、コメントの記述)
・テストコードや仕様書の書き方

今まではプログラムが動けば問題ないと思っていました。
しかし、ロジックをできる限り関数で共通化し、コードをわかりやすくしておくと仕様変更があった際にも比較的容易に対処でき、かつ保守性も高まります。
分かりやすいコードを書くということが実際の業務において必要不可欠だということが、今回の案件で身をもって理解できました。

またアルゴリズムの組み方だけでなくテストや開発書類の書き方についてなども、スキルの高いエンジニアの方々にフォローしてもらったことが非常に勉強になりました。