こんにちは。データ戦略室の小宮です。
突然ですがテストコード、書いてますか?
ソフトウェア開発ではもはや当たり前なことかと思いますが、 データ分析においてのテストはソフトウェア開発と少し異なる部分があります。
それは、実際に作成したデータに対してそのデータが正しいかどうか?(仕様通りかどうか)のテストを行うということです。
データ戦略室では社内プロダクトからデータを収集・分析し、分析したデータをプロダクトに活かすことを目的としています。 そのため、データの正しさというのは非常に重要になってきます。 もちろんソフトウェア開発でも重要ですが。
そこで今回はデータ戦略室で利用しているデータ変換(Transformation)ツール、dbtを使ったデータのテストについて触れてみたいと思います。
※データ分析基盤はELT (Extraction, Load, Transformation) で設計、構築しており、 T (Transformation)の部分をdbtというツールを使用しているというイメージです。
dbt?
簡単にdbtの概要を説明すると、dbtはデータ変換 (Transformation) に特化したツールで、SQLをデータウェアハウスに対して実行し、ビューやテーブルを作成できます。 自動でテーブル定義などのドキュメンテーションも行ってくれます。
また、前述の通りデータに対してのテストを簡単に記述することができます。 以降では実際にデータに対してテストを実行してみます。
やってみる
一般的なテスト
dbtでは標準でテストが4つ用意されていて、 値がユニークかどうか、nullが含まれていないか、など簡単なテストは以下のようにymlを定義するだけでテストできます。 よくある注文テーブル(orders)を例にしてみます。
version: 2 models: - name: orders columns: - name: order_id tests: - unique - not_null - name: status tests: - accepted_values: values: ['placed', 'shipped', 'completed', 'returned'] - name: customer_id tests: - relationships: to: ref('customers') field: id
それぞれテストは以下のような内容です。
unique
: order_id列は一意である必要がありますnot_null
: order_id列はnull値を許容しませんaccepted_values
: status列は'placed','shipped','completed','returned'のいずれかの値である必要がありますrelationships
: customer_id列はcustomerテーブル内のidとして存在する必要があります (参照整合性)
適当な値を入れて実際にテストしてみます。
テストはdbt test
コマンドで実行できます。
$ dbt test --select orders 05:14:56 Running with dbt=1.0.8 05:14:57 [WARNING]: Test 'test.relationships_orders_customer_id__id__ref_customers_.4282b0c525' (models/dbt_test/schema.yml) depends on a node named 'customers' which was not found 05:14:57 Found 1 models, 1 tests, 0 snapshots, 0 analyses, 0 macros, 0 operations, 0 seed files, 0 sources, 0 exposures, 0 metrics 05:14:57 05:14:58 05:14:58 Running 2 on-run-start hooks 05:14:59 1 of 2 START hook: on-run-start.0................................. [RUN] 05:14:59 1 of 2 OK hook: on-run-start.0.................................... [OK in 0.00s] 05:14:59 2 of 2 START hook: on-run-start.1................................. [RUN] 05:14:59 2 of 2 OK hook: on-run-start.1.................................... [OK in 0.00s] 05:14:59 05:14:59 Concurrency: 4 threads (target='default') 05:14:59 05:14:59 3 of 3 START test unique_orders_order_id........................................ [RUN] 05:14:59 2 of 3 START test not_null_orders_order_id...................................... [RUN] 05:14:59 1 of 3 START test accepted_values_orders_status__placed__shipped__completed__returned [RUN] 05:15:00 2 of 3 PASS not_null_orders_order_id............................................ [PASS in 1.10s] 05:15:00 3 of 3 PASS unique_orders_order_id.............................................. [PASS in 1.16s] 05:15:00 1 of 3 PASS accepted_values_orders_status__placed__shipped__completed__returned. [PASS in 1.16s] 05:15:00 05:15:00 Finished running 3 tests, 2 hooks in 2.86s. 05:15:00 05:15:00 Completed successfully 05:15:00 05:15:00 Done. PASS=3 WARN=0 ERROR=0 SKIP=0 TOTAL=3
成功しました。customersテーブルを作っていないので3行目でWARNINGが出てます。
より複雑なテスト
標準的なテストの他に、より複雑なテストを記述することもできます。
この場合、SELECT文を記述します。そのSELECT結果が0件である場合はテストが通ることになります。
例えば、fct_payments
という注文テーブルがあったとして、
金額が正の値であるかどうかのテストは以下のように記述できます。
select order_id, sum(amount) as total_amount from {{ ref('fct_payments' )}} group by order_id having not(total_amount >= 0)
もし金額が0未満のデータがある場合、SELECT結果が1件以上になりエラーとなるわけです。
ジョブに登録して自動でテストを行う
dbtではジョブを設定でき、スケジュールやトリガーを設定してデータ変換実行時にテストを行うことができます。 この設定を行うことで自動的にテストが走り、データが正しいかどうかチェックしてくれるわけです。
おわりに
データ活用を行う上でデータの信頼性は重要です。 間違ったデータを使って分析してしまうと、その後の意思決定に大きな影響が出てしまいます。 dbtではより柔軟にテストを書くことができるためデータの信頼性をより上げていくことができると思いました。
それではまたいつか。