分析 SQL のテストとデータチェックスタイルの提案
Naofumi Yamada
Data Engineerこの記事は bq_sushi で発表した BigQuery データ前処理の暗黒面 の個人的な解説です。
#
はじめに従来のオンプレミスのリレーショナルデータベース(Oracle や SQL Server、MySQL など)では、なるべく処理を少なくするため、データ中心の比較的短い SQL を記述していました。しかし、BigQuery ではコスパ良くデータ処理を実行するには、手続き中心の比較的長い SQL を記述する必要があります。
コーディング規約(Bigquery時代における、分析SQLコーディングスタイルの提唱 や 分析SQLのコーディングスタイル)の議論はありますが、SQL の継続的な開発論は確立していなさそうです。
本稿では、SQL をテスト駆動開発に乗せ、継続的な開発運用を行う ことを目的とし、分析 SQL のテスト手法とデータ品質の同定法について述べます。
#
分析 SQL のテスト手法分析 SQL をテスト可能にするため、以下の工程を踏みます。
- 意味のある単位でサブクエリを FUNCTION か WITH 句に分離する。
- 可変テーブルへの参照を全てローカル参照に置き換え、SQL ファイルとして保存する。
- テスト用の入出力ファイルを作る。
- SQL ファイルに、テスト用の入出力ファイルを FUNCTION か WITH 句として結合する。
- テスト用の入力時に、テスト用出力が返ってくるかテーブルを比較する。
#
意味のある単位でサブクエリを FUNCTION か WITH 句に分離する。以下のクエリを例に始めてみましょう。まずは意味単位にファイル分割です。
#
birth_date_percentile_5#
user_master#
pos#
rfm_feature#
concatinated_features#
可変テーブルへの参照を全てローカル参照に置き換え、SQL ファイルとして保存する。分割が終わったら、FUNCTION 以外のいくつか持っている参照を全てローカル参照に置き換えます。これをやっておくことでテストの時には、WITH 句でテストデータを投入、実利用時は WITH 句で実テーブル投入ができます。 テーブル参照を持つ FUNCTION に関しては取り除けない(ARRAY_AGG して引数に渡せば小さいテーブルは渡せるが大きいテーブルに対応できない)ので、そのまま参照透過性を保証する必要がある。
#
birth_date_percentile_5諦めて放置か、これくらいならクエリが正しいことをレビューにて通す。 今回は、ARRAY_AGG した結果を受け取ることにして対応する。
#
user_masterFROM 句を書き換え、CURRENT_DATE() は再現性がないので、関数として切り出して定数を入れた。
#
MY_CURRENT_DATE#
user_master#
posFROM 句を書き換え。
#
rfm_featureそのまま。
#
concatinated_featuresそのままです。
#
テスト用の入出力ファイルを作る。網羅しようとすると結構工数かかりますが、境界値の JOIN など甘くなりやすいところに気をつけてテスト用データを作ります。利用回数の多いものは確実に、テストしたくないものは念入りに、どうしてもコストがかかるところはレビューで誤魔化します。
#
dirty_user_masterクレンジング前のユーザマスタです。
#
user_masterクレンジング後に期待するユーザマスタです。
#
dirty_posクレンジング前の購買情報です。
#
dirty_posクレンジング後に期待する購買情報です。
#
rfm_feature#
SQL ファイルに、テスト用の入出力ファイルを FUNCTION か WITH 句として結合する。SQL の依存関係を解消できるようにクエリを結合します。いろいろ試行錯誤しながら、これができるフレームワークを作っているので、ある程度の完成度になったらお披露目します。 最低限、任意のプログラミング言語から、sql ファイルを読み込んで中身をフォーマット文字列に入れられれば良いです。
#
テスト用の入力時に、テスト用出力が返ってくるかテーブルを比較する。上のクエリをくっつければテストクエリの完成です。 同じ列(重複、配列、構造体非対応)なら TRUE を返し、そうでないなら ERROR を吐くようになっています。
以上で、簡単なテストはできるようになりました。 テストがないと運用時に動作保証ができなくなってしまうので、BigQuery で処理を多段に行う際には、テストを書くようにしています。
#
データ品質の同定法これまでで、データ変換のテストができるようになりました。 しかし、機械学習のプロジェクトを継続的に回すためには、データ変換のテストだけでは不十分です。入力データが劣化していないか調べる必要があります。これに関しては、リレーショナルデータベースの制約確認と、要約統計量の比較によってコントロールできるのではと仮説を立てて仕組みづくりを行っています。
#
リレーショナルデータベースの制約リレーショナルデータベースに存在し、BigQuery に存在しないのが、制約です。
- CHECK
- NOT NULL
- UNIQUE
- FOREIGN KEY については、とても重要な制約ですので、データ分析を行う際には気をつけているのではないでしょうか。
#
CHECKcheck expression が成り立っていない列がないか確認します。 社内では標準スキーマを定義しておいて、スキーマベースで値域やフォーマットチェックを行っています。
#
NOT NULLcolumn が NULL でないことを確認します。BigQuery の REQUIRED で済めばいいのですが、お客様からいただくデータのフォーマットが揺れた時に対処できないため、NULLABLE で取り込んでこのチェックを実施するようにしています。
#
UNIQUEcolumn1, column2 がユニークであることを確認します。ユニーク保証ができないので、時折確認しておかないと、ユニークキーだと思い込んでいるものの JOIN でテーブルが大きくなって取り返しのつかないことになります。
#
FOREIGN KEYmy_table1.key1, key2 が my_table2.key1, key2 を全て含有していることを確認します。外部キー制約がついておらず、手動運用の末に不整合が起きているデータも見かけます。そのようなデータに対し外部キー制約が必要な時は以下のクエリで確認し、必須でないなら不整合割合を見て問題の大きさによって無視することもあります。
#
要約統計量の比較制約だけでは、データの質が変わったことを検知できません。これを検知するために、社内では要約統計量を算出しています。超巨大なデータも要約統計量を一度計算しておけば、比較のコストが小さい点も魅力です。手動でチェックする場合には、カテゴリ変数に差異がないか、ヒストグラムに大きなずれがないかを目視するようにしています。
#
Tensorflow Data ValidationTensorflow Data Validation は visualize_statistics でデータの可視化、validate_statistics にてデータの同定を行ってくれます。
#
pandas-profilingpandas-profiling はもう少し小さなデータセット向きで、データの可視化を行ってくれます。小さくて初めましてのデータについてはこれがお手軽です。
#
bq_profilebq_profile は同僚の rhoboro 氏に作っていただいた、BigQuery で要約統計量を見たいときに使えるツールです。 可視化よりも、継続していくプロジェクトの中でデータ品質の劣化がないか確かめるのに重きをおいて作って(いると認識して)おり、大きなデータセットに対しても高速に動作するため多用しています。
#
おわりに要約統計量の比較はツール紹介になってしまいましたが、社内では BigQuery をいかに運用品質で使っていくかを検討しています。そのために、データ変換が間違っていないこと、入力データ品質に劣化がないことを確認する仕組みとして上記のような仕組みを試しています。もし良いやり方をご存知であれば、ぜひ教えてください。