dev@parquet.apache.org で、parquet-mr の古いモジュールの廃止案が出ています。廃止対象の中には Parquet ファイルの情報を出力する Command Line Tool の parquet-tools も含まれています。

parquet-tools は Parquet ファイルの情報を確認するのに便利だったのですが、parquet-cli もあるので 2つ CLI をメンテするのはどうなんだろう?という話になっています。

私は parquet-cli は使ったことが無かったので、使い方を調べてみました。(2019/02/17 時点)

なお私が知る限りですが、「parquet-cli」という名前のツールは parquet-mr に含まれている Java のツールと、chhantyal/parquet-cli にある Python のツールの2種類があります。今回は parquet-mr の方を取り上げています。

使い方

parquet-cli を使いたい場合は、github から clone して parquet-mr のトップディレクトリで mvn install -DskipTests=true で全モジュールを install するのが簡単です。Linux 環境でのビルドと実行方法は README.md にあります。

Command

parquet-cliparquet-tools 同様、いくつかのコマンドが用意されています。

meta

Schema や Row Group 単位の情報を確認することができます。

$ java -cp target/classes;target/dependency/* org.apache.parquet.cli.Main meta C:\Users\masayuki\Downloads\test. parquet

File path:  C:\Users\masayuki\Downloads\test.parquet
Created by: parquet-mr version 1.12.0-SNAPSHOT (build 1e62e2e2ca903d4109480bc87ceec1dc954b6c92)
Properties:
  writer.model.name: example
Schema:
message test {
  required int32 int32_field;
  required int64 int64_field;
  required float float_field;
  required double double_field;
  required binary binary_field;
  required int64 timestamp_field (TIMESTAMP(MILLIS,true));
}


Row group 0:  count: 395  15.87 B records  start: 4  total: 6.120 kB
--------------------------------------------------------------------------------
                 type      encodings count     avg size   nulls   min / max
int32_field      INT32     _   D     395       0.20 B     0       "32" / "426"
int64_field      INT64     _   D     395       0.20 B     0       "64" / "458"
float_field      FLOAT     _   _     395       4.13 B     0       "1.0" / "395.0"
double_field     DOUBLE    _   _     395       8.13 B     0       "2.0" / "396.0"
binary_field     BINARY    _   D     395       2.98 B     0       "0x6162636465666768696A6B6..." / "0x6162636465666768696A6B6..."
timestamp_field  INT64     _   D     395       0.23 B     0       "2018-11-04T12:41:15.123+0000" / "2018-11-04T12:47:49.123+0000"

Row group 1:  count: 395  15.92 B records  start: 6271  total: 6.142 kB
--------------------------------------------------------------------------------
                 type      encodings count     avg size   nulls   min / max
int32_field      INT32     _   D     395       0.20 B     0       "427" / "821"
int64_field      INT64     _   D     395       0.20 B     0       "459" / "853"
float_field      FLOAT     _   _     395       4.13 B     0       "396.0" / "790.0"
double_field     DOUBLE    _   _     395       8.13 B     0       "397.0" / "791.0"
binary_field     BINARY    _   D     395       3.03 B     0       "0x6162636465666768696A6B6..." / "0x6162636465666768696A6B6..."
timestamp_field  INT64     _   D     395       0.23 B     0       "2018-11-04T12:47:50.123+0000" / "2018-11-04T12:54:24.123+0000"

Row group 2:  count: 234  16.53 B records  start: 12560  total: 3.777 kB
--------------------------------------------------------------------------------
                 type      encodings count     avg size   nulls   min / max
int32_field      INT32     _   D     234       0.17 B     0       "822" / "1055"
int64_field      INT64     _   D     234       0.31 B     0       "854" / "1087"
float_field      FLOAT     _   _     234       4.11 B     0       "791.0" / "1024.0"
double_field     DOUBLE    _   _     234       8.21 B     0       "792.0" / "1025.0"
binary_field     BINARY    _   D     234       3.38 B     0       "0x6162636465666768696A6B6..." / "0x6162636465666768696A6B6..."
timestamp_field  INT64     _   D     234       0.35 B     0       "2018-11-04T12:54:25.123+0000" / "2018-11-04T12:58:18.123+0000"

count はレコードの数です。minmax は Column Chunks の Statistics です。encodeings の欄は2つあり、前者は Compression に関するもので、以下のようになっています。

 1  public static String shortCodec(CompressionCodecName codec) {
 2    switch (codec) {
 3      case UNCOMPRESSED:
 4        return "_";
 5      case SNAPPY:
 6        return "S";
 7      case GZIP:
 8        return "G";
 9      case LZO:
10        return "L";
11      case BROTLI:
12        return "B";
13      case LZ4:
14        return "4";
15      case ZSTD:
16        return "Z";
17      default:
18        return "?";
19    }
20  }

後者は encoding に関するものです。この出力内容はフォーマットによって少し異なるようですが、概ね Delta 系が D、Dictionary 系が R、Plain が _ となります。

pages

Column Chunks の各ページの情報を出力します。

$ java -cp target/classes;target/dependency/* org.apache.parquet.cli.Main pages C:\Users\masayuki\Downloads\test.parquet

Column: int32_field
--------------------------------------------------------------------------------
  page   type  enc  count   avg size   size       rows     nulls   min / max
  0-0    data  _ D  300     0.07 B     21 B       300      0
  0-1    data  _ D  95      0.12 B     11 B       95       0
  1-0    data  _ D  300     0.07 B     22 B       300      0
  1-1    data  _ D  95      0.12 B     11 B       95       0
  2-0    data  _ D  234     0.07 B     17 B       234      0


Column: int64_field
--------------------------------------------------------------------------------
  page   type  enc  count   avg size   size       rows     nulls   min / max
  0-0    data  _ D  200     0.09 B     17 B       200      0
  0-1    data  _ D  195     0.09 B     17 B       195      0
  1-0    data  _ D  200     0.09 B     17 B       200      0
  1-1    data  _ D  195     0.09 B     17 B       195      0
  2-0    data  _ D  200     0.09 B     17 B       200      0
  2-1    data  _ D  34      0.32 B     11 B       34       0


Column: float_field
--------------------------------------------------------------------------------
  page   type  enc  count   avg size   size       rows     nulls   min / max
  0-0    data  _ _  300     4.00 B     1.172 kB   300      0
  0-1    data  _ _  95      4.00 B     380 B      95       0
  1-0    data  _ _  300     4.00 B     1.172 kB   300      0
  1-1    data  _ _  95      4.00 B     380 B      95       0
  2-0    data  _ _  234     4.00 B     936 B      234      0


Column: double_field
--------------------------------------------------------------------------------
  page   type  enc  count   avg size   size       rows     nulls   min / max
  0-0    data  _ _  200     8.00 B     1.563 kB   200      0
  0-1    data  _ _  195     8.00 B     1.523 kB   195      0
  1-0    data  _ _  200     8.00 B     1.563 kB   200      0
  1-1    data  _ _  195     8.00 B     1.523 kB   195      0
  2-0    data  _ _  200     8.00 B     1.563 kB   200      0
  2-1    data  _ _  34      8.00 B     272 B      34       0


Column: binary_field
--------------------------------------------------------------------------------
  page   type  enc  count   avg size   size       rows     nulls   min / max
  0-0    data  _ D  100     2.75 B     275 B      100      0
  0-1    data  _ D  100     2.77 B     277 B      100      0
  0-2    data  _ D  100     2.77 B     277 B      100      0
  0-3    data  _ D  95      2.61 B     248 B      95       0
  1-0    data  _ D  100     2.83 B     283 B      100      0
  1-1    data  _ D  100     2.83 B     283 B      100      0
  1-2    data  _ D  100     2.83 B     283 B      100      0
  1-3    data  _ D  95      2.62 B     249 B      95       0
  2-0    data  _ D  100     2.82 B     282 B      100      0
  2-1    data  _ D  100     2.82 B     282 B      100      0
  2-2    data  _ D  34      4.56 B     155 B      34       0


Column: timestamp_field
--------------------------------------------------------------------------------
  page   type  enc  count   avg size   size       rows     nulls   min / max
  0-0    data  _ D  200     0.12 B     23 B       200      0
  0-1    data  _ D  195     0.12 B     23 B       195      0
  1-0    data  _ D  200     0.12 B     23 B       200      0
  1-1    data  _ D  195     0.12 B     23 B       195      0
  2-0    data  _ D  200     0.12 B     23 B       200      0
  2-1    data  _ D  34      0.47 B     16 B       34       0

page はページのインデックスで、「(Row Group Index)-(Page Index)」のフォーマットで出力します。type は Data ページの場合は data、Dictionary ページの場合は dict と表示されます。minmax が表示されていないのは、前回書いた ColumnIndex の対応で、ページには Statistics を書き込まないようになっている為です。

dictionary

指定した column の dictionary データを表示します。指定した column が dictionary encoding になっている必要があります。

$ java -cp 'target/classes:target/dependency/*' org.apache.parquet.cli.Main dictionary /work/parquet-mr/data/userdata1.parquet -c last_name

Row group 0 dictionary for "last_name":
     0: "Jordan"
     1: "Freeman"
     2: "Morgan"
     3: "Riley"
     4: "Burns"
     5: "White"
     6: "Holmes"
     7: "Howell"
     8: "Foster"
     9: "Stewart"
    10: "Perkins"
    ...

check-stats

これは help コマンドで出力される以下の説明にあるように、PARQUET-251 で対応されている Statistics の不具合を確認するコマンドです。大分前の Issue なので、最近作成した Parquet ファイルに対して使うことは無いと思います。

    check-stats
        Check Parquet files for corrupt page and column stats (PARQUET-251)

schema

Parquet ファイルの Schema を表示します。Avro の Schema も表示できます。

$ java -cp 'target/classes:target/dependency/*' org.apache.parquet.cli.Main schema /work/parquet-mr/data/test.parquet
{
  "type" : "record",
  "name" : "test",
  "fields" : [ {
    "name" : "int32_field",
    "type" : "int"
  }, {
    "name" : "int64_field",
    "type" : "long"
  }, {
    "name" : "float_field",
    "type" : "float"
  }, {
    "name" : "double_field",
    "type" : "double"
  }, {
    "name" : "binary_field",
    "type" : "bytes"
  }, {
    "name" : "timestamp_field",
    "type" : {
      "type" : "long",
      "logicalType" : "timestamp-millis"
    }
  } ]
}

csv-schema

CSV ファイルのデータを読んで Schema を表示します。以下のような CSV ファイルを用意して、

emp_id,dept_id,name,created_at,updated_at
1,1,"test1","2019-02-17 10:00:00","2019-02-17 12:00:00"
2,2,"test2","2019-02-17 10:00:00","2019-02-17 12:00:00"

以下のように実行すると、Schema が出力されます。

$ java -cp 'target/classes:target/dependency/*' org.apache.parquet.cli.Main csv-schema /work/parquet-mr/data/test.csv --record-name Test
{
  "type" : "record",
  "name" : "Test",
  "fields" : [ {
    "name" : "emp_id",
    "type" : [ "null", "long" ],
    "doc" : "Type inferred from '1'",
    "default" : null
  }, {
    "name" : "dept_id",
    "type" : [ "null", "long" ],
    "doc" : "Type inferred from '1'",
    "default" : null
  }, {
    "name" : "name",
    "type" : [ "null", "string" ],
    "doc" : "Type inferred from 'test1'",
    "default" : null
  }, {
    "name" : "created_at",
    "type" : [ "null", "string" ],
    "doc" : "Type inferred from '2019-02-17 10:00:00'",
    "default" : null
  }, {
    "name" : "updated_at",
    "type" : [ "null", "string" ],
    "doc" : "Type inferred from '2019-02-17 12:00:00\" '",
    "default" : null
  } ]
}

convert-csv

CSV ファイルを Parquet フォーマットのファイルに変換します。

$ java -cp 'target/classes:target/dependency/*' org.apache.parquet.cli.Main convert-csv ../data/test.csv -o ../data/test-csv.parquet
$ java -cp 'target/classes:target/dependency/*' org.apache.parquet.cli.Main head -n 10 ../data/test-csv.parquet
{"emp_id": 1, "dept_id": 1, "name": "test1", "created_at": "2019-02-17 10:00:00", "updated_at": "2019-02-17 12:00:00\" "}
{"emp_id": 2, "dept_id": 2, "name": "test2", "created_at": "2019-02-17 10:00:00", "updated_at": "2019-02-17 12:00:00"}

convert

Avro フォーマットのファイルを Parquet フォーマットのファイルに変換します。

java -cp 'target/classes:target/dependency/*' org.apache.t.cli.Main convert /work/parquet-mr/data/userdata1.avro -o ../data/userdata1.parquet

to-avro

convert の逆で、Parquet フォーマットのファイルを Avro フォーマットのファイルに変換します。

java -cp 'target/classes:target/dependency/*' org.apache.parquet.cli.Main to-avro /work/parquet-mr/data/test.parquet -o ../data/test.avro

cathead は Parquet ファイル内のデータを出力するコマンドです。どちらのコマンドも、先頭から -n で指定した分だけレコードを出力します。cat-n を指定しないと全データを出力します。

java -cp 'target/classes:target/dependency/*' org.apache.parquet.cli.Main head -n 5 /work/parquet-mr/data/test.parquet
{"int32_field": 32, "int64_field": 64, "float_field": 1.0, "double_field": 2.0, "binary_field": {"bytes": "abcdefghijklmnopqrstuvwxyz0"}, "timestamp_field": 1541335275123}
{"int32_field": 33, "int64_field": 65, "float_field": 2.0, "double_field": 3.0, "binary_field": {"bytes": "abcdefghijklmnopqrstuvwxyz1"}, "timestamp_field": 1541335276123}
{"int32_field": 34, "int64_field": 66, "float_field": 3.0, "double_field": 4.0, "binary_field": {"bytes": "abcdefghijklmnopqrstuvwxyz2"}, "timestamp_field": 1541335277123}
{"int32_field": 35, "int64_field": 67, "float_field": 4.0, "double_field": 5.0, "binary_field": {"bytes": "abcdefghijklmnopqrstuvwxyz3"}, "timestamp_field": 1541335278123}
{"int32_field": 36, "int64_field": 68, "float_field": 5.0, "double_field": 6.0, "binary_field": {"bytes": "abcdefghijklmnopqrstuvwxyz4"}, "timestamp_field": 1541335279123}

column-index

ColumnIndex を出力します。

$ java -cp 'target/classes:target/dependency/*' org.apache.parquet.cli.Main column-index ../data/test.parquet
row-group 0:
column index for column int32_field:
Boudary order: ASCENDING
                      null count  min                                       max

page-0                         0  32                                        331

page-1                         0  332                                       426


offset index for column int32_field:
                          offset   compressed size       first row index
page-0                         4                44                     0
page-1                        48                34                   300
...

Conclusion

parquet-tools と比較すると、ファイルの内容を表示する機能は大体同じですが、CSV や Avro から Parquet に変換する機能が付いているのが嬉しいところです。