MAGAZINE
ルーターマガジン
DuckDB を使ってCSV納品データの確認をする
 
						はじめに
皆さん、こんにちは、エンジニアの Hodoshima です。
弊社では、クローリングによって得られたデータを、ファイルを作成したり、クラウドストレージへアップロードしたり、データベースに記録したりと様々な方法で納品しています。
その中でも、データを csv ファイルの形式で納品する際、検品のために納品するファイルの要約を行うことがあります。その時に、できるだけ簡単に済ませられるような方法がないかと思っていたところ、DuckDB を使うことによって、新たなテーブルを作成したりする必要がなくデータの確認を行うことができます。
今回の記事では、DuckDB を用いた、データの確認方法を紹介します。
DuckDB のインストール方法
MacOS の場合は、brew を経由でインストールすることができます。Homebrew Formulae
brew install duckdbまた、公式ページから、インストール用のページが用意されているため、このページからでもインストールすることができます。インストール用ページ
DuckDB の使い方
ここからは、以下の csv ファイルが products.csv として保存されており、このファイルについての確認を行うとして説明します。 
"id","name","price"
"1","鉛筆","100"
"2","消しゴム","50"
"3","コンパス","300"
"4","下敷き","120"
"5","ノート","400"
"6","修正液","200"
"7","のり","80"
"8","ボールペン","210"
"9","筆","1000"
"10","定規","250"まず、products.csv ファイルが保存されているファイルと同じフォルダに移動し、コマンドライン上から DuckDB を起動させます。この際に、csv ファイルをコマンドライン引数として起動させます。
% duckdb products.csv
DuckDB v1.3.0 (Ossivalis) 71c5c07cdd
Enter ".help" for usage hints.
D select * from products;すると、DuckDB がターミナル上で起動します。
このターミナルでは、色々なことができますが、特に、products.csv ファイルをあたかも products テーブルかのように扱い、SQL 文を記述することができます 。
そのため、以下のように SQL 文を実行すると、products.csv にある商品の数と、平均の価格を取得することができます。
D select count(id) as product_count, avg(price) as average_price from products;
┌───────────────┬───────────────┐
│ product_count │ average_price │
│     int64     │    double     │
├───────────────┼───────────────┤
│      10       │     271.0     │
└───────────────┴───────────────┘csv ファイルを要約させる
DuckDB には、summarize 関数が用意されています。
summarize 関数を使うことによって、読み込んだ csv ファイルの要約を見ることができます。
カラムごとに、以下を確認できます (カラムの型と内容によってはできないものもあり) 。
- column_name: カラム名
- column_type: カラムの型
- min,- max: 最小値、最大値
- approx_unique: 推定されるデータの種類数 (推定のため、正確ではない場合がある)
- avg: 平均
- std: 標準偏差
- q25,- q50,- q75: 第1四分位数、第2四分位数 (中央値)、第3四分位数
- count: レコードの数 (NULL 含める)
- null_percentage:- NULLの割合
D summarize products;
┌─────────────┬─────────────┬─────────┬─────────┬───────────────┬─────────┬────────────────────┬─────────┬─────────┬─────────┬───────┬─────────────────┐
│ column_name │ column_type │   min   │   max   │ approx_unique │   avg   │        std         │   q25   │   q50   │   q75   │ count │ null_percentage │
│   varchar   │   varchar   │ varchar │ varchar │     int64     │ varchar │      varchar       │ varchar │ varchar │ varchar │ int64 │  decimal(9,2)   │
├─────────────┼─────────────┼─────────┼─────────┼───────────────┼─────────┼────────────────────┼─────────┼─────────┼─────────┼───────┼─────────────────┤
│ id          │ BIGINT      │ 1       │ 10      │            11 │ 5.5     │ 3.0276503540974917 │ 3       │ 6       │ 8       │    10 │            0.00 │
│ name        │ VARCHAR     │ のり    │ 鉛筆    │            10 │ NULL    │ NULL               │ NULL    │ NULL    │ NULL    │    10 │            0.00 │
│ price       │ BIGINT      │ 50      │ 1000    │            11 │ 271.0   │ 277.98681023866345 │ 100     │ 205     │ 300     │    10 │            0.00 │
└─────────────┴─────────────┴─────────┴─────────┴───────────────┴─────────┴────────────────────┴─────────┴─────────┴─────────┴───────┴─────────────────┘この機能を使うことによって、納品ファイルの異常検知を行うことができます。
例えば、以下のように、納品しようとしていた商品ファイルにて異常があったとします (id が 6 のリクエストの商品名と価格がおかしい)。
"id","name","price"
"1","鉛筆","100"
"2","消しゴム","50"
"3","コンパス","300"
"4","下敷き","120"
"5","ノート","400"
"6","【商品データがありません】","0"
"7","のり","80"
"8","ボールペン","210"
"9","筆","1000"
"10","定規","250"この csv ファイルに対して、DuckDB から summarize をこの商品ファイルに対して行うことによって、name の最小値が警告文であり、と price の最小値が 0 となっているため、期待されていた値になっておらず、異常なデータがあることがわかります。
┌─────────────┬─────────────┬────────────────────────────┬─────────┬───────────────┬─────────┬────────────────────┬─────────┬─────────┬─────────┬───────┬─────────────────┐
│ column_name │ column_type │            min             │   max   │ approx_unique │   avg   │        std         │   q25   │   q50   │   q75   │ count │ null_percentage │
│   varchar   │   varchar   │          varchar           │ varchar │     int64     │ varchar │      varchar       │ varchar │ varchar │ varchar │ int64 │  decimal(9,2)   │
├─────────────┼─────────────┼────────────────────────────┼─────────┼───────────────┼─────────┼────────────────────┼─────────┼─────────┼─────────┼───────┼─────────────────┤
│ id          │ BIGINT      │ 1                          │ 10      │            11 │ 5.5     │ 3.0276503540974917 │ 3       │ 6       │ 8       │    10 │            0.00 │
│ name        │ VARCHAR     │ 【商品データがありません】 │ 鉛筆    │            10 │ NULL    │ NULL               │ NULL    │ NULL    │ NULL    │    10 │            0.00 │
│ price       │ BIGINT      │ 0                          │ 1000    │            11 │ 251.0   │ 290.5722323661059  │ 80      │ 165     │ 300     │    10 │            0.00 │
└─────────────┴─────────────┴────────────────────────────┴─────────┴───────────────┴─────────┴────────────────────┴─────────┴─────────┴─────────┴───────┴─────────────────┘DuckDB を使って受けられる恩恵
この DuckDB を使うことによって受けられる恩恵としては、以下があります。
- 依存性が低い: DuckDB と csv ファイル以外に必要とするものがなく、解析を完結させることができる。
- データ型の推論をしてくれる: products.csv の例だと、price フィールドがクオートで囲まれているため、文字列とも受け取れるが、整数型と推論してくれるため、price の平均を取ることができる。
- データの要約が容易: summarize 関数を使うことにより、データの異常値検知を早く行うことができる。通常だと、1 つの代表値 (最大値、最小値、平均値など、複数のデータ全体の傾向や特徴を代表する数値のこと) を取得するために、1 つの命令を書く必要があり、取得したいカラムや代表値が増えるたびに書く必要のある命令の数が増えていく。しかし、DuckDB の sumarize 関数を使うと、カラムの数に関係なく、一発で多種多様な代表値を取得することができる。
まとめ
このように、DuckDB を使うことによって、csv ファイルの中身を簡単に要約し、異常値の検知を行うことができます。代表値を知ることは、異常値の把握だけではなく、データの大まかな分布を知ることにも繋がります。
皆さんも、納品前の最後の検査の際に、DuckDB を使い、代表値を簡単に知ってみてはいかがでしょうか?
CONTACT
お問い合わせ・ご依頼はこちらから
