Python: Parquetフォーマットファイルを入出力する (Pandasとpyarrow)

今回はテーブルデータをParquetファイルで扱う方法について2つ紹介します。

コードは以下の環境で動作確認してます。

% python --version
Python 3.8.5

% pip list   
Package         Version
--------------- -------
numpy           1.19.1
pandas          1.1.0
pip             20.2
pyarrow         1.0.0
python-dateutil 2.8.1
pytz            2020.1
setuptools      49.2.0
six             1.15.0

Apache Parquet

Apache Parquet1はApacheプロジェクトの1つで、環境に依存しない列指向のファイルフォーマットを定義・メンテナンスしています。

github.com

Parquetは以下の特徴を持ちます。詳細は https://parquet.apache.org/documentation/latest/ を参照ください。

  • 列指向フォーマットのため、行指向と比較して、圧縮効率や列に対する集計処理などにおいてアドバンテージを持つ
  • プログラミング言語、CPUアーキテクチャ等に非依存のため、利用できるプラットフォームが豊富
    • Google BigQueryやAmazon AthenaなどもデータソースフォーマットとしてParquetを選択可能
  • ネストしたカラムもエンコード可能

サポートされるデータ型

Parquetで利用できるデータ型だけ確認しておきます。文字列はBYTE_ARRAYに変換する必要があります。

  • BOOLEAN: 1 bit boolean
  • INT32: 32 bit signed ints
  • INT64: 64 bit signed ints
  • INT96: 96 bit signed ints
  • FLOAT: IEEE 32-bit floating point values
  • DOUBLE: IEEE 64-bit floating point values
  • BYTE_ARRAY: arbitrarily long byte arrays.

Pandas DataFrameを用いたParquetファイルの変換

Pandas DataFrameではParquetのファイルを入出力するためのメソッドとして、to_parquetとread_parquetが実装されています。

DataFrameをParqueに保存・ロードする簡単な例を示します。文字列や日付型、NaNを含むデータも難なく変換できてます。

import pandas as pd
from datetime import datetime


dt = datetime.now()
df = pd.DataFrame({
    "id": [1, 2, 3],
    "name": ["Tanaka", "Suzuki", "Sato"],
    "rating": [3.5, None, 4.2],
    "created_at": [dt, dt, dt],
})
print(df.info())
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 3 entries, 0 to 2
# Data columns (total 4 columns):
#  #   Column      Non-Null Count  Dtype
# ---  ------      --------------  -----
#  0   id          3 non-null      int64
#  1   name        3 non-null      object
#  2   rating      2 non-null      float64
#  3   created_at  3 non-null      datetime64[ns]
# dtypes: datetime64[ns](1), float64(1), int64(1), object(1)
# memory usage: 224.0+ bytes

# Parquetで保存
df.to_parquet("./df.parquet")

# Parquetからロード
loaded_df = pd.read_parquet("./df.parquet")

print(loaded_df.info())
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 3 entries, 0 to 2
# Data columns (total 4 columns):
#  #   Column      Non-Null Count  Dtype
# ---  ------      --------------  -----
#  0   id          3 non-null      int64
#  1   name        3 non-null      object
#  2   rating      2 non-null      float64
#  3   created_at  3 non-null      datetime64[ns]
# dtypes: datetime64[ns](1), float64(1), int64(1), object(1)
# memory usage: 224.0+ bytes

print(loaded_df)
#    id    name  rating                 created_at
# 0   1  Tanaka     3.5 2020-08-15 13:33:42.224807
# 1   2  Suzuki     NaN 2020-08-15 13:33:42.224807
# 2   3    Sato     4.2 2020-08-15 13:33:42.224807

Apache Arrow

Apache ArrowもApacheプロジェクトの1つです。こちらはインメモリの列指向データフォーマットを定義し、ライブラリとして提供してます。

arrow.apache.org

Arrowの特徴はこちらです。詳細は https://arrow.apache.org/overview/ を参照ください。

  • 同じ列は同じメモリブロックに含まれるようにレイアウトする
    • SIMD (Single Instruction, Multiple Data) アーキテクチャのCPUで高速に入出力できる
  • プログラミング言語に依存しないフォーマットで、ネストしたデータ型やユーザ定義の型などもサポート
    • Arrowを共通のストレージ (= ハブ) とし、Arrowとのシリアライザ・デシリアライザを実装するだけで、他のプログラミング言語やデータソースとのデータのやり取りができる

f:id:ohke:20200815120437p:plain

pyarrowを用いたParquetファイルの変換

このArrowのPython実装ライブラリの1つがpyarrowです。各種ファイルフォーマットやDataFrameなどに対応しており、例えば、CSVからParquet、ParquetからDataFrameといった変換もpyarrowを仲介することで可能となります。

  • pip install pyarrow でインストールできます
  • 内部的にはpyarrow.Tableオブジェクトとして扱われます

arrow.apache.org

CSVファイル -> Arrowテーブル -> Parquetファイル -> Arrowテーブル -> DataFrameオブジェクト という変換を行います。

# 上の続き

import pyarrow
import pyarrow.parquet
import pyarrow.csv

df.to_csv("test.csv", index=False)

# CSVファイルをArrow形式でロード
loaded_table = pyarrow.csv.read_csv("./test.csv")
print(loaded_table)
# pyarrow.Table
# id: int64
# name: string
# rating: double
# created_at: string

# Parquetに変換して保存
pyarrow.parquet.write_table(loaded_table, "./test.parquet")

# ParquetファイルをArrow形式でロード
loaded_parquet = pyarrow.parquet.read_table("./test.parquet")
print(loaded_parquet)
# pyarrow.Table
# id: int64
# name: string
# rating: double
# created_at: string

# ArrowをDataFrameへ変換
loaded_df = loaded_parquet.to_pandas()
print(loaded_df)
#    id    name  rating                  created_at
# 0   1  Tanaka     3.5  2020-08-15 14:15:08.543007
# 1   2  Suzuki     NaN  2020-08-15 14:15:08.543007
# 2   3    Sato     4.2  2020-08-15 14:15:08.543007

まとめ

今回はParquetファイルをPythonで入出力するための方法を2つ紹介しました。


  1. /ˈpɑːki,ˈpɑːkeɪ/ (パーキ、パーケイ) と読むそうです。